diff --git a/ansible/filter_plugins/address.py b/ansible/filter_plugins/address.py
new file mode 100644
index 0000000000..3757ee8f0e
--- /dev/null
+++ b/ansible/filter_plugins/address.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 Radosław Piliszek (yoctozepto)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from kolla_ansible.kolla_address import kolla_address
+from kolla_ansible.put_address_in_context import put_address_in_context
+
+
+class FilterModule(object):
+    """IP address filters"""
+
+    def filters(self):
+        return {
+            'kolla_address': kolla_address,
+            'put_address_in_context': put_address_in_context,
+        }
diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index d1e9f6aa80..0b351d94c5 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -64,10 +64,10 @@ container_proxy:
 
 # By default, Kolla API services bind to the network address assigned
 # to the api_interface.  Allow the bind address to be an override.
-api_interface_address: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+api_interface_address: "{{ 'api' | kolla_address }}"
 
 # This is used to get the ip corresponding to the storage_interface.
-storage_interface_address: "{{ hostvars[inventory_hostname]['ansible_' + storage_interface]['ipv4']['address'] }}"
+storage_interface_address: "{{ 'storage' | kolla_address }}"
 
 ################
 # Chrony options
@@ -109,9 +109,9 @@ docker_restart_policy_retry: "10"
 
 # Extra docker options for Zun
 docker_configure_for_zun: "no"
-docker_zun_options: -H tcp://{{ api_interface_address }}:2375
+docker_zun_options: -H tcp://{{ api_interface_address | put_address_in_context('url') }}:2375
 docker_zun_config:
-  cluster-store: etcd://{% for host in groups.get('etcd', []) %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['etcd_client_port'] }}{% if not loop.last %},{% endif %}{% endfor %}
+  cluster-store: etcd://{% for host in groups.get('etcd', []) %}{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['etcd_client_port'] }}{% if not loop.last %},{% endif %}{% endfor %}
 
 # Timeout after Docker sends SIGTERM before sending SIGKILL.
 docker_graceful_timeout: 60
@@ -165,7 +165,7 @@ om_rpc_port: "{{ rabbitmq_port }}"
 om_rpc_group: "rabbitmq"
 om_rpc_vhost: "/"
 
-rpc_transport_url: "{{ om_rpc_transport }}://{% for host in groups[om_rpc_group] %}{{ om_rpc_user }}:{{ om_rpc_password }}@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ om_rpc_port }}{% if not loop.last %},{% endif %}{% endfor %}/{{ om_rpc_vhost }}"
+rpc_transport_url: "{{ om_rpc_transport }}://{% for host in groups[om_rpc_group] %}{{ om_rpc_user }}:{{ om_rpc_password }}@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ om_rpc_port }}{% if not loop.last %},{% endif %}{% endfor %}/{{ om_rpc_vhost }}"
 
 # oslo.messaging notify transport valid options are [ rabbit ]
 om_notify_transport: "rabbit"
@@ -175,7 +175,7 @@ om_notify_port: "{{ rabbitmq_port }}"
 om_notify_group: "rabbitmq"
 om_notify_vhost: "/"
 
-notify_transport_url: "{{ om_notify_transport }}://{% for host in groups[om_notify_group] %}{{ om_notify_user }}:{{ om_notify_password }}@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ om_notify_port }}{% if not loop.last %},{% endif %}{% endfor %}/{{ om_notify_vhost }}"
+notify_transport_url: "{{ om_notify_transport }}://{% for host in groups[om_notify_group] %}{{ om_notify_user }}:{{ om_notify_password }}@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ om_notify_port }}{% if not loop.last %},{% endif %}{% endfor %}/{{ om_notify_vhost }}"
 
 ####################
 # Networking options
@@ -193,9 +193,24 @@ tunnel_interface: "{{ network_interface }}"
 octavia_network_interface: "{{ api_interface }}"
 bifrost_network_interface: "{{ network_interface }}"
 dns_interface: "{{ network_interface }}"
-migration_interface_address: "{{ hostvars[inventory_hostname]['ansible_' + migration_interface]['ipv4']['address'] }}"
-tunnel_interface_address: "{{ hostvars[inventory_hostname]['ansible_' + tunnel_interface]['ipv4']['address'] }}"
-octavia_network_interface_address: "{{ hostvars[inventory_hostname]['ansible_' + octavia_network_interface]['ipv4']['address'] }}"
+
+# Configure the address family (AF) per network.
+# Valid options are [ ipv4, ipv6 ]
+network_address_family: "ipv4"
+api_address_family: "{{ network_address_family }}"
+storage_address_family: "{{ network_address_family }}"
+cluster_address_family: "{{ network_address_family }}"
+swift_storage_address_family: "{{ storage_address_family }}"
+swift_replication_address_family: "{{ swift_storage_address_family }}"
+migration_address_family: "{{ network_address_family }}"
+tunnel_address_family: "{{ network_address_family }}"
+octavia_network_address_family: "{{ api_address_family }}"
+bifrost_network_address_family: "{{ network_address_family }}"
+dns_address_family: "{{ network_address_family }}"
+
+migration_interface_address: "{{ 'migration' | kolla_address }}"
+tunnel_interface_address: "{{ 'tunnel' | kolla_address }}"
+octavia_network_interface_address: "{{ 'octavia_network' | kolla_address }}"
 
 # Valid options are [ openvswitch, linuxbridge, vmware_nsxv, vmware_nsxv3, vmware_dvs, opendaylight ]
 neutron_plugin_agent: "openvswitch"
@@ -715,14 +730,14 @@ enable_kibana: "{{ 'yes' if enable_central_logging | bool or enable_monasca | bo
 ####################
 # Redis options
 ####################
-redis_connection_string: "redis://{% for host in groups['redis'] %}{% if host == groups['redis'][0] %}admin:{{ redis_master_password }}@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ redis_sentinel_port }}?sentinel=kolla{% else %}&sentinel_fallback={{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ redis_sentinel_port }}{% endif %}{% endfor %}&db=0&socket_timeout=60&retry_on_timeout=yes"
+redis_connection_string: "redis://{% for host in groups['redis'] %}{% if host == groups['redis'][0] %}admin:{{ redis_master_password }}@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ redis_sentinel_port }}?sentinel=kolla{% else %}&sentinel_fallback={{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ redis_sentinel_port }}{% endif %}{% endfor %}&db=0&socket_timeout=60&retry_on_timeout=yes"
 
 ####################
 # Osprofiler options
 ####################
 # valid values: ["elasticsearch", "redis"]
 osprofiler_backend: "elasticsearch"
-elasticsearch_connection_string: "elasticsearch://{{ elasticsearch_address }}:{{ elasticsearch_port }}"
+elasticsearch_connection_string: "elasticsearch://{{ elasticsearch_address | put_address_in_context('url') }}:{{ elasticsearch_port }}"
 osprofiler_backend_connection_string: "{{ redis_connection_string if osprofiler_backend == 'redis' else elasticsearch_connection_string }}"
 
 ####################
@@ -762,9 +777,9 @@ kibana_log_prefix: "flog"
 ####################
 keystone_internal_fqdn: "{{ kolla_internal_fqdn }}"
 keystone_external_fqdn: "{{ kolla_external_fqdn }}"
-keystone_admin_url: "{{ admin_protocol }}://{{ keystone_internal_fqdn }}:{{ keystone_admin_port }}"
-keystone_internal_url: "{{ internal_protocol }}://{{ keystone_internal_fqdn }}:{{ keystone_public_port }}"
-keystone_public_url: "{{ public_protocol }}://{{ keystone_external_fqdn }}:{{ keystone_public_port }}"
+keystone_admin_url: "{{ admin_protocol }}://{{ keystone_internal_fqdn | put_address_in_context('url') }}:{{ keystone_admin_port }}"
+keystone_internal_url: "{{ internal_protocol }}://{{ keystone_internal_fqdn | put_address_in_context('url') }}:{{ keystone_public_port }}"
+keystone_public_url: "{{ public_protocol }}://{{ keystone_external_fqdn | put_address_in_context('url') }}:{{ keystone_public_port }}"
 
 keystone_admin_user: "admin"
 keystone_admin_project: "admin"
diff --git a/ansible/roles/aodh/defaults/main.yml b/ansible/roles/aodh/defaults/main.yml
index fd58d635fc..1d30a1ae5d 100644
--- a/ansible/roles/aodh/defaults/main.yml
+++ b/ansible/roles/aodh/defaults/main.yml
@@ -58,7 +58,7 @@ aodh_evaluation_interval: 300
 ####################
 aodh_database_name: "aodh"
 aodh_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}aodh{% endif %}"
-aodh_database_address: "{{ database_address }}:{{ database_port }}"
+aodh_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Notification
@@ -129,9 +129,9 @@ aodh_notifier_extra_volumes: "{{ aodh_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-aodh_admin_endpoint: "{{ admin_protocol }}://{{ aodh_internal_fqdn }}:{{ aodh_api_port }}"
-aodh_internal_endpoint: "{{ internal_protocol }}://{{ aodh_internal_fqdn }}:{{ aodh_api_port }}"
-aodh_public_endpoint: "{{ public_protocol }}://{{ aodh_external_fqdn }}:{{ aodh_api_port }}"
+aodh_admin_endpoint: "{{ admin_protocol }}://{{ aodh_internal_fqdn | put_address_in_context('url') }}:{{ aodh_api_port }}"
+aodh_internal_endpoint: "{{ internal_protocol }}://{{ aodh_internal_fqdn | put_address_in_context('url') }}:{{ aodh_api_port }}"
+aodh_public_endpoint: "{{ public_protocol }}://{{ aodh_external_fqdn | put_address_in_context('url') }}:{{ aodh_api_port }}"
 
 aodh_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/aodh/templates/aodh.conf.j2 b/ansible/roles/aodh/templates/aodh.conf.j2
index 8fc3c3c16d..b424936048 100644
--- a/ansible/roles/aodh/templates/aodh.conf.j2
+++ b/ansible/roles/aodh/templates/aodh.conf.j2
@@ -16,7 +16,7 @@ connection = mysql+pymysql://{{ aodh_database_user }}:{{ aodh_database_password
 [keystone_authtoken]
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 www_authenticate_uri = {{ keystone_internal_url }}
 project_domain_name = {{ default_project_domain_name }}
 project_name = service
diff --git a/ansible/roles/aodh/templates/wsgi-aodh.conf.j2 b/ansible/roles/aodh/templates/wsgi-aodh.conf.j2
index ec111eedcb..932eb6ec0f 100644
--- a/ansible/roles/aodh/templates/wsgi-aodh.conf.j2
+++ b/ansible/roles/aodh/templates/wsgi-aodh.conf.j2
@@ -4,7 +4,7 @@
     {% set python_path = '/var/lib/kolla/venv/lib/python' + distro_python_version + '/site-packages' %}
 {% endif %}
 {% set binary_path = '/usr/bin' if aodh_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
-Listen {{ api_interface_address }}:{{ aodh_api_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ aodh_api_listen_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/barbican/defaults/main.yml b/ansible/roles/barbican/defaults/main.yml
index 37686cbc37..9ea7f5ae62 100644
--- a/ansible/roles/barbican/defaults/main.yml
+++ b/ansible/roles/barbican/defaults/main.yml
@@ -43,7 +43,7 @@ barbican_services:
 ####################
 barbican_database_name: "barbican"
 barbican_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}barbican{% endif %}"
-barbican_database_address: "{{ database_address }}:{{ database_port }}"
+barbican_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -93,9 +93,9 @@ barbican_worker_extra_volumes: "{{ barbican_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-barbican_admin_endpoint: "{{ admin_protocol }}://{{ barbican_internal_fqdn }}:{{ barbican_api_port }}"
-barbican_internal_endpoint: "{{ internal_protocol }}://{{ barbican_internal_fqdn }}:{{ barbican_api_port }}"
-barbican_public_endpoint: "{{ public_protocol }}://{{ barbican_external_fqdn }}:{{ barbican_api_port }}"
+barbican_admin_endpoint: "{{ admin_protocol }}://{{ barbican_internal_fqdn | put_address_in_context('url') }}:{{ barbican_api_port }}"
+barbican_internal_endpoint: "{{ internal_protocol }}://{{ barbican_internal_fqdn | put_address_in_context('url') }}:{{ barbican_api_port }}"
+barbican_public_endpoint: "{{ public_protocol }}://{{ barbican_external_fqdn | put_address_in_context('url') }}:{{ barbican_api_port }}"
 
 barbican_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/barbican/templates/barbican-api.ini.j2 b/ansible/roles/barbican/templates/barbican-api.ini.j2
index ce353292da..302cf98d58 100644
--- a/ansible/roles/barbican/templates/barbican-api.ini.j2
+++ b/ansible/roles/barbican/templates/barbican-api.ini.j2
@@ -1,5 +1,5 @@
 [uwsgi]
-socket = {{ api_interface_address }}:{{ barbican_api_listen_port }}
+socket = {{ api_interface_address | put_address_in_context('url') }}:{{ barbican_api_listen_port }}
 protocol = http
 processes = {{ openstack_service_workers }}
 lazy = true
diff --git a/ansible/roles/barbican/templates/barbican.conf.j2 b/ansible/roles/barbican/templates/barbican.conf.j2
index 06eaff3fa7..c2d14a2bca 100644
--- a/ansible/roles/barbican/templates/barbican.conf.j2
+++ b/ansible/roles/barbican/templates/barbican.conf.j2
@@ -4,7 +4,7 @@ log_dir = /var/log/kolla/barbican
 
 bind_port = {{ barbican_api_listen_port }}
 bind_host = {{ api_interface_address }}
-host_href = {{ public_protocol }}://{{ barbican_external_fqdn }}:{{ barbican_api_port }}
+host_href = {{ public_protocol }}://{{ barbican_external_fqdn | put_address_in_context('url') }}:{{ barbican_api_port }}
 backlog = 4096
 max_allowed_secret_in_bytes = 10000
 max_allowed_request_size_in_bytes = 1000000
@@ -62,7 +62,7 @@ auth_type = password
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/baremetal/tasks/pre-install.yml b/ansible/roles/baremetal/tasks/pre-install.yml
index 61387beb71..e53d5452e6 100644
--- a/ansible/roles/baremetal/tasks/pre-install.yml
+++ b/ansible/roles/baremetal/tasks/pre-install.yml
@@ -37,7 +37,7 @@
         {% set api_interface = hostvars[host]['api_interface'] %}
         {% if host not in groups['bifrost'] or 'ansible_' + api_interface in hostvars[host] %}
         {% set hostnames = [hostvars[host]['ansible_nodename'], hostvars[host]['ansible_hostname']] %}
-        {{ hostvars[host]['ansible_' + api_interface]['ipv4']['address'] }} {{ hostnames | unique | join(' ') }}
+        {{ 'api' | kolla_address(host) }} {{ hostnames | unique | join(' ') }}
         {% endif %}
         {% endfor %}
   become: True
diff --git a/ansible/roles/blazar/defaults/main.yml b/ansible/roles/blazar/defaults/main.yml
index 103b5ca05d..4343348641 100644
--- a/ansible/roles/blazar/defaults/main.yml
+++ b/ansible/roles/blazar/defaults/main.yml
@@ -39,7 +39,7 @@ blazar_aggregate_pool_name: "freepool"
 ####################
 blazar_database_name: "blazar"
 blazar_database_user: "blazar"
-blazar_database_address: "{{ database_address }}:{{ database_port }}"
+blazar_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -77,9 +77,9 @@ blazar_manager_extra_volumes: "{{ blazar_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-blazar_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ blazar_api_port }}/v1"
-blazar_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ blazar_api_port }}/v1"
-blazar_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ blazar_api_port }}/v1"
+blazar_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ blazar_api_port }}/v1"
+blazar_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ blazar_api_port }}/v1"
+blazar_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ blazar_api_port }}/v1"
 
 blazar_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/blazar/templates/blazar.conf.j2 b/ansible/roles/blazar/templates/blazar.conf.j2
index fb778e9f6b..91d1e46c01 100644
--- a/ansible/roles/blazar/templates/blazar.conf.j2
+++ b/ansible/roles/blazar/templates/blazar.conf.j2
@@ -35,7 +35,7 @@ service_token_roles_required = True
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [database]
 connection = mysql+pymysql://{{ blazar_database_user }}:{{ blazar_database_password }}@{{ blazar_database_address }}/{{ blazar_database_name }}
diff --git a/ansible/roles/ceilometer/templates/ceilometer.conf.j2 b/ansible/roles/ceilometer/templates/ceilometer.conf.j2
index 59dfa93d0b..412e01b0bf 100644
--- a/ansible/roles/ceilometer/templates/ceilometer.conf.j2
+++ b/ansible/roles/ceilometer/templates/ceilometer.conf.j2
@@ -51,4 +51,4 @@ connection_password = {{ xenserver_password }}
 [cache]
 backend = oslo_cache.memcache_pool
 enabled = True
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
diff --git a/ansible/roles/ceph/defaults/main.yml b/ansible/roles/ceph/defaults/main.yml
index 04d6b9d77b..e54fc224cf 100644
--- a/ansible/roles/ceph/defaults/main.yml
+++ b/ansible/roles/ceph/defaults/main.yml
@@ -101,9 +101,9 @@ ceph_mds_hostname: "{%- if ceph_mds_host_type == 'HOSTNAME' -%}{{ ansible_hostna
 ####################
 ## Ceph_rgw_keystone
 ####################
-swift_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ rgw_port }}{{ '/' if ceph_rgw_compatibility|bool else '/swift/' }}v1"
-swift_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ rgw_port }}{{ '/' if ceph_rgw_compatibility|bool else '/swift/' }}v1"
-swift_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ rgw_port }}{{ '/' if ceph_rgw_compatibility|bool else '/swift/' }}v1"
+swift_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ rgw_port }}{{ '/' if ceph_rgw_compatibility|bool else '/swift/' }}v1"
+swift_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ rgw_port }}{{ '/' if ceph_rgw_compatibility|bool else '/swift/' }}v1"
+swift_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ rgw_port }}{{ '/' if ceph_rgw_compatibility|bool else '/swift/' }}v1"
 
 ceph_rgw_keystone_user: "ceph_rgw"
 
diff --git a/ansible/roles/ceph/tasks/upgrade.yml b/ansible/roles/ceph/tasks/upgrade.yml
index 659c78820c..2239ceac04 100644
--- a/ansible/roles/ceph/tasks/upgrade.yml
+++ b/ansible/roles/ceph/tasks/upgrade.yml
@@ -3,7 +3,7 @@
 
 - name: Check final release (as running on MONs)
   become: true
-  command: "docker exec ceph_mon ceph -m {{ hostvars[inventory_hostname]['ansible_' + storage_interface]['ipv4']['address'] }} versions"
+  command: "docker exec ceph_mon ceph -m {{ 'storage' | kolla_address }} versions"
   changed_when: false
   register: ceph_release_command
   delegate_to: "{{ groups['ceph-mon'][0] }}"
@@ -11,7 +11,7 @@
 
 - name: Finalize the upgrade by disallowing older OSDs
   become: true
-  command: "docker exec ceph_mon ceph -m {{ hostvars[inventory_hostname]['ansible_' + storage_interface]['ipv4']['address'] }} osd require-osd-release {{ ((ceph_release_command.stdout|from_json).mon | string).split(' ')[4] }}"
+  command: "docker exec ceph_mon ceph -m {{ 'storage' | kolla_address }} osd require-osd-release {{ ((ceph_release_command.stdout|from_json).mon | string).split(' ')[4] }}"
   changed_when: false
   delegate_to: "{{ groups['ceph-mon'][0] }}"
   run_once: true
diff --git a/ansible/roles/ceph/templates/ceph-mon.json.j2 b/ansible/roles/ceph/templates/ceph-mon.json.j2
index b92e00395b..60b88c0e80 100644
--- a/ansible/roles/ceph/templates/ceph-mon.json.j2
+++ b/ansible/roles/ceph/templates/ceph-mon.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/usr/bin/ceph-mon -f {% if ceph_debug %}-d{% endif %} -i {{ ceph_mon_hostname }} --public-addr {{ storage_interface_address }}:6789",
+    "command": "/usr/bin/ceph-mon -f {% if ceph_debug %}-d{% endif %} -i {{ ceph_mon_hostname }} --public-addr {{ storage_interface_address | put_address_in_context('url') }}:6789",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/ceph.conf",
diff --git a/ansible/roles/ceph/templates/ceph-osd.json.j2 b/ansible/roles/ceph/templates/ceph-osd.json.j2
index fe6f754696..a73488e72e 100644
--- a/ansible/roles/ceph/templates/ceph-osd.json.j2
+++ b/ansible/roles/ceph/templates/ceph-osd.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/usr/bin/ceph-osd -f {% if ceph_debug %}-d{% endif %} --public-addr {{ storage_interface_address }} --cluster-addr {{ hostvars[inventory_hostname]['ansible_' + cluster_interface]['ipv4']['address'] }}",
+    "command": "/usr/bin/ceph-osd -f {% if ceph_debug %}-d{% endif %} --public-addr {{ storage_interface_address }} --cluster-addr {{ 'cluster' | kolla_address }}",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/ceph.conf",
diff --git a/ansible/roles/ceph/templates/ceph.conf.j2 b/ansible/roles/ceph/templates/ceph.conf.j2
index 0f3423b929..10209e1f05 100644
--- a/ansible/roles/ceph/templates/ceph.conf.j2
+++ b/ansible/roles/ceph/templates/ceph.conf.j2
@@ -13,12 +13,16 @@ mon initial members = {% for host in groups['ceph-mon'] %}{{ hostvars[host]['ans
 {% elif ceph_mon_host_type == 'INVENTORY' %}
 mon initial members = {% for host in groups['ceph-mon'] %}{{ host }}{% if not loop.last %}, {% endif %}{% endfor %}
 {%- else %}
-mon initial members = {% for host in groups['ceph-mon'] %}{{ hostvars[host]['ansible_' + hostvars[host]['storage_interface']]['ipv4']['address'] }}{% if not loop.last %}, {% endif %}{% endfor %}
+mon initial members = {% for host in groups['ceph-mon'] %}{{ 'storage' | kolla_address(host) }}{% if not loop.last %}, {% endif %}{% endfor %}
 {% endif %}
 
-mon host = {% for host in groups['ceph-mon'] %}{{ hostvars[host]['ansible_' + hostvars[host]['storage_interface']]['ipv4']['address'] }}{% if not loop.last %}, {% endif %}{% endfor %}
+mon host = {% for host in groups['ceph-mon'] %}{{ 'storage' | kolla_address(host) }}{% if not loop.last %}, {% endif %}{% endfor %}
 
-mon addr = {% for host in groups['ceph-mon'] %}{{ hostvars[host]['ansible_' + hostvars[host]['storage_interface']]['ipv4']['address'] }}:6789{% if not loop.last %}, {% endif %}{% endfor %}
+mon addr = {% for host in groups['ceph-mon'] %}{{ 'storage' | kolla_address(host) | put_address_in_context('url') }}:6789{% if not loop.last %}, {% endif %}{% endfor %}
+
+{% if storage_address_family == 'ipv6' %}
+ms bind ipv6 = true
+{% endif %}
 
 auth cluster required = cephx
 auth service required = cephx
@@ -43,8 +47,8 @@ mon cluster log file = /var/log/kolla/ceph/$cluster.log
 
 {% if service_name is defined and service_name == 'ceph-rgw' %}
 [client.radosgw.gateway]
-host = {{ hostvars[inventory_hostname]['ansible_' + storage_interface]['ipv4']['address'] }}
-rgw frontends = civetweb port={{ api_interface_address }}:{{ rgw_port }}
+host = {{ 'storage' | kolla_address }}
+rgw frontends = civetweb port={{ api_interface_address | put_address_in_context('url') }}:{{ rgw_port }}
 {% if enable_ceph_rgw_keystone | bool %}
 rgw_keystone_url = {{ keystone_admin_url }}
 rgw_keystone_admin_user = {{ ceph_rgw_keystone_user }}
diff --git a/ansible/roles/cinder/defaults/main.yml b/ansible/roles/cinder/defaults/main.yml
index 44f01eedac..c049593ff1 100644
--- a/ansible/roles/cinder/defaults/main.yml
+++ b/ansible/roles/cinder/defaults/main.yml
@@ -91,7 +91,7 @@ ceph_client_cinder_backup_keyring_caps:
 ####################
 cinder_database_name: "cinder"
 cinder_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}cinder{% endif %}"
-cinder_database_address: "{{ database_address }}:{{ database_port }}"
+cinder_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 # Max number of object to consider
 # when run online data migration
@@ -164,12 +164,12 @@ cinder_volume_extra_volumes: "{{ cinder_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-cinder_v2_admin_endpoint: "{{ admin_protocol }}://{{ cinder_internal_fqdn }}:{{ cinder_api_port }}/v2/%(tenant_id)s"
-cinder_v2_internal_endpoint: "{{ internal_protocol }}://{{ cinder_internal_fqdn }}:{{ cinder_api_port }}/v2/%(tenant_id)s"
-cinder_v2_public_endpoint: "{{ public_protocol }}://{{ cinder_external_fqdn }}:{{ cinder_api_port }}/v2/%(tenant_id)s"
-cinder_v3_admin_endpoint: "{{ admin_protocol }}://{{ cinder_internal_fqdn }}:{{ cinder_api_port }}/v3/%(tenant_id)s"
-cinder_v3_internal_endpoint: "{{ internal_protocol }}://{{ cinder_internal_fqdn }}:{{ cinder_api_port }}/v3/%(tenant_id)s"
-cinder_v3_public_endpoint: "{{ public_protocol }}://{{ cinder_external_fqdn }}:{{ cinder_api_port }}/v3/%(tenant_id)s"
+cinder_v2_admin_endpoint: "{{ admin_protocol }}://{{ cinder_internal_fqdn | put_address_in_context('url') }}:{{ cinder_api_port }}/v2/%(tenant_id)s"
+cinder_v2_internal_endpoint: "{{ internal_protocol }}://{{ cinder_internal_fqdn | put_address_in_context('url') }}:{{ cinder_api_port }}/v2/%(tenant_id)s"
+cinder_v2_public_endpoint: "{{ public_protocol }}://{{ cinder_external_fqdn | put_address_in_context('url') }}:{{ cinder_api_port }}/v2/%(tenant_id)s"
+cinder_v3_admin_endpoint: "{{ admin_protocol }}://{{ cinder_internal_fqdn | put_address_in_context('url') }}:{{ cinder_api_port }}/v3/%(tenant_id)s"
+cinder_v3_internal_endpoint: "{{ internal_protocol }}://{{ cinder_internal_fqdn | put_address_in_context('url') }}:{{ cinder_api_port }}/v3/%(tenant_id)s"
+cinder_v3_public_endpoint: "{{ public_protocol }}://{{ cinder_external_fqdn | put_address_in_context('url') }}:{{ cinder_api_port }}/v3/%(tenant_id)s"
 
 cinder_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/cinder/templates/cinder-wsgi.conf.j2 b/ansible/roles/cinder/templates/cinder-wsgi.conf.j2
index 9230ee7187..e926fd52da 100644
--- a/ansible/roles/cinder/templates/cinder-wsgi.conf.j2
+++ b/ansible/roles/cinder/templates/cinder-wsgi.conf.j2
@@ -3,7 +3,7 @@
 {% else %}
     {% set python_path = '/var/lib/kolla/venv/lib/python' + distro_python_version + '/site-packages' %}
 {% endif %}
-Listen {{ api_interface_address }}:{{ cinder_api_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ cinder_api_listen_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/cinder/templates/cinder.conf.j2 b/ansible/roles/cinder/templates/cinder.conf.j2
index 64796fc1dc..4dbed097ce 100644
--- a/ansible/roles/cinder/templates/cinder.conf.j2
+++ b/ansible/roles/cinder/templates/cinder.conf.j2
@@ -13,7 +13,7 @@ my_ip = {{ api_interface_address }}
 osapi_volume_workers = {{ openstack_service_workers }}
 volume_name_template = volume-%s
 
-glance_api_servers = {{ internal_protocol }}://{{ glance_internal_fqdn }}:{{ glance_api_port }}
+glance_api_servers = {{ internal_protocol }}://{{ glance_internal_fqdn | put_address_in_context('url') }}:{{ glance_api_port }}
 
 glance_num_retries = {{ groups['glance-api'] | length }}
 glance_api_version = 2
@@ -42,7 +42,7 @@ backup_share = {{ cinder_backup_share }}
 backup_file_size = 327680000
 {% elif enable_swift | bool and cinder_backup_driver == "swift" %}
 backup_driver = cinder.backup.drivers.swift.SwiftBackupDriver
-backup_swift_url = {{ internal_protocol }}://{{ swift_internal_fqdn }}:{{ swift_proxy_server_port }}/v1/AUTH_
+backup_swift_url = {{ internal_protocol }}://{{ swift_internal_fqdn | put_address_in_context('url') }}:{{ swift_proxy_server_port }}/v1/AUTH_
 backup_swift_auth = per_user
 backup_swift_auth_version = 1
 backup_swift_user =
@@ -103,7 +103,7 @@ password = {{ cinder_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [oslo_concurrency]
@@ -210,5 +210,5 @@ auth_endpoint = {{ keystone_internal_url }}
 backend_url = {{ redis_connection_string }}
 {% elif cinder_coordination_backend == 'etcd' %}
 # NOTE(jeffrey4l): python-etcd3 module do not support multi endpoint here.
-backend_url = etcd3://{{ hostvars[groups['etcd'][0]]['ansible_' + hostvars[groups['etcd'][0]]['api_interface']]['ipv4']['address'] }}:{{ etcd_client_port }}
+backend_url = etcd3://{{ 'api' | kolla_address(groups['etcd'][0]) | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endif %}
diff --git a/ansible/roles/cloudkitty/defaults/main.yml b/ansible/roles/cloudkitty/defaults/main.yml
index 2621a807fe..d23ec2a121 100644
--- a/ansible/roles/cloudkitty/defaults/main.yml
+++ b/ansible/roles/cloudkitty/defaults/main.yml
@@ -34,7 +34,7 @@ cloudkitty_services:
 ####################
 cloudkitty_database_name: "cloudkitty"
 cloudkitty_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}cloudkitty{% endif %}"
-cloudkitty_database_address: "{{ database_address }}:{{ database_port }}"
+cloudkitty_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -72,9 +72,9 @@ cloudkitty_api_extra_volumes: "{{ cloudkitty_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-cloudkitty_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ cloudkitty_api_port }}"
-cloudkitty_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ cloudkitty_api_port }}"
-cloudkitty_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ cloudkitty_api_port }}"
+cloudkitty_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ cloudkitty_api_port }}"
+cloudkitty_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ cloudkitty_api_port }}"
+cloudkitty_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ cloudkitty_api_port }}"
 
 cloudkitty_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2 b/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
index c3dcf0aa6a..5679aec15b 100644
--- a/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
+++ b/ansible/roles/cloudkitty/templates/cloudkitty.conf.j2
@@ -27,7 +27,7 @@ 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_concurrency]
 lock_path = /var/lib/cloudkitty/tmp
diff --git a/ansible/roles/cloudkitty/templates/wsgi-cloudkitty.conf.j2 b/ansible/roles/cloudkitty/templates/wsgi-cloudkitty.conf.j2
index 03c9b114f8..24b170a966 100644
--- a/ansible/roles/cloudkitty/templates/wsgi-cloudkitty.conf.j2
+++ b/ansible/roles/cloudkitty/templates/wsgi-cloudkitty.conf.j2
@@ -1,6 +1,6 @@
 {% set python_path = '/usr/lib/python2.7/site-packages' if cloudkitty_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
 {% set binary_path = '/usr/bin' if cloudkitty_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
-Listen {{ api_interface_address }}:{{ cloudkitty_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ cloudkitty_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/collectd/templates/collectd.conf.j2 b/ansible/roles/collectd/templates/collectd.conf.j2
index fad5e438fb..3c8ef90f88 100644
--- a/ansible/roles/collectd/templates/collectd.conf.j2
+++ b/ansible/roles/collectd/templates/collectd.conf.j2
@@ -7,5 +7,5 @@ LoadPlugin memory
 LoadPlugin network
 
 <Plugin "network">
-    Server "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}" "{{ collectd_udp_port }}"
+    Server "{{ 'api' | kolla_address }}" "{{ collectd_udp_port }}"
 </Plugin>
diff --git a/ansible/roles/common/templates/conf/output/00-local.conf.j2 b/ansible/roles/common/templates/conf/output/00-local.conf.j2
index 00fd6d5ffe..f9753eb9d6 100644
--- a/ansible/roles/common/templates/conf/output/00-local.conf.j2
+++ b/ansible/roles/common/templates/conf/output/00-local.conf.j2
@@ -33,7 +33,7 @@
     <store>
        @type monasca
        keystone_url {{ keystone_internal_url }}
-       monasca_log_api {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_log_api_port }}
+       monasca_log_api {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_log_api_port }}
        monasca_log_api_version v3.0
        username {{ monasca_agent_user }}
        password {{ monasca_agent_password }}
@@ -81,7 +81,7 @@
     <store>
        @type monasca
        keystone_url {{ keystone_internal_url }}
-       monasca_log_api {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_log_api_port }}
+       monasca_log_api {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_log_api_port }}
        monasca_log_api_version v3.0
        username {{ monasca_agent_user }}
        password {{ monasca_agent_password }}
diff --git a/ansible/roles/common/templates/conf/output/02-monasca.conf.j2 b/ansible/roles/common/templates/conf/output/02-monasca.conf.j2
index 420e6cdad7..dad304e6c1 100644
--- a/ansible/roles/common/templates/conf/output/02-monasca.conf.j2
+++ b/ansible/roles/common/templates/conf/output/02-monasca.conf.j2
@@ -3,7 +3,7 @@
     <store>
        @type monasca
        keystone_url {{ keystone_internal_url }}
-       monasca_log_api {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_log_api_port }}
+       monasca_log_api {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_log_api_port }}
        monasca_log_api_version v3.0
        username {{ monasca_agent_user }}
        password {{ monasca_agent_password }}
diff --git a/ansible/roles/congress/defaults/main.yml b/ansible/roles/congress/defaults/main.yml
index fd1c0e92e5..0837ad7d94 100644
--- a/ansible/roles/congress/defaults/main.yml
+++ b/ansible/roles/congress/defaults/main.yml
@@ -41,7 +41,7 @@ congress_services:
 ####################
 congress_database_name: "congress"
 congress_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}congress{% endif %}"
-congress_database_address: "{{ database_address }}:{{ database_port }}"
+congress_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -90,9 +90,9 @@ congress_datasource_extra_volumes: "{{ congress_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-congress_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ congress_api_port }}"
-congress_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ congress_api_port }}"
-congress_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ congress_api_port }}"
+congress_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ congress_api_port }}"
+congress_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ congress_api_port }}"
+congress_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ congress_api_port }}"
 
 congress_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/congress/templates/congress.conf.j2 b/ansible/roles/congress/templates/congress.conf.j2
index 5eb3260aa3..8bf690796e 100644
--- a/ansible/roles/congress/templates/congress.conf.j2
+++ b/ansible/roles/congress/templates/congress.conf.j2
@@ -40,7 +40,7 @@ password = {{ congress_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
@@ -54,4 +54,4 @@ policy_file = {{ congress_policy_file }}
 enable_proxy_headers_parsing = True
 
 [congress]
-url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ congress_api_port }}
+url = {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ congress_api_port }}
diff --git a/ansible/roles/cyborg/defaults/main.yml b/ansible/roles/cyborg/defaults/main.yml
index e6af6ae413..c2c79b3145 100644
--- a/ansible/roles/cyborg/defaults/main.yml
+++ b/ansible/roles/cyborg/defaults/main.yml
@@ -29,7 +29,7 @@ cyborg_services:
 ####################
 cyborg_database_name: "cyborg"
 cyborg_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}cyborg{% endif %}"
-cyborg_database_address: "{{ database_address }}:{{ database_port }}"
+cyborg_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
@@ -77,9 +77,9 @@ cyborg_conductor_extra_volumes: "{{ cyborg_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-cyborg_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ cyborg_api_port }}"
-cyborg_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ cyborg_api_port }}"
-cyborg_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ cyborg_api_port }}"
+cyborg_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ cyborg_api_port }}"
+cyborg_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ cyborg_api_port }}"
+cyborg_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ cyborg_api_port }}"
 
 cyborg_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/cyborg/templates/cyborg.conf.j2 b/ansible/roles/cyborg/templates/cyborg.conf.j2
index 6fd8dae9ec..7bf0a1b3cd 100644
--- a/ansible/roles/cyborg/templates/cyborg.conf.j2
+++ b/ansible/roles/cyborg/templates/cyborg.conf.j2
@@ -16,14 +16,14 @@ connection = mysql+pymysql://{{ cyborg_database_user }}:{{ cyborg_database_passw
 [keystone_authtoken]
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
-auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_public_port }}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+auth_uri = {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ keystone_public_port }}
 project_domain_name = {{ default_project_domain_name }}
 project_name = service
 user_domain_name = {{ default_user_domain_name }}
 username = {{ cyborg_keystone_user }}
 password = {{ cyborg_keystone_password }}
-auth_url = {{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_admin_port }}
+auth_url = {{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ keystone_admin_port }}
 auth_type = password
 
 {% if cyborg_policy_file is defined %}
diff --git a/ansible/roles/designate/defaults/main.yml b/ansible/roles/designate/defaults/main.yml
index b91a13e46a..162746f620 100644
--- a/ansible/roles/designate/defaults/main.yml
+++ b/ansible/roles/designate/defaults/main.yml
@@ -71,7 +71,7 @@ designate_services:
 ####################
 designate_database_name: "designate"
 designate_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}designate{% endif %}"
-designate_database_address: "{{ database_address }}:{{ database_port }}"
+designate_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -164,9 +164,9 @@ designate_sink_extra_volumes: "{{ designate_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-designate_admin_endpoint: "{{ admin_protocol }}://{{ designate_internal_fqdn }}:{{ designate_api_port }}"
-designate_internal_endpoint: "{{ internal_protocol }}://{{ designate_internal_fqdn }}:{{ designate_api_port }}"
-designate_public_endpoint: "{{ public_protocol }}://{{ designate_external_fqdn }}:{{ designate_api_port }}"
+designate_admin_endpoint: "{{ admin_protocol }}://{{ designate_internal_fqdn | put_address_in_context('url') }}:{{ designate_api_port }}"
+designate_internal_endpoint: "{{ internal_protocol }}://{{ designate_internal_fqdn | put_address_in_context('url') }}:{{ designate_api_port }}"
+designate_public_endpoint: "{{ public_protocol }}://{{ designate_external_fqdn | put_address_in_context('url') }}:{{ designate_api_port }}"
 
 designate_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/designate/tasks/precheck.yml b/ansible/roles/designate/tasks/precheck.yml
index 10d67e6b85..c470b39743 100644
--- a/ansible/roles/designate/tasks/precheck.yml
+++ b/ansible/roles/designate/tasks/precheck.yml
@@ -21,7 +21,7 @@
 
 - name: Checking free port for designate mdns
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + dns_interface]['ipv4']['address'] }}"
+    host: "{{ 'dns' | kolla_address }}"
     port: "{{ designate_mdns_port }}"
     connect_timeout: 1
     timeout: 1
diff --git a/ansible/roles/designate/templates/designate.conf.j2 b/ansible/roles/designate/templates/designate.conf.j2
index 580ccccfdf..686cb3c669 100644
--- a/ansible/roles/designate/templates/designate.conf.j2
+++ b/ansible/roles/designate/templates/designate.conf.j2
@@ -11,8 +11,8 @@ default_pool_id = {{ designate_pool_id }}
 workers = {{ openstack_service_workers }}
 
 [service:api]
-listen = {{ api_interface_address }}:{{ designate_api_listen_port }}
-api_base_uri = {{ internal_protocol }}://{{ designate_internal_fqdn }}:{{ designate_api_port }}
+listen = {{ api_interface_address | put_address_in_context('url') }}:{{ designate_api_listen_port }}
+api_base_uri = {{ internal_protocol }}://{{ designate_internal_fqdn | put_address_in_context('url') }}:{{ designate_api_port }}
 workers = {{ openstack_service_workers }}
 enable_api_admin = True
 enable_host_header = True
@@ -32,7 +32,7 @@ service_token_roles_required = True
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [service:sink]
 enabled_notification_handlers = nova_fixed, neutron_floatingip
@@ -40,7 +40,7 @@ workers = {{ openstack_service_workers }}
 
 {% if service_name == 'designate-mdns' %}
 [service:mdns]
-listen = {{ hostvars[inventory_hostname]['ansible_' + dns_interface]['ipv4']['address'] }}:{{ designate_mdns_port }}
+listen = {{ 'dns' | kolla_address | put_address_in_context('url') }}:{{ designate_mdns_port }}
 workers = {{ openstack_service_workers }}
 {% endif %}
 
@@ -104,5 +104,5 @@ policy_file = {{ designate_policy_file }}
 backend_url = {{ redis_connection_string }}
 {% elif designate_coordination_backend == 'etcd' %}
 # NOTE(noxoid): python-etcd3 does not support multiple endpoints
-backend_url = etcd3://{{ hostvars[groups['etcd'][0]]['ansible_' + hostvars[groups['etcd'][0]]['api_interface']]['ipv4']['address'] }}:{{ etcd_client_port }}
+backend_url = etcd3://{{ 'api' | kolla_address(groups['etcd'][0]) | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endif %}
diff --git a/ansible/roles/designate/templates/named.conf.j2 b/ansible/roles/designate/templates/named.conf.j2
index 94512c4d15..c3b164e4f6 100644
--- a/ansible/roles/designate/templates/named.conf.j2
+++ b/ansible/roles/designate/templates/named.conf.j2
@@ -1,9 +1,9 @@
 #jinja2: trim_blocks: False
 include "/etc/rndc.key";
 options {
-        listen-on port {{ designate_bind_port }} { {{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }}; };
+        listen-on port {{ designate_bind_port }} { {{ 'api' | kolla_address }}; };
         {% if api_interface != dns_interface %}
-        listen-on port {{ designate_bind_port }} { {{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['dns_interface']]['ipv4']['address'] }}; };
+        listen-on port {{ designate_bind_port }} { {{ 'dns' | kolla_address }}; };
         {% endif %}
         directory       "/var/lib/named";
         allow-new-zones yes;
@@ -15,9 +15,9 @@ options {
         forwarders { {{ designate_forwarders_addresses }}; };
         {% endif %}
         minimal-responses yes;
-        allow-notify { {% for host in groups['designate-worker'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }};{% endfor %} };
+        allow-notify { {% for host in groups['designate-worker'] %}{{ 'api' | kolla_address(host) }};{% endfor %} };
 };
 
 controls {
-        inet {{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }} port {{ designate_rndc_port }} allow { {% for host in groups['designate-worker'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}; {% endfor %} } keys { "rndc-key"; };
+        inet {{ 'api' | kolla_address }} port {{ designate_rndc_port }} allow { {% for host in groups['designate-worker'] %}{{ 'api' | kolla_address(host) }}; {% endfor %} } keys { "rndc-key"; };
 };
diff --git a/ansible/roles/designate/templates/pools.yaml.j2 b/ansible/roles/designate/templates/pools.yaml.j2
index eddbba77c7..b88c9ad4c2 100644
--- a/ansible/roles/designate/templates/pools.yaml.j2
+++ b/ansible/roles/designate/templates/pools.yaml.j2
@@ -9,7 +9,7 @@
   nameservers:
 {% if designate_backend == 'bind9' %}
 {% for host in groups['designate-backend-bind9'] %}
-    - host: {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}
+    - host: {{ 'api' | kolla_address(host) }}
       port: {{ designate_bind_port }}
 {% endfor %}
 {% elif designate_backend_external == 'bind9' %}
@@ -22,16 +22,16 @@
 {% if designate_backend == 'bind9' %}
 {% for bind_host in groups['designate-backend-bind9'] %}
     - type: bind9
-      description: BIND9 Server {{ hostvars[bind_host]['ansible_' + hostvars[bind_host]['api_interface']]['ipv4']['address'] }}
+      description: BIND9 Server {{ 'api' | kolla_address(bind_host) }}
       masters:
 {% for mdns_host in groups['designate-mdns'] %}
-        - host: {{ hostvars[mdns_host]['ansible_' + hostvars[mdns_host]['dns_interface']]['ipv4']['address'] }}
+        - host: {{ 'dns' | kolla_address(mdns_host) }}
           port: {{ designate_mdns_port }}
 {% endfor %}
       options:
-        host: {{ hostvars[bind_host]['ansible_' + hostvars[bind_host]['api_interface']]['ipv4']['address'] }}
+        host: {{ 'api' | kolla_address(bind_host) }}
         port: {{ designate_bind_port }}
-        rndc_host: {{ hostvars[bind_host]['ansible_' + hostvars[bind_host]['api_interface']]['ipv4']['address'] }}
+        rndc_host: {{ 'api' | kolla_address(bind_host) }}
         rndc_port: {{ designate_rndc_port }}
         rndc_key_file: /etc/designate/rndc.key
 {% endfor %}
@@ -41,7 +41,7 @@
       description: BIND9 Server {{ bind_host }}
       masters:
 {% for mdns_host in groups['designate-mdns'] %}
-        - host: {{ hostvars[mdns_host]['ansible_' + hostvars[mdns_host]['dns_interface']]['ipv4']['address'] }}
+        - host: {{ 'dns' | kolla_address(mdns_host) }}
           port: {{ designate_mdns_port }}
 {% endfor %}
       options:
@@ -71,7 +71,7 @@
       description: Default Infoblox Pool
       masters:
 {% for mdns_host in groups['designate-mdns'] %}
-        - host: {{ hostvars[mdns_host]['ansible_' + hostvars[mdns_host]['dns_interface']]['ipv4']['address'] }}
+        - host: {{ 'dns' | kolla_address(mdns_host) }}
           port: {{ designate_mdns_port }}
 {% endfor %}
       options:
diff --git a/ansible/roles/designate/templates/rndc.conf.j2 b/ansible/roles/designate/templates/rndc.conf.j2
index 69ec742987..82e139ff07 100644
--- a/ansible/roles/designate/templates/rndc.conf.j2
+++ b/ansible/roles/designate/templates/rndc.conf.j2
@@ -1,6 +1,6 @@
 #include "/etc/rndc.key";
 options {
         default-key "rndc-key";
-        default-server {{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }};
+        default-server {{ 'api' | kolla_address }};
         default-port {{ designate_rndc_port }};
 };
diff --git a/ansible/roles/elasticsearch/tasks/upgrade.yml b/ansible/roles/elasticsearch/tasks/upgrade.yml
index cacf7f2ee5..3633a61a91 100644
--- a/ansible/roles/elasticsearch/tasks/upgrade.yml
+++ b/ansible/roles/elasticsearch/tasks/upgrade.yml
@@ -3,7 +3,7 @@
 # https://www.elastic.co/guide/en/elasticsearch/reference/5.6/restart-upgrade.html
 - name: Disable shard allocation
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/_cluster/settings"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/_cluster/settings"
     method: PUT
     status_code: 200
     return_content: yes
@@ -14,7 +14,7 @@
 
 - name: Perform a synced flush
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/_flush/synced"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/_flush/synced"
     method: POST
     status_code: 200
     return_content: yes
diff --git a/ansible/roles/elasticsearch/templates/elasticsearch.yml.j2 b/ansible/roles/elasticsearch/templates/elasticsearch.yml.j2
index b853914244..618fd03089 100644
--- a/ansible/roles/elasticsearch/templates/elasticsearch.yml.j2
+++ b/ansible/roles/elasticsearch/templates/elasticsearch.yml.j2
@@ -2,12 +2,12 @@
 {% set minimum_master_nodes = (num_nodes / 2 + 1) | round(0, 'floor') | int if num_nodes > 2 else 1 %}
 {% set recover_after_nodes = (num_nodes * 2 / 3) | round(0, 'floor') | int if num_nodes > 1 else 1 %}
 node.name: "{{ api_interface_address }}"
-network.host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+network.host: "{{ 'api' | kolla_address }}"
 
 cluster.name: "{{ elasticsearch_cluster_name }}"
 node.master: true
 node.data: true
-discovery.zen.ping.unicast.hosts: [{% for host in groups['elasticsearch'] %}"{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}"{% if not loop.last %},{% endif %}{% endfor %}]
+discovery.zen.ping.unicast.hosts: [{% for host in groups['elasticsearch'] %}"{{ 'api' | kolla_address(host) }}"{% if not loop.last %},{% endif %}{% endfor %}]
 
 discovery.zen.minimum_master_nodes: {{ minimum_master_nodes }}
 http.port: {{ elasticsearch_port }}
diff --git a/ansible/roles/etcd/defaults/main.yml b/ansible/roles/etcd/defaults/main.yml
index 4d6688aa93..3b9d8f05ab 100644
--- a/ansible/roles/etcd/defaults/main.yml
+++ b/ansible/roles/etcd/defaults/main.yml
@@ -9,12 +9,12 @@ etcd_services:
     environment:
       ETCD_DATA_DIR: "/var/lib/etcd"
       ETCD_NAME: "{{ ansible_hostname }}"
-      ETCD_ADVERTISE_CLIENT_URLS: "{{ internal_protocol }}://{{ api_interface_address }}:{{ etcd_client_port }}"
-      ETCD_LISTEN_CLIENT_URLS: "{{ internal_protocol }}://{{ api_interface_address }}:{{ etcd_client_port }}"
-      ETCD_INITIAL_ADVERTISE_PEER_URLS: "{{ internal_protocol }}://{{ api_interface_address }}:{{ etcd_peer_port }}"
-      ETCD_LISTEN_PEER_URLS: "{{ internal_protocol }}://{{ api_interface_address }}:{{ etcd_peer_port }}"
+      ETCD_ADVERTISE_CLIENT_URLS: "{{ internal_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ etcd_client_port }}"
+      ETCD_LISTEN_CLIENT_URLS: "{{ internal_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ etcd_client_port }}"
+      ETCD_INITIAL_ADVERTISE_PEER_URLS: "{{ internal_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ etcd_peer_port }}"
+      ETCD_LISTEN_PEER_URLS: "{{ internal_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ etcd_peer_port }}"
       ETCD_INITIAL_CLUSTER_TOKEN: "{{ etcd_cluster_token }}"
-      ETCD_INITIAL_CLUSTER: "{% for host in groups['etcd'] %}{{ hostvars[host]['ansible_hostname'] }}={{ internal_protocol }}://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ etcd_peer_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+      ETCD_INITIAL_CLUSTER: "{% for host in groups['etcd'] %}{{ hostvars[host]['ansible_hostname'] }}={{ internal_protocol }}://{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ etcd_peer_port }}{% if not loop.last %},{% endif %}{% endfor %}"
       ETCD_INITIAL_CLUSTER_STATE: "new"
       ETCD_OUT_FILE: "/var/log/kolla/etcd/etcd.log"
       KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
diff --git a/ansible/roles/freezer/defaults/main.yml b/ansible/roles/freezer/defaults/main.yml
index 9b4cb0d927..a701ff82ab 100644
--- a/ansible/roles/freezer/defaults/main.yml
+++ b/ansible/roles/freezer/defaults/main.yml
@@ -34,7 +34,7 @@ freezer_services:
 freezer_database_backend: "mariadb"
 freezer_database_name: "freezer"
 freezer_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}freezer{% endif %}"
-freezer_database_address: "{{ database_address }}:{{ database_port }}"
+freezer_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 freezer_elasticsearch_replicas: "1"
 freezer_es_protocol:
 freezer_es_address:
@@ -77,9 +77,9 @@ freezer_scheduler_extra_volumes: "{{ freezer_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-freezer_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ freezer_api_port }}"
-freezer_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ freezer_api_port }}"
-freezer_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ freezer_api_port }}"
+freezer_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ freezer_api_port }}"
+freezer_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ freezer_api_port }}"
+freezer_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ freezer_api_port }}"
 
 freezer_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/freezer/templates/freezer.conf.j2 b/ansible/roles/freezer/templates/freezer.conf.j2
index 35b5741c62..e31e48c737 100644
--- a/ansible/roles/freezer/templates/freezer.conf.j2
+++ b/ansible/roles/freezer/templates/freezer.conf.j2
@@ -33,7 +33,7 @@ password = {{ freezer_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 {% if freezer_policy_file is defined %}
 [oslo_policy]
@@ -63,7 +63,7 @@ backend = elasticsearch
 driver = elasticsearch
 
 [elasticsearch]
-hosts = {{ freezer_es_protocol }}://{{ freezer_es_address }}:{{ freezer_es_port }}
+hosts = {{ freezer_es_protocol }}://{{ freezer_es_address | put_address_in_context('url') }}:{{ freezer_es_port }}
 number_of_replicas = {{ freezer_elasticsearch_replicas }}
 index = freezer
 {% endif %}
diff --git a/ansible/roles/freezer/templates/wsgi-freezer-api.conf.j2 b/ansible/roles/freezer/templates/wsgi-freezer-api.conf.j2
index 47b696315b..601bfacf5a 100644
--- a/ansible/roles/freezer/templates/wsgi-freezer-api.conf.j2
+++ b/ansible/roles/freezer/templates/wsgi-freezer-api.conf.j2
@@ -1,6 +1,6 @@
 {% set freezer_log_dir = '/var/log/kolla/freezer' %}
 {% set python_path = '/usr/lib/python2.7/site-packages' if freezer_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
-Listen {{ api_interface_address }}:{{ freezer_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ freezer_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/glance/defaults/main.yml b/ansible/roles/glance/defaults/main.yml
index c71f793874..2c661535c3 100644
--- a/ansible/roles/glance/defaults/main.yml
+++ b/ansible/roles/glance/defaults/main.yml
@@ -37,7 +37,7 @@ glance_services:
 ####################
 # HAProxy
 ####################
-haproxy_members: "{% for host in glance_api_hosts %}server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ glance_api_listen_port }} check inter 2000 rise 2 fall 5;{% endfor %}"
+haproxy_members: "{% for host in glance_api_hosts %}server {{ hostvars[host]['ansible_hostname'] }} {{ 'api' | kolla_address(host) }}:{{ glance_api_listen_port }} check inter 2000 rise 2 fall 5;{% endfor %}"
 
 ####################
 # Keystone
@@ -92,7 +92,7 @@ ceph_client_glance_keyring_caps:
 ####################
 glance_database_name: "glance"
 glance_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}glance{% endif %}"
-glance_database_address: "{{ database_address }}:{{ database_port }}"
+glance_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -147,9 +147,9 @@ glance_store_backends: "{{ glance_backends|selectattr('enabled', 'equalto', true
 # OpenStack
 ####################
 
-glance_admin_endpoint: "{{ admin_protocol }}://{{ glance_internal_fqdn }}:{{ glance_api_port }}"
-glance_internal_endpoint: "{{ internal_protocol }}://{{ glance_internal_fqdn }}:{{ glance_api_port }}"
-glance_public_endpoint: "{{ public_protocol }}://{{ glance_external_fqdn }}:{{ glance_api_port }}"
+glance_admin_endpoint: "{{ admin_protocol }}://{{ glance_internal_fqdn | put_address_in_context('url') }}:{{ glance_api_port }}"
+glance_internal_endpoint: "{{ internal_protocol }}://{{ glance_internal_fqdn | put_address_in_context('url') }}:{{ glance_api_port }}"
+glance_public_endpoint: "{{ public_protocol }}://{{ glance_external_fqdn | put_address_in_context('url') }}:{{ glance_api_port }}"
 
 glance_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/glance/templates/glance-api.conf.j2 b/ansible/roles/glance/templates/glance-api.conf.j2
index f26cab7900..a7f68d0198 100644
--- a/ansible/roles/glance/templates/glance-api.conf.j2
+++ b/ansible/roles/glance/templates/glance-api.conf.j2
@@ -38,7 +38,7 @@ password = {{ glance_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [paste_deploy]
 flavor = {% if enable_glance_image_cache | bool %}keystone+cachemanagement{% else %}keystone{% endif %}
diff --git a/ansible/roles/gnocchi/defaults/main.yml b/ansible/roles/gnocchi/defaults/main.yml
index 0a1cb29ed6..b16c6f0a87 100644
--- a/ansible/roles/gnocchi/defaults/main.yml
+++ b/ansible/roles/gnocchi/defaults/main.yml
@@ -69,7 +69,7 @@ swift_admin_tenant_name: "admin"
 ####################
 gnocchi_database_name: "gnocchi"
 gnocchi_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}gnocchi{% endif %}"
-gnocchi_database_address: "{{ database_address }}:{{ database_port }}"
+gnocchi_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -120,9 +120,9 @@ gnocchi_statsd_extra_volumes: "{{ gnocchi_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-gnocchi_admin_endpoint: "{{ admin_protocol }}://{{ gnocchi_internal_fqdn }}:{{ gnocchi_api_port }}"
-gnocchi_internal_endpoint: "{{ internal_protocol }}://{{ gnocchi_internal_fqdn }}:{{ gnocchi_api_port }}"
-gnocchi_public_endpoint: "{{ public_protocol }}://{{ gnocchi_external_fqdn }}:{{ gnocchi_api_port }}"
+gnocchi_admin_endpoint: "{{ admin_protocol }}://{{ gnocchi_internal_fqdn | put_address_in_context('url') }}:{{ gnocchi_api_port }}"
+gnocchi_internal_endpoint: "{{ internal_protocol }}://{{ gnocchi_internal_fqdn | put_address_in_context('url') }}:{{ gnocchi_api_port }}"
+gnocchi_public_endpoint: "{{ public_protocol }}://{{ gnocchi_external_fqdn | put_address_in_context('url') }}:{{ gnocchi_api_port }}"
 
 gnocchi_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/gnocchi/templates/gnocchi.conf.j2 b/ansible/roles/gnocchi/templates/gnocchi.conf.j2
index 98a607fa4f..7a952fe334 100644
--- a/ansible/roles/gnocchi/templates/gnocchi.conf.j2
+++ b/ansible/roles/gnocchi/templates/gnocchi.conf.j2
@@ -53,7 +53,7 @@ auth_type = password
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 {% if gnocchi_policy_file is defined %}
 [oslo_policy]
@@ -87,5 +87,5 @@ swift_project_name = {{ swift_admin_tenant_name }}
 
 {% if enable_grafana | bool %}
 [cors]
-allowed_origin = {{ public_protocol }}://{{ kolla_external_fqdn }}:{{ grafana_server_port }}
+allowed_origin = {{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ grafana_server_port }}
 {% endif %}
diff --git a/ansible/roles/gnocchi/templates/wsgi-gnocchi.conf.j2 b/ansible/roles/gnocchi/templates/wsgi-gnocchi.conf.j2
index 1392aa1515..a78219555d 100644
--- a/ansible/roles/gnocchi/templates/wsgi-gnocchi.conf.j2
+++ b/ansible/roles/gnocchi/templates/wsgi-gnocchi.conf.j2
@@ -4,7 +4,7 @@
     {% set python_path = '/var/lib/kolla/venv/lib/python' + distro_python_version + '/site-packages' %}
 {% endif %}
 {% set wsgi_path = '/usr/bin' if gnocchi_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
-Listen {{ api_interface_address }}:{{ gnocchi_api_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ gnocchi_api_listen_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/grafana/defaults/main.yml b/ansible/roles/grafana/defaults/main.yml
index 57130572d5..7589142a53 100644
--- a/ansible/roles/grafana/defaults/main.yml
+++ b/ansible/roles/grafana/defaults/main.yml
@@ -26,7 +26,7 @@ grafana_services:
 ####################
 grafana_database_name: "grafana"
 grafana_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}grafana{% endif %}"
-grafana_database_address: "{{ database_address }}:{{ database_port }}"
+grafana_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Datasource
@@ -39,7 +39,7 @@ grafana_data_sources:
       database: "telegraf"
       name: "telegraf"
       type: "influxdb"
-      url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ influxdb_http_port }}"
+      url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ influxdb_http_port }}"
       access: "proxy"
       basicAuth: false
   elasticsearch:
@@ -48,7 +48,7 @@ grafana_data_sources:
       name: "elasticsearch"
       type: "elasticsearch"
       access: "proxy"
-      url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}"
+      url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}"
       database: "flog-*"
       jsonData:
         esVersion: 5
diff --git a/ansible/roles/grafana/tasks/post_config.yml b/ansible/roles/grafana/tasks/post_config.yml
index 438f38f57a..07385f5234 100644
--- a/ansible/roles/grafana/tasks/post_config.yml
+++ b/ansible/roles/grafana/tasks/post_config.yml
@@ -1,7 +1,7 @@
 ---
 - name: Wait for grafana application ready
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ grafana_server_port }}/login"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ grafana_server_port }}/login"
     status_code: 200
   register: result
   until: result.get('status') == 200
@@ -11,7 +11,7 @@
 
 - name: Enable grafana datasources
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ grafana_server_port }}/api/datasources"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ grafana_server_port }}/api/datasources"
     method: POST
     user: "{{ grafana_admin_username }}"
     password: "{{ grafana_admin_password }}"
@@ -29,7 +29,7 @@
 
 - name: Disable Getting Started panel
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ grafana_server_port }}/api/user/helpflags/1"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ grafana_server_port }}/api/user/helpflags/1"
     method: PUT
     user: "{{ grafana_admin_username }}"
     password: "{{ grafana_admin_password }}"
diff --git a/ansible/roles/grafana/templates/prometheus.yaml.j2 b/ansible/roles/grafana/templates/prometheus.yaml.j2
index ce9b96748a..f1be95873c 100644
--- a/ansible/roles/grafana/templates/prometheus.yaml.j2
+++ b/ansible/roles/grafana/templates/prometheus.yaml.j2
@@ -5,5 +5,5 @@ datasources:
   type: prometheus
   access: proxy
   orgId: 1
-  url: http://{{ kolla_internal_vip_address }}:{{ prometheus_port }}
+  url: http://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ prometheus_port }}
   version: 1
diff --git a/ansible/roles/haproxy-config/templates/haproxy_single_service_listen.cfg.j2 b/ansible/roles/haproxy-config/templates/haproxy_single_service_listen.cfg.j2
index c23071a373..d8a3dccfcc 100644
--- a/ansible/roles/haproxy-config/templates/haproxy_single_service_listen.cfg.j2
+++ b/ansible/roles/haproxy-config/templates/haproxy_single_service_listen.cfg.j2
@@ -60,9 +60,8 @@ listen {{ service_name }}
             {% endfor %}
         {% else %}
             {% for host in groups[host_group] %}
-                {% set api_interface = "ansible_%s"|format(hostvars[host]['api_interface']) %}
                 {% set host_name = hostvars[host]['ansible_hostname'] %}
-                {% set host_ip = hostvars[host][api_interface]['ipv4']['address'] %}
+                {% set host_ip = 'api' | kolla_address(host) %}
     server {{ host_name }} {{ host_ip }}:{{ listen_port }} {{ haproxy_health_check }}
             {% endfor %}
         {% endif %}
diff --git a/ansible/roles/haproxy-config/templates/haproxy_single_service_split.cfg.j2 b/ansible/roles/haproxy-config/templates/haproxy_single_service_split.cfg.j2
index 2e3eee74a7..27b63e4da6 100644
--- a/ansible/roles/haproxy-config/templates/haproxy_single_service_split.cfg.j2
+++ b/ansible/roles/haproxy-config/templates/haproxy_single_service_split.cfg.j2
@@ -80,9 +80,8 @@ backend {{ service_name }}_back
         {% endfor %}
     {% else %}
         {% for host in groups[host_group] %}
-            {% set api_interface = "ansible_%s"|format(hostvars[host]['api_interface']) %}
             {% set host_name = hostvars[host]['ansible_hostname'] %}
-            {% set host_ip = hostvars[host][api_interface]['ipv4']['address'] %}
+            {% set host_ip = 'api' | kolla_address(host) %}
     server {{ host_name }} {{ host_ip }}:{{ listen_port }} {{ haproxy_health_check }}
         {% endfor %}
     {% endif %}
diff --git a/ansible/roles/haproxy/tasks/precheck.yml b/ansible/roles/haproxy/tasks/precheck.yml
index 6a99c2b78f..7dd8db2651 100644
--- a/ansible/roles/haproxy/tasks/precheck.yml
+++ b/ansible/roles/haproxy/tasks/precheck.yml
@@ -169,13 +169,13 @@
     - inventory_hostname in groups['haproxy']
     - api_interface_address != kolla_internal_vip_address
 
+# FIXME(yoctozepto): this req seems arbitrary, they need not be, just routable is fine
 - name: Checking if kolla_internal_vip_address is in the same network as api_interface on all nodes
   become: true
   command: ip -o addr show dev {{ api_interface }}
   register: ip_addr_output
   changed_when: false
   failed_when: >-
-    '169.254.' not in kolla_internal_vip_address and
     ( ip_addr_output is failed or
      kolla_internal_vip_address | ipaddr(ip_addr_output.stdout.split()[3]) is none)
   when:
diff --git a/ansible/roles/haproxy/tasks/upgrade.yml b/ansible/roles/haproxy/tasks/upgrade.yml
index 20711fcc2c..f77113291a 100644
--- a/ansible/roles/haproxy/tasks/upgrade.yml
+++ b/ansible/roles/haproxy/tasks/upgrade.yml
@@ -1,15 +1,16 @@
 ---
 - include_tasks: config.yml
 
-- set_fact: secondary_addresses={{ hostvars[inventory_hostname]['ansible_' + api_interface].get('ipv4_secondaries', []) | map(attribute='address') | list }}
-
 - name: Stopping all slave keepalived containers
+  vars:
+    key: "{{ 'ipv6' if api_address_family == 'ipv6' else 'ipv4_secondaries' }}"
+    addresses: "{{ hostvars[inventory_hostname]['ansible_' + api_interface].get(key, []) | map(attribute='address') | list }}"
   become: true
   kolla_docker:
     action: "stop_container"
     common_options: "{{ docker_common_options }}"
     name: "keepalived"
-  when: kolla_internal_vip_address not in secondary_addresses
+  when: kolla_internal_vip_address not in addresses
   notify:
     - Restart keepalived container
 
diff --git a/ansible/roles/haproxy/templates/keepalived.conf.j2 b/ansible/roles/haproxy/templates/keepalived.conf.j2
index 0fd81bf8e1..43ac5d7aa3 100644
--- a/ansible/roles/haproxy/templates/keepalived.conf.j2
+++ b/ansible/roles/haproxy/templates/keepalived.conf.j2
@@ -17,7 +17,7 @@ vrrp_instance kolla_internal_vip_{{ keepalived_virtual_router_id }} {
 {% if groups['haproxy'] | length > 1 %}
     unicast_peer {
 {% for host in groups['haproxy'] %}
-{% set ip_addr = hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] %}
+{% set ip_addr = 'api' | kolla_address(host) %}
 {% if ip_addr != api_interface_address %}
         {{ ip_addr }}
 {% endif %}
diff --git a/ansible/roles/heat/defaults/main.yml b/ansible/roles/heat/defaults/main.yml
index aaee97bce6..02b7a77a4d 100644
--- a/ansible/roles/heat/defaults/main.yml
+++ b/ansible/roles/heat/defaults/main.yml
@@ -55,7 +55,7 @@ heat_services:
 ####################
 heat_database_name: "heat"
 heat_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}heat{% endif %}"
-heat_database_address: "{{ database_address }}:{{ database_port }}"
+heat_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -104,12 +104,12 @@ heat_engine_extra_volumes: "{{ heat_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-heat_admin_endpoint: "{{ admin_protocol }}://{{ heat_internal_fqdn }}:{{ heat_api_port }}/v1/%(tenant_id)s"
-heat_internal_endpoint: "{{ internal_protocol }}://{{ heat_internal_fqdn }}:{{ heat_api_port }}/v1/%(tenant_id)s"
-heat_public_endpoint: "{{ public_protocol }}://{{ heat_external_fqdn }}:{{ heat_api_port }}/v1/%(tenant_id)s"
-heat_cfn_admin_endpoint: "{{ admin_protocol }}://{{ heat_cfn_internal_fqdn }}:{{ heat_api_cfn_port }}/v1"
-heat_cfn_internal_endpoint: "{{ internal_protocol }}://{{ heat_cfn_internal_fqdn }}:{{ heat_api_cfn_port }}/v1"
-heat_cfn_public_endpoint: "{{ public_protocol }}://{{ heat_cfn_external_fqdn }}:{{ heat_api_cfn_port }}/v1"
+heat_admin_endpoint: "{{ admin_protocol }}://{{ heat_internal_fqdn | put_address_in_context('url') }}:{{ heat_api_port }}/v1/%(tenant_id)s"
+heat_internal_endpoint: "{{ internal_protocol }}://{{ heat_internal_fqdn | put_address_in_context('url') }}:{{ heat_api_port }}/v1/%(tenant_id)s"
+heat_public_endpoint: "{{ public_protocol }}://{{ heat_external_fqdn | put_address_in_context('url') }}:{{ heat_api_port }}/v1/%(tenant_id)s"
+heat_cfn_admin_endpoint: "{{ admin_protocol }}://{{ heat_cfn_internal_fqdn | put_address_in_context('url') }}:{{ heat_api_cfn_port }}/v1"
+heat_cfn_internal_endpoint: "{{ internal_protocol }}://{{ heat_cfn_internal_fqdn | put_address_in_context('url') }}:{{ heat_api_cfn_port }}/v1"
+heat_cfn_public_endpoint: "{{ public_protocol }}://{{ heat_cfn_external_fqdn | put_address_in_context('url') }}:{{ heat_api_cfn_port }}/v1"
 
 heat_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/heat/templates/heat.conf.j2 b/ansible/roles/heat/templates/heat.conf.j2
index 45b081ab85..5df52f6c84 100644
--- a/ansible/roles/heat/templates/heat.conf.j2
+++ b/ansible/roles/heat/templates/heat.conf.j2
@@ -3,8 +3,8 @@ debug = {{ heat_logging_debug }}
 
 log_dir = /var/log/kolla/heat
 
-heat_metadata_server_url = {{ public_protocol }}://{{ heat_cfn_external_fqdn }}:{{ heat_api_cfn_port }}
-heat_waitcondition_server_url = {{ public_protocol }}://{{ heat_cfn_external_fqdn }}:{{ heat_api_cfn_port }}/v1/waitcondition
+heat_metadata_server_url = {{ public_protocol }}://{{ heat_cfn_external_fqdn | put_address_in_context('url') }}:{{ heat_api_cfn_port }}
+heat_waitcondition_server_url = {{ public_protocol }}://{{ heat_cfn_external_fqdn | put_address_in_context('url') }}:{{ heat_api_cfn_port }}/v1/waitcondition
 
 heat_stack_user_role = {{ heat_stack_user_role }}
 
@@ -52,13 +52,13 @@ password = {{ heat_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [cache]
 backend = oslo_cache.memcache_pool
 enabled = True
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [trustee]
diff --git a/ansible/roles/horizon/defaults/main.yml b/ansible/roles/horizon/defaults/main.yml
index ced3f46934..811ee93f64 100644
--- a/ansible/roles/horizon/defaults/main.yml
+++ b/ansible/roles/horizon/defaults/main.yml
@@ -74,7 +74,7 @@ horizon_keystone_domain_choices:
 ####################
 horizon_database_name: "horizon"
 horizon_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}horizon{% endif %}"
-horizon_database_address: "{{ database_address }}:{{ database_port }}"
+horizon_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
diff --git a/ansible/roles/horizon/templates/horizon.conf.j2 b/ansible/roles/horizon/templates/horizon.conf.j2
index cef86f374d..7c509676ab 100644
--- a/ansible/roles/horizon/templates/horizon.conf.j2
+++ b/ansible/roles/horizon/templates/horizon.conf.j2
@@ -1,6 +1,6 @@
 {% set python_path = '/usr/share/openstack-dashboard' if horizon_install_type == 'binary' else '/var/lib/kolla/venv/lib/python' + distro_python_version + '/site-packages' %}
 
-Listen {{ api_interface_address }}:{{ horizon_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ horizon_listen_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/horizon/templates/local_settings.j2 b/ansible/roles/horizon/templates/local_settings.j2
index 8e9b676be7..16b2bf7d32 100644
--- a/ansible/roles/horizon/templates/local_settings.j2
+++ b/ansible/roles/horizon/templates/local_settings.j2
@@ -179,7 +179,7 @@ SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
 CACHES = {
     'default': {
         'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
-        'LOCATION': [{% for host in groups['memcached'] %}'{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}'{% if not loop.last %},{% endif %}{% endfor %}]
+        'LOCATION': [{% for host in groups['memcached'] %}'{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}'{% if not loop.last %},{% endif %}{% endfor %}]
     }
 }
 {% endif %}
diff --git a/ansible/roles/influxdb/templates/influxdb.conf.j2 b/ansible/roles/influxdb/templates/influxdb.conf.j2
index e849afdc7b..799ce1b31f 100644
--- a/ansible/roles/influxdb/templates/influxdb.conf.j2
+++ b/ansible/roles/influxdb/templates/influxdb.conf.j2
@@ -36,7 +36,7 @@ reporting-disabled = true
   store-interval = "10s"
 [http]
   enabled = true
-  bind-address = "{{ api_interface_address }}:{{ influxdb_http_port }}"
+  bind-address = "{{ api_interface_address | put_address_in_context('url') }}:{{ influxdb_http_port }}"
   auth-enabled = false
   log-enabled = true
   write-tracing = false
diff --git a/ansible/roles/ironic/defaults/main.yml b/ansible/roles/ironic/defaults/main.yml
index 370157c791..5d29aba90f 100644
--- a/ansible/roles/ironic/defaults/main.yml
+++ b/ansible/roles/ironic/defaults/main.yml
@@ -81,11 +81,11 @@ ironic_services:
 ####################
 ironic_database_name: "ironic"
 ironic_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}ironic{% endif %}"
-ironic_database_address: "{{ database_address }}:{{ database_port }}"
+ironic_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ironic_inspector_database_name: "ironic_inspector"
 ironic_inspector_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}ironic_inspector{% endif %}"
-ironic_inspector_database_address: "{{ database_address }}:{{ database_port }}"
+ironic_inspector_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -173,13 +173,13 @@ ironic_dnsmasq_extra_volumes: "{{ ironic_extra_volumes }}"
 ####################
 ironic_inspector_keystone_user: "ironic-inspector"
 
-ironic_admin_endpoint: "{{ admin_protocol }}://{{ ironic_internal_fqdn }}:{{ ironic_api_port }}"
-ironic_internal_endpoint: "{{ internal_protocol }}://{{ ironic_internal_fqdn }}:{{ ironic_api_port }}"
-ironic_public_endpoint: "{{ public_protocol }}://{{ ironic_external_fqdn }}:{{ ironic_api_port }}"
+ironic_admin_endpoint: "{{ admin_protocol }}://{{ ironic_internal_fqdn | put_address_in_context('url') }}:{{ ironic_api_port }}"
+ironic_internal_endpoint: "{{ internal_protocol }}://{{ ironic_internal_fqdn | put_address_in_context('url') }}:{{ ironic_api_port }}"
+ironic_public_endpoint: "{{ public_protocol }}://{{ ironic_external_fqdn | put_address_in_context('url') }}:{{ ironic_api_port }}"
 
-ironic_inspector_admin_endpoint: "{{ admin_protocol }}://{{ ironic_inspector_internal_fqdn }}:{{ ironic_inspector_port }}"
-ironic_inspector_internal_endpoint: "{{ internal_protocol }}://{{ ironic_inspector_internal_fqdn }}:{{ ironic_inspector_port }}"
-ironic_inspector_public_endpoint: "{{ public_protocol }}://{{ ironic_inspector_external_fqdn }}:{{ ironic_inspector_port }}"
+ironic_inspector_admin_endpoint: "{{ admin_protocol }}://{{ ironic_inspector_internal_fqdn | put_address_in_context('url') }}:{{ ironic_inspector_port }}"
+ironic_inspector_internal_endpoint: "{{ internal_protocol }}://{{ ironic_inspector_internal_fqdn | put_address_in_context('url') }}:{{ ironic_inspector_port }}"
+ironic_inspector_public_endpoint: "{{ public_protocol }}://{{ ironic_inspector_external_fqdn | put_address_in_context('url') }}:{{ ironic_inspector_port }}"
 
 ironic_logging_debug: "{{ openstack_logging_debug }}"
 
@@ -197,7 +197,7 @@ ironic_dnsmasq_default_gateway:
 ironic_dnsmasq_boot_file: "{% if enable_ironic_ipxe | bool %}undionly.kpxe{% else %}pxelinux.0{% endif %}"
 ironic_cleaning_network:
 ironic_console_serial_speed: "115200n8"
-ironic_ipxe_url: http://{{ api_interface_address }}:{{ ironic_ipxe_port }}
+ironic_ipxe_url: http://{{ api_interface_address | put_address_in_context('url') }}:{{ ironic_ipxe_port }}
 ironic_enable_rolling_upgrade: "yes"
 ironic_inspector_kernel_cmdline_extras: []
 ironic_inspector_pxe_filter: "{% if enable_neutron | bool %}dnsmasq{% else %}none{% endif %}"
diff --git a/ansible/roles/ironic/templates/inspector.ipxe.j2 b/ansible/roles/ironic/templates/inspector.ipxe.j2
index b82afa83ff..cd25a1ae88 100644
--- a/ansible/roles/ironic/templates/inspector.ipxe.j2
+++ b/ansible/roles/ironic/templates/inspector.ipxe.j2
@@ -13,6 +13,6 @@ chain pxelinux.cfg/${mac:hexhyp} || goto inspector_ipa
 :inspector_ipa
 :retry_boot
 imgfree
-kernel --timeout 30000 {{ ironic_ipxe_url }}/ironic-agent.kernel ipa-inspection-callback-url=http://{{ ironic_inspector_internal_fqdn }}:{{ ironic_inspector_port }}/v1/continue systemd.journald.forward_to_console=yes BOOTIF=${mac} initrd=ironic-agent.initramfs {{ ironic_inspector_kernel_cmdline_extras | join(' ') }} || goto retry_boot
+kernel --timeout 30000 {{ ironic_ipxe_url }}/ironic-agent.kernel ipa-inspection-callback-url=http://{{ ironic_inspector_internal_fqdn | put_address_in_context('url') }}:{{ ironic_inspector_port }}/v1/continue systemd.journald.forward_to_console=yes BOOTIF=${mac} initrd=ironic-agent.initramfs {{ ironic_inspector_kernel_cmdline_extras | join(' ') }} || goto retry_boot
 initrd --timeout 30000 {{ ironic_ipxe_url }}/ironic-agent.initramfs || goto retry_boot
 boot
diff --git a/ansible/roles/ironic/templates/ironic-dnsmasq.conf.j2 b/ansible/roles/ironic/templates/ironic-dnsmasq.conf.j2
index 89a54f9ba1..44e53e4da3 100644
--- a/ansible/roles/ironic/templates/ironic-dnsmasq.conf.j2
+++ b/ansible/roles/ironic/templates/ironic-dnsmasq.conf.j2
@@ -1,13 +1,22 @@
+# NOTE(yoctozepto): ironic-dnsmasq is used to deliver DHCP(v6) service
+# DNS service is disabled:
 port=0
+
 interface={{ ironic_dnsmasq_interface }}
+bind-interfaces
+
 dhcp-range={{ ironic_dnsmasq_dhcp_range }}
+dhcp-sequential-ip
+
+{% if api_address_family == 'ipv6' %}
+{# TODO(yoctozepto): IPv6-only support - DHCPv6 PXE support #}
+{# different options must be used here #}
+{% else %}{# ipv4 #}
 {% if ironic_dnsmasq_default_gateway is not none %}
 dhcp-option=3,{{ ironic_dnsmasq_default_gateway }}
 {% endif %}
 dhcp-option=option:tftp-server,{{ api_interface_address }}
 dhcp-option=option:server-ip-address,{{ api_interface_address }}
-bind-interfaces
-dhcp-sequential-ip
 dhcp-option=210,/tftpboot/
 {% if enable_ironic_ipxe | bool %}
 dhcp-match=ipxe,175
@@ -20,6 +29,8 @@ dhcp-option=tag:ipxe,option:bootfile-name,{{ ironic_ipxe_url }}/inspector.ipxe
 dhcp-option=tag:efi,tag:!ipxe,option:bootfile-name,ipxe.efi
 {% endif %}
 dhcp-option=option:bootfile-name,{{ ironic_dnsmasq_boot_file }}
+{% endif %}{# ipv6/ipv4 #}
+
 {% if ironic_inspector_pxe_filter == 'dnsmasq' %}
 dhcp-hostsdir=/etc/dnsmasq/dhcp-hostsdir
 {% endif %}
diff --git a/ansible/roles/ironic/templates/ironic-inspector.conf.j2 b/ansible/roles/ironic/templates/ironic-inspector.conf.j2
index 1cbd379633..54b68eab5d 100644
--- a/ansible/roles/ironic/templates/ironic-inspector.conf.j2
+++ b/ansible/roles/ironic/templates/ironic-inspector.conf.j2
@@ -40,7 +40,7 @@ password = {{ ironic_inspector_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 {% endif %}
 
 {% if ironic_policy_file is defined %}
diff --git a/ansible/roles/ironic/templates/ironic-ipxe-httpd.conf.j2 b/ansible/roles/ironic/templates/ironic-ipxe-httpd.conf.j2
index 8109e21e3e..41a554d518 100644
--- a/ansible/roles/ironic/templates/ironic-ipxe-httpd.conf.j2
+++ b/ansible/roles/ironic/templates/ironic-ipxe-httpd.conf.j2
@@ -1,4 +1,4 @@
-Listen {{ api_interface_address }}:{{ ironic_ipxe_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ ironic_ipxe_port }}
 
 TraceEnable off
 
diff --git a/ansible/roles/ironic/templates/ironic.conf.j2 b/ansible/roles/ironic/templates/ironic.conf.j2
index 5290bad3b5..d22e1d0f7b 100644
--- a/ansible/roles/ironic/templates/ironic.conf.j2
+++ b/ansible/roles/ironic/templates/ironic.conf.j2
@@ -66,7 +66,7 @@ valid_interfaces = internal
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 {% endif %}
 
 {% if enable_cinder | bool %}
@@ -151,7 +151,7 @@ region_name = {{ openstack_region_name }}
 valid_interfaces = internal
 {% else %}
 auth_type = none
-endpoint_override = {{ internal_protocol }}://{{ ironic_internal_fqdn }}:{{ ironic_api_port }}
+endpoint_override = {{ internal_protocol }}://{{ ironic_internal_fqdn | put_address_in_context('url') }}:{{ ironic_api_port }}
 {% endif %}
 
 [agent]
diff --git a/ansible/roles/ironic/templates/pxelinux.default.j2 b/ansible/roles/ironic/templates/pxelinux.default.j2
index 7f0880970d..518d8aa64d 100644
--- a/ansible/roles/ironic/templates/pxelinux.default.j2
+++ b/ansible/roles/ironic/templates/pxelinux.default.j2
@@ -2,6 +2,6 @@ default introspect
 
 label introspect
 kernel ironic-agent.kernel
-append initrd=ironic-agent.initramfs ipa-inspection-callback-url=http://{{ ironic_inspector_internal_fqdn }}:{{ ironic_inspector_port }}/v1/continue systemd.journald.forward_to_console=yes {{ ironic_inspector_kernel_cmdline_extras | join(' ') }}
+append initrd=ironic-agent.initramfs ipa-inspection-callback-url=http://{{ ironic_inspector_internal_fqdn | put_address_in_context('url') }}:{{ ironic_inspector_port }}/v1/continue systemd.journald.forward_to_console=yes {{ ironic_inspector_kernel_cmdline_extras | join(' ') }}
 
 ipappend 3
diff --git a/ansible/roles/iscsi/templates/tgtd.json.j2 b/ansible/roles/iscsi/templates/tgtd.json.j2
index 3f38ef996f..0501c52fbd 100644
--- a/ansible/roles/iscsi/templates/tgtd.json.j2
+++ b/ansible/roles/iscsi/templates/tgtd.json.j2
@@ -1,4 +1,4 @@
 {
-    "command": "tgtd -d 1 -f --iscsi portal={{ api_interface_address }}:{{ iscsi_port }}",
+    "command": "tgtd -d 1 -f --iscsi portal={{ api_interface_address | put_address_in_context('url') }}:{{ iscsi_port }}",
     "config_files": []
 }
diff --git a/ansible/roles/kafka/defaults/main.yml b/ansible/roles/kafka/defaults/main.yml
index eeb055ae65..720da8bb2b 100644
--- a/ansible/roles/kafka/defaults/main.yml
+++ b/ansible/roles/kafka/defaults/main.yml
@@ -19,7 +19,7 @@ kafka_services:
 kafka_cluster_name: "kolla_kafka"
 kafka_log_dir: "/var/log/kolla/kafka"
 kafka_heap_opts: "-Xmx1G -Xms1G"
-kafka_zookeeper: "{% for host in groups['zookeeper'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ zookeeper_client_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+kafka_zookeeper: "{% for host in groups['zookeeper'] %}{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ zookeeper_client_port }}{% if not loop.last %},{% endif %}{% endfor %}"
 kafka_broker_count: "{{ groups['kafka'] | length }}"
 
 ####################
diff --git a/ansible/roles/kafka/templates/kafka.server.properties.j2 b/ansible/roles/kafka/templates/kafka.server.properties.j2
index db3ada59c0..d7acf2dbd6 100644
--- a/ansible/roles/kafka/templates/kafka.server.properties.j2
+++ b/ansible/roles/kafka/templates/kafka.server.properties.j2
@@ -1,4 +1,4 @@
-listeners=PLAINTEXT://{{ api_interface_address }}:{{ kafka_port }}
+listeners=PLAINTEXT://{{ api_interface_address | put_address_in_context('url') }}:{{ kafka_port }}
 controlled.shutdown.enable=true
 auto.leader.rebalance.enable=true
 num.network.threads=3
diff --git a/ansible/roles/karbor/defaults/main.yml b/ansible/roles/karbor/defaults/main.yml
index 83b0870dae..6c6c3282b6 100644
--- a/ansible/roles/karbor/defaults/main.yml
+++ b/ansible/roles/karbor/defaults/main.yml
@@ -41,7 +41,7 @@ karbor_services:
 ####################
 karbor_database_name: "karbor"
 karbor_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}karbor{% endif %}"
-karbor_database_address: "{{ database_address }}:{{ database_port }}"
+karbor_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -87,9 +87,9 @@ karbor_api_extra_volumes: "{{ karbor_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-karbor_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ karbor_api_port }}/v1/%(project_id)s"
-karbor_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ karbor_api_port }}/v1/%(project_id)s"
-karbor_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ karbor_api_port }}/v1/%(project_id)s"
+karbor_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ karbor_api_port }}/v1/%(project_id)s"
+karbor_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ karbor_api_port }}/v1/%(project_id)s"
+karbor_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ karbor_api_port }}/v1/%(project_id)s"
 
 karbor_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/karbor/templates/karbor.conf.j2 b/ansible/roles/karbor/templates/karbor.conf.j2
index 94d2d65a83..4190e04bf8 100644
--- a/ansible/roles/karbor/templates/karbor.conf.j2
+++ b/ansible/roles/karbor/templates/karbor.conf.j2
@@ -42,7 +42,7 @@ password = {{ karbor_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/keystone/defaults/main.yml b/ansible/roles/keystone/defaults/main.yml
index c6ac1ad0b3..a9f992912a 100644
--- a/ansible/roles/keystone/defaults/main.yml
+++ b/ansible/roles/keystone/defaults/main.yml
@@ -57,7 +57,7 @@ keystone_services:
 ####################
 keystone_database_name: "keystone"
 keystone_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}keystone{% endif %}"
-keystone_database_address: "{{ database_address }}:{{ database_port }}"
+keystone_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
diff --git a/ansible/roles/keystone/templates/fernet-node-sync.sh.j2 b/ansible/roles/keystone/templates/fernet-node-sync.sh.j2
index 971b332760..afc7d87052 100644
--- a/ansible/roles/keystone/templates/fernet-node-sync.sh.j2
+++ b/ansible/roles/keystone/templates/fernet-node-sync.sh.j2
@@ -11,6 +11,6 @@ fi
 # For each host node sync tokens
 {% for host in groups['keystone'] %}
 {% if inventory_hostname != host %}
-/usr/bin/rsync -azu --delete -e 'ssh -i /var/lib/keystone/.ssh/id_rsa -p {{ hostvars[host]['keystone_ssh_port'] }} -F /var/lib/keystone/.ssh/config' keystone@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:/etc/keystone/fernet-keys/ /etc/keystone/fernet-keys
+/usr/bin/rsync -azu --delete -e 'ssh -i /var/lib/keystone/.ssh/id_rsa -p {{ hostvars[host]['keystone_ssh_port'] }} -F /var/lib/keystone/.ssh/config' keystone@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:/etc/keystone/fernet-keys/ /etc/keystone/fernet-keys
 {% endif %}
 {% endfor %}
diff --git a/ansible/roles/keystone/templates/fernet-push.sh.j2 b/ansible/roles/keystone/templates/fernet-push.sh.j2
index cd77375812..6179cb2a81 100644
--- a/ansible/roles/keystone/templates/fernet-push.sh.j2
+++ b/ansible/roles/keystone/templates/fernet-push.sh.j2
@@ -2,6 +2,6 @@
 
 {% for host in groups['keystone'] %}
 {% if inventory_hostname != host %}
-/usr/bin/rsync -az -e 'ssh -i /var/lib/keystone/.ssh/id_rsa -p {{ hostvars[host]['keystone_ssh_port'] }} -F /var/lib/keystone/.ssh/config' --delete /etc/keystone/fernet-keys/ keystone@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:/etc/keystone/fernet-keys
+/usr/bin/rsync -az -e 'ssh -i /var/lib/keystone/.ssh/id_rsa -p {{ hostvars[host]['keystone_ssh_port'] }} -F /var/lib/keystone/.ssh/config' --delete /etc/keystone/fernet-keys/ keystone@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:/etc/keystone/fernet-keys
 {% endif %}
 {% endfor %}
diff --git a/ansible/roles/keystone/templates/keystone.conf.j2 b/ansible/roles/keystone/templates/keystone.conf.j2
index 8340dd5d5d..d8664e96ae 100644
--- a/ansible/roles/keystone/templates/keystone.conf.j2
+++ b/ansible/roles/keystone/templates/keystone.conf.j2
@@ -46,7 +46,7 @@ max_active_keys = {{ ((fernet_token_expiry | int +
 [cache]
 backend = oslo_cache.memcache_pool
 enabled = True
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
@@ -68,5 +68,5 @@ connection_string = {{ osprofiler_backend_connection_string }}
 
 {% if enable_grafana | bool %}
 [cors]
-allowed_origin = {{ public_protocol }}://{{ kolla_external_fqdn }}:{{ grafana_server_port }}
+allowed_origin = {{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ grafana_server_port }}
 {% endif %}
diff --git a/ansible/roles/keystone/templates/wsgi-keystone.conf.j2 b/ansible/roles/keystone/templates/wsgi-keystone.conf.j2
index 1dc914a5d2..99899e4f2e 100644
--- a/ansible/roles/keystone/templates/wsgi-keystone.conf.j2
+++ b/ansible/roles/keystone/templates/wsgi-keystone.conf.j2
@@ -5,8 +5,8 @@
 {% set python_path = '/var/lib/kolla/venv/lib/python' + distro_python_version + '/site-packages' %}
 {% endif %}
 {% set binary_path = '/usr/bin' if keystone_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
-Listen {{ api_interface_address }}:{{ keystone_public_listen_port }}
-Listen {{ api_interface_address }}:{{ keystone_admin_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ keystone_public_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ keystone_admin_listen_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/kibana/tasks/post_config.yml b/ansible/roles/kibana/tasks/post_config.yml
index bba785ee45..bc3b512550 100644
--- a/ansible/roles/kibana/tasks/post_config.yml
+++ b/ansible/roles/kibana/tasks/post_config.yml
@@ -7,7 +7,7 @@
 
 - name: Register the kibana index in elasticsearch
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/.kibana"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/.kibana"
     method: PUT
     body: "{{ kibana_default_index_options | to_json }}"
     body_format: json
@@ -22,7 +22,7 @@
 
 - name: Wait for kibana to register in elasticsearch
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/.kibana"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/.kibana"
     status_code: 200
   register: result
   until: result.status == 200
@@ -32,7 +32,7 @@
 
 - name: Change kibana config to set index as defaultIndex
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/.kibana/config/*"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/.kibana/config/*"
     method: PUT
     body:
       defaultIndex: "{{ kibana_default_index_pattern }}"
@@ -44,7 +44,7 @@
   uri:
     headers:
       Content-Type: application/json
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/.kibana"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/.kibana"
     method: GET
   register: kibana_default_indexes
   run_once: true
@@ -60,7 +60,7 @@
 
 - name: Add index pattern to kibana
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}/.kibana/index-pattern/{{ kibana_default_index_pattern }}"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}/.kibana/index-pattern/{{ kibana_default_index_pattern }}"
     method: PUT
     body: "{{ kibana_default_index | to_json }}"
     body_format: json
diff --git a/ansible/roles/kibana/templates/kibana.yml.j2 b/ansible/roles/kibana/templates/kibana.yml.j2
index ecd33f2d4a..581dcafce0 100644
--- a/ansible/roles/kibana/templates/kibana.yml.j2
+++ b/ansible/roles/kibana/templates/kibana.yml.j2
@@ -2,7 +2,7 @@ kibana.defaultAppId: "{{ kibana_default_app_id }}"
 logging.dest: /var/log/kolla/kibana/kibana.log
 server.port: {{ kibana_server_port }}
 server.host: "{{ api_interface_address }}"
-elasticsearch.url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ elasticsearch_port }}"
+elasticsearch.url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ elasticsearch_port }}"
 elasticsearch.requestTimeout: {{ kibana_elasticsearch_request_timeout }}
 elasticsearch.shardTimeout: {{ kibana_elasticsearch_shard_timeout }}
 elasticsearch.ssl.verificationMode: "{{ 'full' if kibana_elasticsearch_ssl_verify | bool else 'none' }}"
diff --git a/ansible/roles/kuryr/templates/kuryr.conf.j2 b/ansible/roles/kuryr/templates/kuryr.conf.j2
index 9aad659266..b41e5f41e9 100644
--- a/ansible/roles/kuryr/templates/kuryr.conf.j2
+++ b/ansible/roles/kuryr/templates/kuryr.conf.j2
@@ -1,5 +1,5 @@
 [DEFAULT]
-kuryr_uri = {{ internal_protocol }}://{{ api_interface_address }}:{{ kuryr_port }}
+kuryr_uri = {{ internal_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ kuryr_port }}
 debug = {{ kuryr_logging_debug }}
 log_dir = /var/log/kolla/kuryr
 
diff --git a/ansible/roles/kuryr/templates/kuryr.spec.j2 b/ansible/roles/kuryr/templates/kuryr.spec.j2
index e9a7ada93a..15ccc34da6 100644
--- a/ansible/roles/kuryr/templates/kuryr.spec.j2
+++ b/ansible/roles/kuryr/templates/kuryr.spec.j2
@@ -1 +1 @@
-http://{{ api_interface_address }}:{{ kuryr_port }}
+http://{{ api_interface_address | put_address_in_context('url') }}:{{ kuryr_port }}
diff --git a/ansible/roles/magnum/defaults/main.yml b/ansible/roles/magnum/defaults/main.yml
index 818c74cc66..89e4983a3a 100644
--- a/ansible/roles/magnum/defaults/main.yml
+++ b/ansible/roles/magnum/defaults/main.yml
@@ -37,7 +37,7 @@ magnum_services:
 ####################
 magnum_database_name: "magnum"
 magnum_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}magnum{% endif %}"
-magnum_database_address: "{{ database_address }}:{{ database_port }}"
+magnum_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -85,9 +85,9 @@ magnum_conductor_extra_volumes: "{{ magnum_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-magnum_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ magnum_api_port }}/v1"
-magnum_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ magnum_api_port }}/v1"
-magnum_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ magnum_api_port }}/v1"
+magnum_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ magnum_api_port }}/v1"
+magnum_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ magnum_api_port }}/v1"
+magnum_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ magnum_api_port }}/v1"
 
 magnum_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/magnum/templates/magnum.conf.j2 b/ansible/roles/magnum/templates/magnum.conf.j2
index 67517f68ee..c7c5fcdad8 100644
--- a/ansible/roles/magnum/templates/magnum.conf.j2
+++ b/ansible/roles/magnum/templates/magnum.conf.j2
@@ -68,7 +68,7 @@ password = {{ magnum_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [trust]
 trustee_domain_admin_password = {{ magnum_keystone_password }}
diff --git a/ansible/roles/manila/defaults/main.yml b/ansible/roles/manila/defaults/main.yml
index a668c1f4e2..e6ca925350 100644
--- a/ansible/roles/manila/defaults/main.yml
+++ b/ansible/roles/manila/defaults/main.yml
@@ -65,7 +65,7 @@ ceph_client_manila_keyring_caps:
 #####################
 manila_database_name: "manila"
 manila_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}manila{% endif %}"
-manila_database_address: "{{ database_address }}:{{ database_port }}"
+manila_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 #####################
@@ -129,12 +129,12 @@ manila_data_extra_volumes: "{{ manila_extra_volumes }}"
 #####################
 ## OpenStack
 #####################
-manila_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ manila_api_port }}/v1/%(tenant_id)s"
-manila_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ manila_api_port }}/v1/%(tenant_id)s"
-manila_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ manila_api_port }}/v1/%(tenant_id)s"
-manila_v2_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ manila_api_port }}/v2/%(tenant_id)s"
-manila_v2_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ manila_api_port }}/v2/%(tenant_id)s"
-manila_v2_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ manila_api_port }}/v2/%(tenant_id)s"
+manila_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ manila_api_port }}/v1/%(tenant_id)s"
+manila_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ manila_api_port }}/v1/%(tenant_id)s"
+manila_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ manila_api_port }}/v1/%(tenant_id)s"
+manila_v2_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ manila_api_port }}/v2/%(tenant_id)s"
+manila_v2_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ manila_api_port }}/v2/%(tenant_id)s"
+manila_v2_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ manila_api_port }}/v2/%(tenant_id)s"
 
 manila_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/manila/templates/manila-share.conf.j2 b/ansible/roles/manila/templates/manila-share.conf.j2
index 26989d33b3..0e66d9bdc7 100644
--- a/ansible/roles/manila/templates/manila-share.conf.j2
+++ b/ansible/roles/manila/templates/manila-share.conf.j2
@@ -19,7 +19,7 @@ password = {{ cinder_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [nova]
 auth_uri = {{ keystone_internal_url }}
@@ -35,10 +35,10 @@ password = {{ nova_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [neutron]
-url = {{ internal_protocol }}://{{ neutron_internal_fqdn }}:{{ neutron_server_port }}
+url = {{ internal_protocol }}://{{ neutron_internal_fqdn | put_address_in_context('url') }}:{{ neutron_server_port }}
 auth_uri = {{ keystone_internal_url }}
 auth_url = {{ keystone_admin_url }}
 auth_type = password
@@ -52,7 +52,7 @@ password = {{ neutron_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 {% if enable_manila_backend_generic | bool %}
 [generic]
diff --git a/ansible/roles/manila/templates/manila.conf.j2 b/ansible/roles/manila/templates/manila.conf.j2
index 2b01dc1399..7ee7f9a917 100644
--- a/ansible/roles/manila/templates/manila.conf.j2
+++ b/ansible/roles/manila/templates/manila.conf.j2
@@ -40,7 +40,7 @@ password = {{ manila_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/mariadb/defaults/main.yml b/ansible/roles/mariadb/defaults/main.yml
index 50d661f57b..054eb9bd59 100644
--- a/ansible/roles/mariadb/defaults/main.yml
+++ b/ansible/roles/mariadb/defaults/main.yml
@@ -45,7 +45,7 @@ database_max_timeout: 120
 ####################
 # HAProxy
 ####################
-internal_haproxy_members: "{% for host in groups['mariadb'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ mariadb_port }} check inter 2000 rise 2 fall 5{% if not loop.first %} backup{% endif %};{% endfor %}"
+internal_haproxy_members: "{% for host in groups['mariadb'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ 'api' | kolla_address(host) }}:{{ mariadb_port }} check inter 2000 rise 2 fall 5{% if not loop.first %} backup{% endif %};{% endfor %}"
 external_haproxy_members: "{% for host in groups['mariadb'] %}server {{ host }} {{ host }}:{{ mariadb_port }} check inter 2000 rise 2 fall 5{% if not loop.first %} backup{% endif %};{% endfor %}"
 
 ####################
diff --git a/ansible/roles/mariadb/templates/galera.cnf.j2 b/ansible/roles/mariadb/templates/galera.cnf.j2
index 5ee209cd37..a66b8c87d8 100644
--- a/ansible/roles/mariadb/templates/galera.cnf.j2
+++ b/ansible/roles/mariadb/templates/galera.cnf.j2
@@ -26,12 +26,12 @@ character-set-server = utf8
 
 datadir=/var/lib/mysql/
 
-wsrep_cluster_address=gcomm://{% if (groups['mariadb'] | length) > 1 %}{% for host in groups['mariadb'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ mariadb_wsrep_port }}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
+wsrep_cluster_address=gcomm://{% if (groups['mariadb'] | length) > 1 %}{% for host in groups['mariadb'] %}{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ mariadb_wsrep_port }}{% if not loop.last %},{% endif %}{% endfor %}{% endif %}
 
-wsrep_provider_options=gmcast.listen_addr=tcp://{{ api_interface_address }}:{{ mariadb_wsrep_port }};ist.recv_addr={{ api_interface_address }}:{{ mariadb_ist_port }}
+wsrep_provider_options=gmcast.listen_addr=tcp://{{ api_interface_address | put_address_in_context('url') }}:{{ mariadb_wsrep_port }};ist.recv_addr={{ api_interface_address | put_address_in_context('url') }}:{{ mariadb_ist_port }}
 
-wsrep_node_address={{ api_interface_address }}:{{ mariadb_wsrep_port }}
-wsrep_sst_receive_address={{ api_interface_address }}:{{ mariadb_sst_port }}
+wsrep_node_address={{ api_interface_address | put_address_in_context('url') }}:{{ mariadb_wsrep_port }}
+wsrep_sst_receive_address={{ api_interface_address | put_address_in_context('url') }}:{{ mariadb_sst_port }}
 
 wsrep_provider={{ wsrep_driver }}
 wsrep_cluster_name="{{ database_cluster_name }}"
@@ -61,3 +61,12 @@ innodb_lock_schedule_algorithm = FCFS
 
 [server]
 pid-file=/var/lib/mysql/mariadb.pid
+
+[sst]
+{% if sst_method == 'mariabackup' and api_address_family == 'ipv6' %}
+# NOTE(yoctozepto): for IPv6 we need to tweak sockopt for socat (mariabackup sst backend)
+# see: https://mariadb.com/kb/en/library/xtrabackup-v2-sst-method/#performing-ssts-with-ipv6-addresses
+# and: https://jira.mariadb.org/browse/MDEV-18797
+# this can be removed when MDEV-18797 is resolved
+sockopt=",pf=ip6"
+{% endif %}
diff --git a/ansible/roles/masakari/defaults/main.yml b/ansible/roles/masakari/defaults/main.yml
index 7423749e7e..00d1723bb1 100644
--- a/ansible/roles/masakari/defaults/main.yml
+++ b/ansible/roles/masakari/defaults/main.yml
@@ -42,7 +42,7 @@ masakari_services:
 ####################
 masakari_database_name: "masakari"
 masakari_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}masakari{% endif %}"
-masakari_database_address: "{{ database_address }}:{{ database_port }}"
+masakari_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
@@ -92,9 +92,9 @@ masakari_instancemonitor_default_volumes:
 ####################
 # OpenStack
 ####################
-masakari_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ masakari_api_port }}"
-masakari_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ masakari_api_port }}"
-masakari_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ masakari_api_port }}"
+masakari_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ masakari_api_port }}"
+masakari_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ masakari_api_port }}"
+masakari_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ masakari_api_port }}"
 
 masakari_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/masakari/templates/masakari-monitors.conf.j2 b/ansible/roles/masakari/templates/masakari-monitors.conf.j2
index c3a3241b6e..142075894a 100644
--- a/ansible/roles/masakari/templates/masakari-monitors.conf.j2
+++ b/ansible/roles/masakari/templates/masakari-monitors.conf.j2
@@ -12,4 +12,4 @@ username = {{ masakari_keystone_user }}
 password = {{ masakari_keystone_password }}
 
 [libvirt]
-connection_uri = "qemu+tcp://{{ migration_interface_address }}/system"
+connection_uri = "qemu+tcp://{{ migration_interface_address | put_address_in_context('url') }}/system"
diff --git a/ansible/roles/masakari/templates/masakari.conf.j2 b/ansible/roles/masakari/templates/masakari.conf.j2
index b8d97c6f58..4111eac980 100644
--- a/ansible/roles/masakari/templates/masakari.conf.j2
+++ b/ansible/roles/masakari/templates/masakari.conf.j2
@@ -32,7 +32,7 @@ region_name = {{ openstack_region_name }}
 {% if enable_memcached | bool %}
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 {% endif %}
 
 [oslo_messaging_notifications]
diff --git a/ansible/roles/masakari/templates/wsgi-masakari.conf.j2 b/ansible/roles/masakari/templates/wsgi-masakari.conf.j2
index a551aa0e75..9849b5bb49 100644
--- a/ansible/roles/masakari/templates/wsgi-masakari.conf.j2
+++ b/ansible/roles/masakari/templates/wsgi-masakari.conf.j2
@@ -5,7 +5,7 @@
 {% endif %}
 {% set binary_path = '/usr/bin' if masakari_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
 
-Listen {{ api_interface_address }}:{{ masakari_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ masakari_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/memcached/defaults/main.yml b/ansible/roles/memcached/defaults/main.yml
index d721fe511e..0fc550288f 100644
--- a/ansible/roles/memcached/defaults/main.yml
+++ b/ansible/roles/memcached/defaults/main.yml
@@ -25,7 +25,7 @@ memcached_services:
 ####################
 # HAProxy
 ####################
-haproxy_members: "{% for host in groups['memcached'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }} check inter 2000 rise 2 fall 5{% if not loop.first %} backup{% endif %};{% endfor %}"
+haproxy_members: "{% for host in groups['memcached'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ 'api' | kolla_address(host) }}:{{ memcached_port }} check inter 2000 rise 2 fall 5{% if not loop.first %} backup{% endif %};{% endfor %}"
 
 ####################
 # Docker
diff --git a/ansible/roles/mistral/defaults/main.yml b/ansible/roles/mistral/defaults/main.yml
index b8f0dac904..321daab5e9 100644
--- a/ansible/roles/mistral/defaults/main.yml
+++ b/ansible/roles/mistral/defaults/main.yml
@@ -48,7 +48,7 @@ mistral_services:
 ####################
 mistral_database_name: "mistral"
 mistral_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}mistral{% endif %}"
-mistral_database_address: "{{ database_address }}:{{ database_port }}"
+mistral_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -108,9 +108,9 @@ mistral_api_extra_volumes: "{{ mistral_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-mistral_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ mistral_api_port }}/v2"
-mistral_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ mistral_api_port }}/v2"
-mistral_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ mistral_api_port }}/v2"
+mistral_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ mistral_api_port }}/v2"
+mistral_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ mistral_api_port }}/v2"
+mistral_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ mistral_api_port }}/v2"
 
 mistral_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/mistral/templates/mistral.conf.j2 b/ansible/roles/mistral/templates/mistral.conf.j2
index 60e7cb3094..b612cc57ff 100644
--- a/ansible/roles/mistral/templates/mistral.conf.j2
+++ b/ansible/roles/mistral/templates/mistral.conf.j2
@@ -48,11 +48,11 @@ password = {{ mistral_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [mistral]
-url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ mistral_api_port }}
+url = {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ mistral_api_port }}
 
 [openstack_actions]
 os_actions_endpoint_type = internal
diff --git a/ansible/roles/monasca/defaults/main.yml b/ansible/roles/monasca/defaults/main.yml
index ccd2294018..c0eb7bc139 100644
--- a/ansible/roles/monasca/defaults/main.yml
+++ b/ansible/roles/monasca/defaults/main.yml
@@ -141,11 +141,11 @@ monasca_influxdb_retention_policy:
 ####################
 # Monasca
 ####################
-monasca_kafka_servers: "{% for host in groups['kafka'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ kafka_port }}{% if not loop.last %},{% endif %}{% endfor %}"
-monasca_zookeeper_servers: "{% for host in groups['zookeeper'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ zookeeper_client_port }}{% if not loop.last %},{% endif %}{% endfor %}"
-monasca_memcached_servers: "{% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}"
-monasca_elasticsearch_servers: "{% for host in groups['elasticsearch'] %}'{{ internal_protocol }}://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ elasticsearch_port }}'{% if not loop.last %},{% endif %}{% endfor %}"
-monasca_storm_nimbus_servers: "{% for host in groups['storm-nimbus'] %}'{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}'{% if not loop.last %},{% endif %}{% endfor %}"
+monasca_kafka_servers: "{% for host in groups['kafka'] %}{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ kafka_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+monasca_zookeeper_servers: "{% for host in groups['zookeeper'] %}{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ zookeeper_client_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+monasca_memcached_servers: "{% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+monasca_elasticsearch_servers: "{% for host in groups['elasticsearch'] %}'{{ internal_protocol }}://{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ elasticsearch_port }}'{% if not loop.last %},{% endif %}{% endfor %}"
+monasca_storm_nimbus_servers: "{% for host in groups['storm-nimbus'] %}'{{ 'api' | kolla_address(host) }}'{% if not loop.last %},{% endif %}{% endfor %}"
 # NOTE(dszumski): Only one NTP server is currently supported by the Monasca Agent plugin
 monasca_ntp_server: "{{ external_ntp_servers | first }}"
 
@@ -189,7 +189,7 @@ monasca_grafana_data_sources:
       name: "Monasca API"
       type: "monasca-datasource"
       access: "proxy"
-      url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_api_port }}"
+      url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_api_port }}"
       isDefault: True
       basicAuth: false
       jsonData:
@@ -329,13 +329,13 @@ monasca_agent_authorized_roles:
 monasca_delegate_authorized_roles:
   - admin
 
-monasca_api_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_api_port }}/v2.0"
-monasca_api_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_api_port }}/v2.0"
-monasca_api_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ monasca_api_port }}/v2.0"
+monasca_api_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_api_port }}/v2.0"
+monasca_api_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_api_port }}/v2.0"
+monasca_api_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ monasca_api_port }}/v2.0"
 
-monasca_log_api_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_log_api_port }}"
-monasca_log_api_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ monasca_log_api_port }}"
-monasca_log_api_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ monasca_log_api_port }}"
+monasca_log_api_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_log_api_port }}"
+monasca_log_api_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ monasca_log_api_port }}"
+monasca_log_api_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ monasca_log_api_port }}"
 
 monasca_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/monasca/tasks/post_config.yml b/ansible/roles/monasca/tasks/post_config.yml
index b41f937282..4edc8b243d 100644
--- a/ansible/roles/monasca/tasks/post_config.yml
+++ b/ansible/roles/monasca/tasks/post_config.yml
@@ -1,7 +1,7 @@
 ---
 - name: Wait for Monasca Grafana to load
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/login"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/login"
     status_code: 200
   register: result
   until: result.get('status') == 200
@@ -16,7 +16,7 @@
 - name: List Monasca Grafana organisations
   uri:
     method: GET
-    url: '{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/api/orgs'
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/api/orgs"
     user: '{{ monasca_grafana_admin_username }}'
     password: '{{ monasca_grafana_admin_password }}'
     return_content: true
@@ -27,7 +27,7 @@
 - name: Create default control plane organisation if it doesn't exist
   uri:
     method: POST
-    url: '{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/api/orgs'
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/api/orgs"
     user: '{{ monasca_grafana_admin_username }}'
     password: '{{ monasca_grafana_admin_password }}'
     body_format: json
@@ -40,7 +40,7 @@
 - name: Lookup Monasca Grafana control plane organisation ID
   uri:
     method: GET
-    url: '{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/api/orgs/name/{{ monasca_grafana_control_plane_org }}'
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/api/orgs/name/{{ monasca_grafana_control_plane_org }}"
     user: '{{ monasca_grafana_admin_username }}'
     password: '{{ monasca_grafana_admin_password }}'
     return_content: true
@@ -51,7 +51,7 @@
 - name: Add {{ monasca_grafana_admin_username }} user to control plane organisation
   uri:
     method: POST
-    url: '{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/api/orgs/{{ monasca_grafana_conf_org.json.id }}/users'
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/api/orgs/{{ monasca_grafana_conf_org.json.id }}/users"
     user: '{{ monasca_grafana_admin_username }}'
     password: '{{ monasca_grafana_admin_password }}'
     body:
@@ -69,7 +69,7 @@
 - name: Switch Monasca Grafana to the control plane organisation
   uri:
     method: POST
-    url: '{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/api/user/using/{{ monasca_grafana_conf_org.json.id }}'
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/api/user/using/{{ monasca_grafana_conf_org.json.id }}"
     user: '{{ monasca_grafana_admin_username }}'
     password: '{{ monasca_grafana_admin_password }}'
     force_basic_auth: true
@@ -77,7 +77,7 @@
 
 - name: Enable Monasca Grafana datasource for control plane organisation
   uri:
-    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ monasca_grafana_server_port }}/api/datasources"
+    url: "{{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ monasca_grafana_server_port }}/api/datasources"
     method: POST
     user: "{{ monasca_grafana_admin_username }}"
     password: "{{ monasca_grafana_admin_password }}"
diff --git a/ansible/roles/monasca/templates/monasca-api/api.conf.j2 b/ansible/roles/monasca/templates/monasca-api/api.conf.j2
index 436828bbd5..dc1aa4ec71 100644
--- a/ansible/roles/monasca/templates/monasca-api/api.conf.j2
+++ b/ansible/roles/monasca/templates/monasca-api/api.conf.j2
@@ -6,7 +6,7 @@ region = {{ openstack_region_name }}
 
 [database]
 database = {{ monasca_database_name }}
-connection = mysql+pymysql://{{ monasca_database_user }}:{{ monasca_database_password }}@{{ monasca_database_address }}:{{ monasca_database_port }}/{{ monasca_database_name }}
+connection = mysql+pymysql://{{ monasca_database_user }}:{{ monasca_database_password }}@{{ monasca_database_address | put_address_in_context('url') }}:{{ monasca_database_port }}/{{ monasca_database_name }}
 
 [influxdb]
 database_name = {{ monasca_influxdb_name }}
diff --git a/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2 b/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2
index 13da267d61..e5cf902967 100644
--- a/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2
+++ b/ansible/roles/monasca/templates/monasca-api/wsgi-api.conf.j2
@@ -1,7 +1,7 @@
 {% set python_path = '/usr/lib/python2.7/site-packages' if monasca_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
 {% set wsgi_path = '/usr/bin' if monasca_install_type == 'binary' else '/monasca-api/monasca_api/api' %}
 
-Listen {{ api_interface_address }}:{{ monasca_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ monasca_api_port }}
 
 TraceEnable off
 
diff --git a/ansible/roles/monasca/templates/monasca-grafana/grafana.ini.j2 b/ansible/roles/monasca/templates/monasca-grafana/grafana.ini.j2
index 5c7f4afbdf..89067ef7bf 100644
--- a/ansible/roles/monasca/templates/monasca-grafana/grafana.ini.j2
+++ b/ansible/roles/monasca/templates/monasca-grafana/grafana.ini.j2
@@ -19,7 +19,7 @@ enable_gzip = false
 
 [database]
 type = mysql
-host = {{ monasca_database_address }}:{{ monasca_database_port }}
+host = {{ monasca_database_address | put_address_in_context('url') }}:{{ monasca_database_port }}
 name = {{ monasca_grafana_database_name }}
 user = {{ monasca_database_user }}
 password = {{ monasca_database_password }}
@@ -31,7 +31,7 @@ execute_alerts = false
 
 [session]
 provider = mysql
-provider_config = {{ monasca_database_user }}:{{ monasca_database_password }}@tcp({{ monasca_database_address }}:{{ monasca_database_port }})/{{ monasca_grafana_database_name }}
+provider_config = {{ monasca_database_user }}:{{ monasca_database_password }}@tcp({{ monasca_database_address | put_address_in_context('url') }}:{{ monasca_database_port }})/{{ monasca_grafana_database_name }}
 
 cookie_name = monasca_grafana_sess
 cookie_secure = false
diff --git a/ansible/roles/monasca/templates/monasca-log-api/wsgi-log-api.conf.j2 b/ansible/roles/monasca/templates/monasca-log-api/wsgi-log-api.conf.j2
index b72850acae..eac430f1af 100644
--- a/ansible/roles/monasca/templates/monasca-log-api/wsgi-log-api.conf.j2
+++ b/ansible/roles/monasca/templates/monasca-log-api/wsgi-log-api.conf.j2
@@ -1,7 +1,7 @@
 {% set python_path = '/usr/lib/python2.7/site-packages' if monasca_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
 {% set wsgi_path = '/usr/bin' if monasca_install_type == 'binary' else '/monasca-log/monasca_log_api/app' %}
 
-Listen {{ api_interface_address }}:{{ monasca_log_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ monasca_log_api_port }}
 
 TraceEnable off
 
diff --git a/ansible/roles/monasca/templates/monasca-thresh/storm.yml.j2 b/ansible/roles/monasca/templates/monasca-thresh/storm.yml.j2
index ee48f60e72..d8fe3ddaa0 100644
--- a/ansible/roles/monasca/templates/monasca-thresh/storm.yml.j2
+++ b/ansible/roles/monasca/templates/monasca-thresh/storm.yml.j2
@@ -5,5 +5,5 @@ nimbus.seeds: [{{ monasca_storm_nimbus_servers }}]
 storm.zookeeper.port: {{ zookeeper_client_port }}
 storm.zookeeper.servers:
 {% for host in groups['zookeeper'] %}
-  - "{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}"
+  - "{{ 'api' | kolla_address(host) }}"
 {% endfor %}
diff --git a/ansible/roles/monasca/templates/monasca-thresh/thresh-config.yml.j2 b/ansible/roles/monasca/templates/monasca-thresh/thresh-config.yml.j2
index 2d70023fc5..cacad8f828 100644
--- a/ansible/roles/monasca/templates/monasca-thresh/thresh-config.yml.j2
+++ b/ansible/roles/monasca/templates/monasca-thresh/thresh-config.yml.j2
@@ -147,7 +147,7 @@ sporadicMetricNamespaces:
 
 database:
   driverClass: org.drizzle.jdbc.DrizzleDriver
-  url: "jdbc:drizzle://{{ monasca_database_address }}:{{ monasca_database_port }}/{{ monasca_database_name }}"
+  url: "jdbc:drizzle://{{ monasca_database_address | put_address_in_context('url') }}:{{ monasca_database_port }}/{{ monasca_database_name }}"
   user: "{{ monasca_database_user }}"
   password: "{{ monasca_database_password }}"
   properties:
diff --git a/ansible/roles/mongodb/handlers/main.yml b/ansible/roles/mongodb/handlers/main.yml
index d25a4ca483..4a8d1421c5 100644
--- a/ansible/roles/mongodb/handlers/main.yml
+++ b/ansible/roles/mongodb/handlers/main.yml
@@ -20,11 +20,11 @@
     - Bootstrap cluster
 
 - name: Waiting for the mongodb startup
-  wait_for: host={{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }} port={{ mongodb_port }}
+  wait_for: host={{ 'api' | kolla_address }} port={{ mongodb_port }}
 
 - name: Checking current replication status
   become: true
-  command: "docker exec -t mongodb mongo --host {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }} --port {{ mongodb_port }} --quiet --eval rs.status().ok"
+  command: "docker exec -t mongodb mongo --host {{ 'api' | kolla_address }} --port {{ mongodb_port }} --quiet --eval rs.status().ok"
   register: mongodb_replication_status
   changed_when: false
   delegate_to: "{{ groups['mongodb'][0] }}"
diff --git a/ansible/roles/mongodb/templates/bootstrap_cluster.js.j2 b/ansible/roles/mongodb/templates/bootstrap_cluster.js.j2
index fcf6c51afb..d2bef661d3 100644
--- a/ansible/roles/mongodb/templates/bootstrap_cluster.js.j2
+++ b/ansible/roles/mongodb/templates/bootstrap_cluster.js.j2
@@ -6,7 +6,7 @@ printjson(rs.initiate(
       {% for host in groups["mongodb"] %}
       {
         "_id" : {{ loop.index }},
-        "host" : "{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ mongodb_port }}"
+        "host" : "{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ mongodb_port }}"
       }{% if not loop.last %},{% endif %}
       {% endfor %}
     ]
diff --git a/ansible/roles/murano/defaults/main.yml b/ansible/roles/murano/defaults/main.yml
index 11ad0d1ac4..81df8a3fcb 100644
--- a/ansible/roles/murano/defaults/main.yml
+++ b/ansible/roles/murano/defaults/main.yml
@@ -34,7 +34,7 @@ murano_services:
 ####################
 murano_database_name: "murano"
 murano_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}murano{% endif %}"
-murano_database_address: "{{ database_address }}:{{ database_port }}"
+murano_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -72,9 +72,9 @@ murano_engine_extra_volumes: "{{ murano_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-murano_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ murano_api_port }}"
-murano_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ murano_api_port }}"
-murano_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ murano_api_port }}"
+murano_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ murano_api_port }}"
+murano_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ murano_api_port }}"
+murano_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ murano_api_port }}"
 
 murano_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/murano/tasks/import_library_packages.yml b/ansible/roles/murano/tasks/import_library_packages.yml
index 13b5310043..9f653ded8f 100644
--- a/ansible/roles/murano/tasks/import_library_packages.yml
+++ b/ansible/roles/murano/tasks/import_library_packages.yml
@@ -20,7 +20,7 @@
     --os-password {{ keystone_admin_password }}
     --os-project-name {{ openstack_auth.project_name }}
     --os-auth-url {{ keystone_admin_url }}
-    --murano-url {{ admin_protocol }}://{{ api_interface_address }}:{{ murano_api_port }}
+    --murano-url {{ admin_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ murano_api_port }}
     package-list
   register: status
   changed_when: False
@@ -35,7 +35,7 @@
     --os-password {{ keystone_admin_password }}
     --os-project-name {{ openstack_auth.project_name }}
     --os-auth-url {{ keystone_admin_url }}
-    --murano-url {{ admin_protocol }}://{{ api_interface_address }}:{{ murano_api_port }}
+    --murano-url {{ admin_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ murano_api_port }}
     package-import --exists-action u --is-public /io.murano.zip
   run_once: True
   delegate_to: "{{ groups['murano-api'][0] }}"
@@ -50,7 +50,7 @@
     --os-password {{ keystone_admin_password }}
     --os-project-name {{ openstack_auth.project_name }}
     --os-auth-url {{ keystone_admin_url }}
-    --murano-url {{ admin_protocol }}://{{ api_interface_address }}:{{ murano_api_port }}
+    --murano-url {{ admin_protocol }}://{{ api_interface_address | put_address_in_context('url') }}:{{ murano_api_port }}
     package-import --exists-action u --is-public /io.murano.applications.zip
   run_once: True
   delegate_to: "{{ groups['murano-api'][0] }}"
diff --git a/ansible/roles/murano/templates/murano.conf.j2 b/ansible/roles/murano/templates/murano.conf.j2
index 356f4daefc..5f4e3654ff 100644
--- a/ansible/roles/murano/templates/murano.conf.j2
+++ b/ansible/roles/murano/templates/murano.conf.j2
@@ -30,7 +30,7 @@ password = {{ murano_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [murano_auth]
 auth_uri = {{ keystone_internal_url }}/v3
@@ -43,7 +43,7 @@ username = {{ murano_keystone_user }}
 password = {{ murano_keystone_password }}
 
 [murano]
-url = {{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ murano_api_port }}
+url = {{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ murano_api_port }}
 api_workers = {{ openstack_service_workers }}
 
 [oslo_messaging_notifications]
diff --git a/ansible/roles/neutron/defaults/main.yml b/ansible/roles/neutron/defaults/main.yml
index 512da44a41..c0b5557b34 100644
--- a/ansible/roles/neutron/defaults/main.yml
+++ b/ansible/roles/neutron/defaults/main.yml
@@ -160,7 +160,7 @@ neutron_services:
 ####################
 neutron_database_name: "neutron"
 neutron_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}neutron{% endif %}"
-neutron_database_address: "{{ database_address }}:{{ database_port }}"
+neutron_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -322,9 +322,9 @@ ironic_neutron_agent_extra_volumes: "{{ neutron_extra_volumes }}"
 dhcp_agents_per_network: 2
 max_l3_agents_per_router: 3
 
-neutron_admin_endpoint: "{{ admin_protocol }}://{{ neutron_internal_fqdn }}:{{ neutron_server_port }}"
-neutron_internal_endpoint: "{{ internal_protocol }}://{{ neutron_internal_fqdn }}:{{ neutron_server_port }}"
-neutron_public_endpoint: "{{ public_protocol }}://{{ neutron_external_fqdn }}:{{ neutron_server_port }}"
+neutron_admin_endpoint: "{{ admin_protocol }}://{{ neutron_internal_fqdn | put_address_in_context('url') }}:{{ neutron_server_port }}"
+neutron_internal_endpoint: "{{ internal_protocol }}://{{ neutron_internal_fqdn | put_address_in_context('url') }}:{{ neutron_server_port }}"
+neutron_public_endpoint: "{{ public_protocol }}://{{ neutron_external_fqdn | put_address_in_context('url') }}:{{ neutron_server_port }}"
 
 neutron_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/neutron/templates/ml2_conf.ini.j2 b/ansible/roles/neutron/templates/ml2_conf.ini.j2
index e81a2e34ad..d9a3436ef5 100644
--- a/ansible/roles/neutron/templates/ml2_conf.ini.j2
+++ b/ansible/roles/neutron/templates/ml2_conf.ini.j2
@@ -4,6 +4,10 @@
 type_drivers = {{ neutron_type_drivers }}
 tenant_network_types = {{ neutron_tenant_network_types }}
 
+{% if tunnel_address_family == 'ipv6' %}
+overlay_ip_version = 6
+{% endif %}
+
 {% if neutron_plugin_agent == "openvswitch" %}
 {% if enable_hyperv | bool %}
 mechanism_drivers = openvswitch,hyperv
@@ -27,9 +31,9 @@ extension_drivers = port_security
 {% if enable_opendaylight | bool %}
 [ml2_odl]
 {% if enable_haproxy | bool %}
-url = {{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ opendaylight_haproxy_restconf_port }}/controller/nb/v2/neutron
+url = {{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ opendaylight_haproxy_restconf_port }}/controller/nb/v2/neutron
 {% else %}
-url = {{ internal_protocol }}://{{ kolla_internal_vip_address }}:{{ opendaylight_restconf_port }}/controller/nb/v2/neutron
+url = {{ internal_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ opendaylight_restconf_port }}/controller/nb/v2/neutron
 {% endif %}
 username = admin
 password = {{ opendaylight_password }}
@@ -59,7 +63,6 @@ flat_networks = {% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop
 
 [ml2_type_vxlan]
 vni_ranges = 1:1000
-vxlan_group = 239.1.1.1
 
 [securitygroup]
 {% if neutron_plugin_agent == "openvswitch" or neutron_plugin_agent == "opendaylight" %}
diff --git a/ansible/roles/neutron/templates/neutron.conf.j2 b/ansible/roles/neutron/templates/neutron.conf.j2
index 79f0a911d7..c13054c217 100644
--- a/ansible/roles/neutron/templates/neutron.conf.j2
+++ b/ansible/roles/neutron/templates/neutron.conf.j2
@@ -112,7 +112,7 @@ password = {{ neutron_keystone_password }}
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
 
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
@@ -133,12 +133,12 @@ drivers = ovs
 
 {% if enable_octavia | bool %}
 [octavia]
-base_url = {{ internal_protocol }}://{{ octavia_internal_fqdn }}:{{ octavia_api_port }}
+base_url = {{ internal_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}
 {% endif %}
 
 {% if enable_designate | bool %}
 [designate]
-url = {{ internal_protocol }}://{{ designate_internal_fqdn }}:{{ designate_api_port }}/v2
+url = {{ internal_protocol }}://{{ designate_internal_fqdn | put_address_in_context('url') }}:{{ designate_api_port }}/v2
 auth_uri = {{ keystone_internal_url }}
 auth_url = {{ keystone_admin_url }}
 auth_type = password
diff --git a/ansible/roles/nova-hyperv/templates/nova_hyperv.conf.j2 b/ansible/roles/nova-hyperv/templates/nova_hyperv.conf.j2
index 52cc6774c6..7ff452799e 100644
--- a/ansible/roles/nova-hyperv/templates/nova_hyperv.conf.j2
+++ b/ansible/roles/nova-hyperv/templates/nova_hyperv.conf.j2
@@ -31,7 +31,7 @@ user_domain_name = {{ default_user_domain_name }}
 os_region_name = {{ openstack_region_name }}
 
 [glance]
-api_servers = {{ internal_protocol }}://{{ glance_internal_fqdn }}:{{ glance_api_port }}
+api_servers = {{ internal_protocol }}://{{ glance_internal_fqdn | put_address_in_context('url') }}:{{ glance_api_port }}
 
 
 [hyperv]
@@ -45,10 +45,10 @@ enable_instance_metrics_collection = false
 
 [rdp]
 enabled = true
-html5_proxy_base_url = {{ public_protocol }}://{{ kolla_internal_vip_address }}:{{ rdp_port }}
+html5_proxy_base_url = {{ public_protocol }}://{{ kolla_internal_vip_address | put_address_in_context('url') }}:{{ rdp_port }}
 
 [neutron]
-url = {{ internal_protocol }}://{{ neutron_internal_fqdn }}:{{ neutron_server_port }}
+url = {{ internal_protocol }}://{{ neutron_internal_fqdn | put_address_in_context('url') }}:{{ neutron_server_port }}
 auth_strategy = keystone
 project_domain_name = default
 project_name = service
diff --git a/ansible/roles/nova/defaults/main.yml b/ansible/roles/nova/defaults/main.yml
index 71cf51614c..b39eef1014 100644
--- a/ansible/roles/nova/defaults/main.yml
+++ b/ansible/roles/nova/defaults/main.yml
@@ -190,11 +190,11 @@ ceph_client_nova_keyring_caps:
 ####################
 nova_database_name: "nova"
 nova_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}nova{% endif %}"
-nova_database_address: "{{ database_address }}:{{ database_port }}"
+nova_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 nova_api_database_name: "nova_api"
 nova_api_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}nova_api{% endif %}"
-nova_api_database_address: "{{ database_address }}:{{ database_port }}"
+nova_api_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
@@ -345,13 +345,13 @@ haproxy_nova_serialconsole_proxy_tunnel_timeout: "10m"
 # OpenStack
 ####################
 
-nova_legacy_admin_endpoint: "{{ admin_protocol }}://{{ nova_internal_fqdn }}:{{ nova_api_port }}/v2/%(tenant_id)s"
-nova_legacy_internal_endpoint: "{{ internal_protocol }}://{{ nova_internal_fqdn }}:{{ nova_api_port }}/v2/%(tenant_id)s"
-nova_legacy_public_endpoint: "{{ public_protocol }}://{{ nova_external_fqdn }}:{{ nova_api_port }}/v2/%(tenant_id)s"
+nova_legacy_admin_endpoint: "{{ admin_protocol }}://{{ nova_internal_fqdn | put_address_in_context('url') }}:{{ nova_api_port }}/v2/%(tenant_id)s"
+nova_legacy_internal_endpoint: "{{ internal_protocol }}://{{ nova_internal_fqdn | put_address_in_context('url') }}:{{ nova_api_port }}/v2/%(tenant_id)s"
+nova_legacy_public_endpoint: "{{ public_protocol }}://{{ nova_external_fqdn | put_address_in_context('url') }}:{{ nova_api_port }}/v2/%(tenant_id)s"
 
-nova_admin_endpoint: "{{ admin_protocol }}://{{ nova_internal_fqdn }}:{{ nova_api_port }}/v2.1"
-nova_internal_endpoint: "{{ internal_protocol }}://{{ nova_internal_fqdn }}:{{ nova_api_port }}/v2.1"
-nova_public_endpoint: "{{ public_protocol }}://{{ nova_external_fqdn }}:{{ nova_api_port }}/v2.1"
+nova_admin_endpoint: "{{ admin_protocol }}://{{ nova_internal_fqdn | put_address_in_context('url') }}:{{ nova_api_port }}/v2.1"
+nova_internal_endpoint: "{{ internal_protocol }}://{{ nova_internal_fqdn | put_address_in_context('url') }}:{{ nova_api_port }}/v2.1"
+nova_public_endpoint: "{{ public_protocol }}://{{ nova_external_fqdn | put_address_in_context('url') }}:{{ nova_api_port }}/v2.1"
 
 nova_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/nova/templates/nova.conf.d/libvirt.conf.j2 b/ansible/roles/nova/templates/nova.conf.d/libvirt.conf.j2
index 04749e44bf..1f75a00ba0 100644
--- a/ansible/roles/nova/templates/nova.conf.d/libvirt.conf.j2
+++ b/ansible/roles/nova/templates/nova.conf.d/libvirt.conf.j2
@@ -3,7 +3,7 @@
 connection_uri = "qemu+tls://{{ migration_hostname }}/system"
 live_migration_uri = "qemu+tls://%s/system"
 {% else %}
-connection_uri = "qemu+tcp://{{ migration_interface_address }}/system"
+connection_uri = "qemu+tcp://{{ migration_interface_address | put_address_in_context('url') }}/system"
 {% endif %}
 {% if enable_ceph | bool and nova_backend == "rbd" %}
 images_type = rbd
diff --git a/ansible/roles/nova/templates/nova.conf.j2 b/ansible/roles/nova/templates/nova.conf.j2
index 2613710c30..895e0dd27e 100644
--- a/ansible/roles/nova/templates/nova.conf.j2
+++ b/ansible/roles/nova/templates/nova.conf.j2
@@ -72,7 +72,7 @@ novncproxy_port = {{ nova_novncproxy_listen_port }}
 server_listen = {{ api_interface_address }}
 server_proxyclient_address = {{ api_interface_address }}
 {% if inventory_hostname in groups['compute'] %}
-novncproxy_base_url = {{ public_protocol }}://{{ nova_novncproxy_fqdn }}:{{ nova_novncproxy_port }}/vnc_auto.html
+novncproxy_base_url = {{ public_protocol }}://{{ nova_novncproxy_fqdn | put_address_in_context('url') }}:{{ nova_novncproxy_port }}/vnc_auto.html
 {% endif %}
 {% endif %}
 {% elif nova_console == 'spice' %}
@@ -84,7 +84,7 @@ enabled = true
 server_listen = {{ api_interface_address }}
 server_proxyclient_address = {{ api_interface_address }}
 {% if inventory_hostname in groups['compute'] %}
-html5proxy_base_url = {{ public_protocol }}://{{ nova_spicehtml5proxy_fqdn }}:{{ nova_spicehtml5proxy_port }}/spice_auto.html
+html5proxy_base_url = {{ public_protocol }}://{{ nova_spicehtml5proxy_fqdn | put_address_in_context('url') }}:{{ nova_spicehtml5proxy_port }}/spice_auto.html
 {% endif %}
 html5proxy_host = {{ api_interface_address }}
 html5proxy_port = {{ nova_spicehtml5proxy_listen_port }}
@@ -97,7 +97,7 @@ enabled = false
 {% if enable_nova_serialconsole_proxy | bool %}
 [serial_console]
 enabled = true
-base_url = {{ nova_serialproxy_protocol }}://{{ nova_serialproxy_fqdn }}:{{ nova_serialproxy_port }}/
+base_url = {{ nova_serialproxy_protocol }}://{{ nova_serialproxy_fqdn | put_address_in_context('url') }}:{{ nova_serialproxy_port }}/
 serialproxy_host = {{ api_interface_address }}
 serialproxy_port = {{ nova_serialproxy_listen_port }}
 proxyclient_address = {{ api_interface_address }}
@@ -112,7 +112,7 @@ auth_type = password
 project_name = service
 user_domain_name = {{ default_user_domain_name }}
 project_domain_name = {{ default_project_domain_name }}
-endpoint_override = {{ internal_protocol }}://{{ ironic_internal_fqdn }}:{{ ironic_api_port }}/v1
+endpoint_override = {{ internal_protocol }}://{{ ironic_internal_fqdn | put_address_in_context('url') }}:{{ ironic_api_port }}/v1
 {% endif %}
 
 [oslo_middleware]
@@ -122,7 +122,7 @@ enable_proxy_headers_parsing = True
 lock_path = /var/lib/nova/tmp
 
 [glance]
-api_servers = {{ internal_protocol }}://{{ glance_internal_fqdn }}:{{ glance_api_port }}
+api_servers = {{ internal_protocol }}://{{ glance_internal_fqdn | put_address_in_context('url') }}:{{ glance_api_port }}
 
 num_retries = {{ groups['glance-api'] | length }}
 
@@ -163,7 +163,7 @@ max_retries = -1
 [cache]
 backend = oslo_cache.memcache_pool
 enabled = True
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [keystone_authtoken]
@@ -178,7 +178,7 @@ password = {{ nova_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 {% if service_name == 'nova-compute' %}
 {% if nova_compute_virt_type in ['kvm', 'qemu'] %}
diff --git a/ansible/roles/octavia/defaults/main.yml b/ansible/roles/octavia/defaults/main.yml
index deae689dbd..1a424c03d4 100644
--- a/ansible/roles/octavia/defaults/main.yml
+++ b/ansible/roles/octavia/defaults/main.yml
@@ -55,7 +55,7 @@ octavia_required_roles:
 ####################
 octavia_database_name: "octavia"
 octavia_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}octavia{% endif %}"
-octavia_database_address: "{{ database_address }}:{{ database_port }}"
+octavia_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -111,9 +111,9 @@ octavia_worker_extra_volumes: "{{ octavia_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-octavia_admin_endpoint: "{{ admin_protocol }}://{{ octavia_internal_fqdn }}:{{ octavia_api_port }}"
-octavia_internal_endpoint: "{{ internal_protocol }}://{{ octavia_internal_fqdn }}:{{ octavia_api_port }}"
-octavia_public_endpoint: "{{ public_protocol }}://{{ octavia_external_fqdn }}:{{ octavia_api_port }}"
+octavia_admin_endpoint: "{{ admin_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
+octavia_internal_endpoint: "{{ internal_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
+octavia_public_endpoint: "{{ public_protocol }}://{{ octavia_external_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
 
 octavia_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/octavia/templates/octavia.conf.j2 b/ansible/roles/octavia/templates/octavia.conf.j2
index b32a5fa159..0072e0527a 100644
--- a/ansible/roles/octavia/templates/octavia.conf.j2
+++ b/ansible/roles/octavia/templates/octavia.conf.j2
@@ -33,7 +33,7 @@ project_domain_name = {{ default_project_domain_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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [keystone_authtoken]
 www_authenticate_uri = {{ keystone_internal_url }}
@@ -47,13 +47,13 @@ password = {{ octavia_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [health_manager]
 bind_port = {{ octavia_health_manager_port }}
 bind_ip = {{ octavia_network_interface_address }}
 heartbeat_key = insecure
-controller_ip_port_list = {% for host in groups['octavia-health-manager'] %}{{ hostvars[host]['ansible_' + hostvars[host]['octavia_network_interface']]['ipv4']['address'] }}:{{ octavia_health_manager_port }}{% if not loop.last %},{% endif %}{% endfor %}
+controller_ip_port_list = {% for host in groups['octavia-health-manager'] %}{{ 'octavia_network' | kolla_address(host) | put_address_in_context('url') }}:{{ octavia_health_manager_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [controller_worker]
 amp_boot_network_list = {{ octavia_amp_boot_network_list }}
diff --git a/ansible/roles/opendaylight/defaults/main.yml b/ansible/roles/opendaylight/defaults/main.yml
index eb2fce8a49..4e9f0dab8a 100644
--- a/ansible/roles/opendaylight/defaults/main.yml
+++ b/ansible/roles/opendaylight/defaults/main.yml
@@ -37,8 +37,8 @@ opendaylight_services:
 ####################
 # HAProxy
 ####################
-api_haproxy_members: "{% for host in groups['opendaylight'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ opendaylight_restconf_port }} check inter 2000 rise 2 fall 5;{% endfor %}"
-backup_api_haproxy_members: "{% for host in groups['opendaylight'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ opendaylight_restconf_port_backup }} check inter 2000 rise 2 fall 5;{% endfor %}"
+api_haproxy_members: "{% for host in groups['opendaylight'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ 'api' | kolla_address(host) }}:{{ opendaylight_restconf_port }} check inter 2000 rise 2 fall 5;{% endfor %}"
+backup_api_haproxy_members: "{% for host in groups['opendaylight'] %}server {{ hostvars[host]['ansible_hostname'] }} {{ 'api' | kolla_address(host) }}:{{ opendaylight_restconf_port_backup }} check inter 2000 rise 2 fall 5;{% endfor %}"
 
 ####################
 # Docker
diff --git a/ansible/roles/opendaylight/tasks/precheck.yml b/ansible/roles/opendaylight/tasks/precheck.yml
index b7e7018a96..ed1e3863ed 100644
--- a/ansible/roles/opendaylight/tasks/precheck.yml
+++ b/ansible/roles/opendaylight/tasks/precheck.yml
@@ -8,7 +8,7 @@
 
 - name: Checking free port for opendaylight_clustering
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_clustering_port }}"
     connect_timeout: 1
     state: stopped
@@ -18,7 +18,7 @@
 
 - name: Checking free port for opendaylight_restconf
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_restconf_port }}"
     connect_timeout: 1
     state: stopped
@@ -28,7 +28,7 @@
 
 - name: Checking free port for opendaylight_restconf_backup
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_restconf_port_backup }}"
     connect_timeout: 1
     state: stopped
@@ -38,7 +38,7 @@
 
 - name: Checking free port for opendaylight_karaf_ssh
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_karaf_ssh_port }}"
     connect_timeout: 1
     state: stopped
@@ -48,7 +48,7 @@
 
 - name: Checking free port for opendaylight_openflow
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_openflow_port }}"
     connect_timeout: 1
     state: stopped
@@ -58,7 +58,7 @@
 
 - name: Checking free port for opendaylight_ovsdb
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_ovsdb_port }}"
     connect_timeout: 1
     state: stopped
@@ -68,7 +68,7 @@
 
 - name: Checking free port for opendaylight_jetty_conf_port
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_jetty_conf_port }}"
     connect_timeout: 1
     state: stopped
@@ -78,7 +78,7 @@
 
 - name: Checking free port for opendaylight_jetty_conf2_port
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_jetty_conf2_port }}"
     connect_timeout: 1
     state: stopped
@@ -88,7 +88,7 @@
 
 - name: Checking free port for opendaylight_tomcat_port
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_tomcat_port }}"
     connect_timeout: 1
     state: stopped
@@ -98,7 +98,7 @@
 
 - name: Checking free port for opendaylight_tomcat_redirect_port
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ opendaylight_tomcat_redirect_port }}"
     connect_timeout: 1
     state: stopped
diff --git a/ansible/roles/opendaylight/templates/10-rest-connector.xml.j2 b/ansible/roles/opendaylight/templates/10-rest-connector.xml.j2
index 427125f7d2..51cf7bb0c4 100644
--- a/ansible/roles/opendaylight/templates/10-rest-connector.xml.j2
+++ b/ansible/roles/opendaylight/templates/10-rest-connector.xml.j2
@@ -14,7 +14,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
         <module>
           <type xmlns:rest="urn:opendaylight:params:xml:ns:yang:controller:md:sal:rest:connector">rest:rest-connector-impl</type>
           <name>rest-connector-default-impl</name>
-          <websocket-address>{{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }}</websocket-address>
+          <websocket-address>{{ 'api' | kolla_address }}</websocket-address>
           <websocket-port>{{ opendaylight_websocket_port }}</websocket-port>
           <dom-broker>
             <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
diff --git a/ansible/roles/opendaylight/templates/akka.conf.j2 b/ansible/roles/opendaylight/templates/akka.conf.j2
index 22d63e566f..61e1953368 100644
--- a/ansible/roles/opendaylight/templates/akka.conf.j2
+++ b/ansible/roles/opendaylight/templates/akka.conf.j2
@@ -4,17 +4,17 @@ odl-cluster-data {
     remote {
       artery {
           enabled = off
-          canonical.hostname = "{{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }}"
+          canonical.hostname = "{{ 'api' | kolla_address }}"
           canonical.port = {{ opendaylight_clustering_port }}
         }
       netty.tcp {
-        hostname = "{{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }}"
+        hostname = "{{ 'api' | kolla_address }}"
         port = {{ opendaylight_clustering_port }}
       }
     }
 
     cluster {
-      seed-nodes = [{% for host in groups['opendaylight'] %}"akka.tcp://opendaylight-cluster-data@{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ opendaylight_clustering_port }}"{% if not loop.last %},{% endif %}{% endfor %}]
+      seed-nodes = [{% for host in groups['opendaylight'] %}"akka.tcp://opendaylight-cluster-data@{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ opendaylight_clustering_port }}"{% if not loop.last %},{% endif %}{% endfor %}]
 
       roles = [
         "{{ hostvars[inventory_hostname]['ansible_hostname'] }}"
diff --git a/ansible/roles/openvswitch/templates/start-ovs.j2 b/ansible/roles/openvswitch/templates/start-ovs.j2
index fe5f34f28d..d1b5faa022 100644
--- a/ansible/roles/openvswitch/templates/start-ovs.j2
+++ b/ansible/roles/openvswitch/templates/start-ovs.j2
@@ -1,8 +1,8 @@
 #!/usr/bin/env bash
 {% if enable_opendaylight | bool %}
-/usr/bin/ovs-vsctl --no-wait -- set-manager {% for host in groups['opendaylight'] %}tcp:{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ opendaylight_ovsdb_port }} {% endfor %}
+/usr/bin/ovs-vsctl --no-wait -- set-manager {% for host in groups['opendaylight'] %}tcp:{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ opendaylight_ovsdb_port }} {% endfor %}
 
-/usr/bin/ovs-vsctl --no-wait -- set Open_vSwitch . other_config:local_ip={{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['tunnel_interface']]['ipv4']['address'] }}
+/usr/bin/ovs-vsctl --no-wait -- set Open_vSwitch . other_config:local_ip={{ 'tunnel' | kolla_address }}
 /usr/bin/ovs-vsctl --no-wait -- set Open_vSwitch . other_config:provider_mappings=physnet1:{{ neutron_bridge_name }}
 /usr/bin/ovs-vsctl --no-wait -- set Open_vSwitch . external_ids:system-id=`cat /proc/sys/kernel/random/uuid`
 /usr/bin/ovs-vsctl --no-wait -- set Open_vSwitch . external_ids:odl_os_hostconfig_config_odl_l2='{"supported_vnic_types": [{"vnic_type": "normal", "vif_type": "ovs", "vif_details": {} }], "allowed_network_types": [{{ opendaylight_allowed_network_types }}], "datapath_types": ["netdev", "system"], "bridge_mappings": {"physnet1":"{{ neutron_bridge_name }}"} }'
diff --git a/ansible/roles/ovs-dpdk/defaults/main.yml b/ansible/roles/ovs-dpdk/defaults/main.yml
index f71ed81a27..66be60bce0 100644
--- a/ansible/roles/ovs-dpdk/defaults/main.yml
+++ b/ansible/roles/ovs-dpdk/defaults/main.yml
@@ -40,7 +40,7 @@ ovsdpdk_services:
 ovs_bridge_mappings: "{% for bridge in neutron_bridge_name.split(',') %}physnet{{ loop.index0 + 1 }}:{{ bridge }}{% if not loop.last %},{% endif %}{% endfor %}"
 ovs_port_mappings: "{% for bridge in neutron_bridge_name.split(',') %} {{ neutron_external_interface.split(',')[loop.index0] }}:{{ bridge }}{% if not loop.last %},{% endif %}{% endfor %}"
 dpdk_tunnel_interface: "{{neutron_external_interface}}"
-dpdk_tunnel_interface_address: "{{ hostvars[inventory_hostname]['ansible_' + dpdk_tunnel_interface]['ipv4']['address'] }}"
+dpdk_tunnel_interface_address: "{{ 'dpdk_tunnel' | kolla_address }}"
 tunnel_interface_network: "{{ hostvars[inventory_hostname]['ansible_' + dpdk_tunnel_interface]['ipv4']['network']}}/{{hostvars[inventory_hostname]['ansible_' + dpdk_tunnel_interface]['ipv4']['netmask']}}"
 tunnel_interface_cidr: "{{dpdk_tunnel_interface_address}}/{{ tunnel_interface_network | ipaddr('prefix') }}"
 ovs_cidr_mappings: "{% if neutron_bridge_name.split(',')|length != 1 %} {neutron_bridge_name.split(',')[0]}:{{ tunnel_interface_cidr }} {% else %} {{ neutron_bridge_name }}:{{ tunnel_interface_cidr }} {% endif %}"
diff --git a/ansible/roles/panko/defaults/main.yml b/ansible/roles/panko/defaults/main.yml
index 2716ce0b4e..7eb75dda3c 100644
--- a/ansible/roles/panko/defaults/main.yml
+++ b/ansible/roles/panko/defaults/main.yml
@@ -28,8 +28,8 @@ panko_services:
 panko_database_name: "panko"
 panko_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}panko{% endif %}"
 panko_database_port: "{{ mongodb_port if panko_database_type == 'mongodb' else database_port }}"
-panko_database_mongodb_address: "{% for host in groups['mongodb'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ panko_database_port }}{% if not loop.last %},{% endif %}{% endfor %}"
-panko_database_mysql_address: "{{ database_address }}:{{ database_port }}"
+panko_database_mongodb_address: "{% for host in groups['mongodb'] %}{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ panko_database_port }}{% if not loop.last %},{% endif %}{% endfor %}"
+panko_database_mysql_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
@@ -49,9 +49,9 @@ panko_api_extra_volumes: "{{ default_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-panko_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ panko_api_port }}"
-panko_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ panko_api_port }}"
-panko_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ panko_api_port }}"
+panko_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ panko_api_port }}"
+panko_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ panko_api_port }}"
+panko_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ panko_api_port }}"
 
 panko_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/panko/templates/panko.conf.j2 b/ansible/roles/panko/templates/panko.conf.j2
index 9717ad0dec..eb1a83ae23 100644
--- a/ansible/roles/panko/templates/panko.conf.j2
+++ b/ansible/roles/panko/templates/panko.conf.j2
@@ -28,7 +28,7 @@ auth_type = password
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 {% if panko_policy_file is defined %}
 [oslo_policy]
diff --git a/ansible/roles/panko/templates/wsgi-panko.conf.j2 b/ansible/roles/panko/templates/wsgi-panko.conf.j2
index 29214475ff..969073bbea 100644
--- a/ansible/roles/panko/templates/wsgi-panko.conf.j2
+++ b/ansible/roles/panko/templates/wsgi-panko.conf.j2
@@ -1,6 +1,6 @@
 {% set python_path = '/usr/lib/python2.7/site-packages' if panko_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
 {% set binary_path = '/usr/bin' if panko_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
-Listen {{ api_interface_address }}:{{ panko_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ panko_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/placement/defaults/main.yml b/ansible/roles/placement/defaults/main.yml
index 194661956c..e719db7a7c 100644
--- a/ansible/roles/placement/defaults/main.yml
+++ b/ansible/roles/placement/defaults/main.yml
@@ -28,7 +28,7 @@ placement_services:
 ####################
 placement_database_name: "placement"
 placement_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}placement{% endif %}"
-placement_database_address: "{{ database_address }}:{{ database_port }}"
+placement_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
@@ -53,9 +53,9 @@ placement_api_extra_volumes: "{{ default_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-placement_admin_endpoint: "{{ admin_protocol }}://{{ placement_internal_fqdn }}:{{ placement_api_port }}"
-placement_internal_endpoint: "{{ internal_protocol }}://{{ placement_internal_fqdn }}:{{ placement_api_port }}"
-placement_public_endpoint: "{{ public_protocol }}://{{ placement_external_fqdn }}:{{ placement_api_port }}"
+placement_admin_endpoint: "{{ admin_protocol }}://{{ placement_internal_fqdn | put_address_in_context('url') }}:{{ placement_api_port }}"
+placement_internal_endpoint: "{{ internal_protocol }}://{{ placement_internal_fqdn | put_address_in_context('url') }}:{{ placement_api_port }}"
+placement_public_endpoint: "{{ public_protocol }}://{{ placement_external_fqdn | put_address_in_context('url') }}:{{ placement_api_port }}"
 
 placement_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/placement/templates/placement-api-wsgi.conf.j2 b/ansible/roles/placement/templates/placement-api-wsgi.conf.j2
index 5199eb2999..882b7b8937 100644
--- a/ansible/roles/placement/templates/placement-api-wsgi.conf.j2
+++ b/ansible/roles/placement/templates/placement-api-wsgi.conf.j2
@@ -6,7 +6,7 @@
 {% endif %}
 {% set wsgi_directory = '/usr/bin' if placement_install_type == 'binary' else '/var/lib/kolla/venv/bin' %}
 
-Listen {{ api_interface_address }}:{{ placement_api_listen_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ placement_api_listen_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/placement/templates/placement.conf.j2 b/ansible/roles/placement/templates/placement.conf.j2
index 82f2575193..a3fa5fad5f 100644
--- a/ansible/roles/placement/templates/placement.conf.j2
+++ b/ansible/roles/placement/templates/placement.conf.j2
@@ -30,7 +30,7 @@ max_retries = -1
 [cache]
 backend = oslo_cache.memcache_pool
 enabled = True
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [keystone_authtoken]
@@ -45,7 +45,7 @@ password = {{ placement_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 {% if placement_policy_file is defined %}
 [oslo_policy]
diff --git a/ansible/roles/prechecks/tasks/port_checks.yml b/ansible/roles/prechecks/tasks/port_checks.yml
index a2a684d69f..8b6fc81926 100644
--- a/ansible/roles/prechecks/tasks/port_checks.yml
+++ b/ansible/roles/prechecks/tasks/port_checks.yml
@@ -7,10 +7,7 @@
   fail: "msg='Please check the api_interface settings - interface {{ api_interface }} is not active'"
   when: hostvars[inventory_hostname]['ansible_' + api_interface]['active'] != True
 
-- name: Checking the api_interface configuration
-  fail: "msg='Please check the api_interface settings - interface {{ api_interface }} configuration missing'"
-  when: hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4'] is not defined
-
+# kolla_address handles relevant address check
 - name: Checking the api_interface ip address configuration
   fail: "msg='Please check the api_interface settings - interface {{ api_interface }} ip address problem'"
   when: api_interface_address is not defined
diff --git a/ansible/roles/prometheus/defaults/main.yml b/ansible/roles/prometheus/defaults/main.yml
index ce11ce0cbe..c835ff05bb 100644
--- a/ansible/roles/prometheus/defaults/main.yml
+++ b/ansible/roles/prometheus/defaults/main.yml
@@ -108,7 +108,7 @@ prometheus_mysql_exporter_database_user: "{% if use_preconfigured_databases | bo
 # 'service_name:blackbox_exporter_module:endpoint' for example:
 #
 # prometheus_blackbox_exporter_targets:
-#   - 'glance:os_endpoint:http://{{ kolla_external_vip_address }}:{{ glance_api_port}}'
+#   - 'glance:os_endpoint:http://{{ kolla_external_vip_address | put_address_in_context('url') }}:{{ glance_api_port}}'
 #
 # For a list of modules see the alertmanager config.
 prometheus_blackbox_exporter_endpoints: []
diff --git a/ansible/roles/prometheus/tasks/precheck.yml b/ansible/roles/prometheus/tasks/precheck.yml
index 1406f013cd..2b6c6c2d77 100644
--- a/ansible/roles/prometheus/tasks/precheck.yml
+++ b/ansible/roles/prometheus/tasks/precheck.yml
@@ -17,7 +17,7 @@
 
 - name: Checking free port for Prometheus server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ prometheus_port }}"
     connect_timeout: 1
     timeout: 1
@@ -28,7 +28,7 @@
 
 - name: Checking free port for Prometheus node_exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ prometheus_node_exporter_port }}"
     connect_timeout: 1
     timeout: 1
@@ -40,7 +40,7 @@
 
 - name: Checking free port for Prometheus mysqld_exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ prometheus_mysqld_exporter_port }}"
     connect_timeout: 1
     timeout: 1
@@ -52,7 +52,7 @@
 
 - name: Checking free port for Prometheus haproxy_exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ prometheus_haproxy_exporter_port }}"
     connect_timeout: 1
     timeout: 1
@@ -64,7 +64,7 @@
 
 - name: Checking free port for Prometheus memcached_exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ prometheus_memcached_exporter_port }}"
     connect_timeout: 1
     timeout: 1
@@ -76,7 +76,7 @@
 
 - name: Checking free port for Prometheus cAdvisor
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ prometheus_cadvisor_port }}"
     connect_timeout: 1
     timeout: 1
@@ -88,7 +88,7 @@
 
 - name: Checking free ports for Prometheus Alertmanager
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ item }}"
     connect_timeout: 1
     timeout: 1
@@ -103,7 +103,7 @@
 
 - name: Checking free ports for Prometheus openstack-exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ item }}"
     connect_timeout: 1
     timeout: 1
@@ -117,7 +117,7 @@
 
 - name: Checking free ports for Prometheus elasticsearch-exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ item }}"
     connect_timeout: 1
     timeout: 1
@@ -131,7 +131,7 @@
 
 - name: Checking free ports for Prometheus blackbox-exporter
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ item }}"
     connect_timeout: 1
     timeout: 1
diff --git a/ansible/roles/prometheus/templates/clouds.yml.j2 b/ansible/roles/prometheus/templates/clouds.yml.j2
index b7ba2849c9..105178ba58 100644
--- a/ansible/roles/prometheus/templates/clouds.yml.j2
+++ b/ansible/roles/prometheus/templates/clouds.yml.j2
@@ -9,4 +9,4 @@ clouds:
      project_name: {{ keystone_admin_project }}
      project_domain_name: 'Default'
      user_domain_name: 'Default'
-     auth_url: {{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ keystone_admin_port }}/v3
+     auth_url: {{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ keystone_admin_port }}/v3
diff --git a/ansible/roles/prometheus/templates/prometheus-alertmanager.json.j2 b/ansible/roles/prometheus/templates/prometheus-alertmanager.json.j2
index 7a02da25ea..d10aa8f0cb 100644
--- a/ansible/roles/prometheus/templates/prometheus-alertmanager.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-alertmanager.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/prometheus_alertmanager/alertmanager --config.file=/etc/prometheus/alertmanager.yml --web.listen-address={{ api_interface_address }}:{{ prometheus_alertmanager_port }} --web.external-url={{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ prometheus_alertmanager_port }} {% if groups["prometheus-alertmanager"] | length > 1 %} --cluster.listen-address={{ api_interface_address }}:{{ prometheus_alertmanager_cluster_port }} {% for host in groups["prometheus-alertmanager"] %} --cluster.peer={{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_alertmanager_cluster_port'] }}{% endfor %}{% endif %} --storage.path /var/lib/prometheus",
+    "command": "/opt/prometheus_alertmanager/alertmanager --config.file=/etc/prometheus/alertmanager.yml --web.listen-address={{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_alertmanager_port }} --web.external-url={{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ prometheus_alertmanager_port }} {% if groups["prometheus-alertmanager"] | length > 1 %} --cluster.listen-address={{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_alertmanager_cluster_port }} {% for host in groups["prometheus-alertmanager"] %} --cluster.peer={{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_alertmanager_cluster_port'] }}{% endfor %}{% endif %} --storage.path /var/lib/prometheus",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/prometheus-alertmanager.yml",
diff --git a/ansible/roles/prometheus/templates/prometheus-alertmanager.yml.j2 b/ansible/roles/prometheus/templates/prometheus-alertmanager.yml.j2
index ce4f6a6593..e479ec327c 100644
--- a/ansible/roles/prometheus/templates/prometheus-alertmanager.yml.j2
+++ b/ansible/roles/prometheus/templates/prometheus-alertmanager.yml.j2
@@ -11,7 +11,7 @@ receivers:
 {% if enable_vitrage | bool and enable_vitrage_prometheus_datasource | bool %}
     webhook_configs:
       - send_resolved: true
-        url: '{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ vitrage_api_port }}/v1/event'
+        url: '{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ vitrage_api_port }}/v1/event'
         http_config:
           basic_auth:
             username: '{{ keystone_admin_user }}'
diff --git a/ansible/roles/prometheus/templates/prometheus-blackbox-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-blackbox-exporter.json.j2
index 0ea688fac7..252d3bf104 100644
--- a/ansible/roles/prometheus/templates/prometheus-blackbox-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-blackbox-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/blackbox_exporter/blackbox_exporter --config.file=/etc/prometheus/blackbox.yml --web.listen-address={{ api_interface_address }}:{{ prometheus_blackbox_exporter_port }} --log.level=info",
+    "command": "/opt/blackbox_exporter/blackbox_exporter --config.file=/etc/prometheus/blackbox.yml --web.listen-address={{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_blackbox_exporter_port }} --log.level=info",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/prometheus-blackbox-exporter.yml",
diff --git a/ansible/roles/prometheus/templates/prometheus-elasticsearch-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-elasticsearch-exporter.json.j2
index 4aea2129ea..06573a2034 100644
--- a/ansible/roles/prometheus/templates/prometheus-elasticsearch-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-elasticsearch-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/elasticsearch_exporter/elasticsearch_exporter -es.uri http://{{ api_interface_address }}:{{ elasticsearch_port }} -web.listen-address {{ api_interface_address }}:{{ prometheus_elasticsearch_exporter_port }}",
+    "command": "/opt/elasticsearch_exporter/elasticsearch_exporter -es.uri http://{{ api_interface_address | put_address_in_context('url') }}:{{ elasticsearch_port }} -web.listen-address {{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_elasticsearch_exporter_port }}",
     "config_files": [],
     "permissions": [
         {
diff --git a/ansible/roles/prometheus/templates/prometheus-haproxy-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-haproxy-exporter.json.j2
index c4b03497bb..564dd6fc51 100644
--- a/ansible/roles/prometheus/templates/prometheus-haproxy-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-haproxy-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/haproxy_exporter/haproxy_exporter --haproxy.scrape-uri unix:/var/lib/kolla/haproxy/haproxy.sock --web.listen-address {{ api_interface_address }}:{{ prometheus_haproxy_exporter_port }}",
+    "command": "/opt/haproxy_exporter/haproxy_exporter --haproxy.scrape-uri unix:/var/lib/kolla/haproxy/haproxy.sock --web.listen-address {{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_haproxy_exporter_port }}",
     "config_files": [],
     "permissions": [
         {
diff --git a/ansible/roles/prometheus/templates/prometheus-memcached-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-memcached-exporter.json.j2
index 3a1c2bbaf9..5de9435308 100644
--- a/ansible/roles/prometheus/templates/prometheus-memcached-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-memcached-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/memcached_exporter/memcached_exporter --web.listen-address {{ api_interface_address }}:{{ prometheus_memcached_exporter_port }} --memcached.address {{ api_interface_address }}:{{ memcached_port }}",
+    "command": "/opt/memcached_exporter/memcached_exporter --web.listen-address {{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_memcached_exporter_port }} --memcached.address {{ api_interface_address | put_address_in_context('url') }}:{{ memcached_port }}",
     "config_files": [],
     "permissions": [
         {
diff --git a/ansible/roles/prometheus/templates/prometheus-mysqld-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-mysqld-exporter.json.j2
index 0d4863bcf9..91cc2addeb 100644
--- a/ansible/roles/prometheus/templates/prometheus-mysqld-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-mysqld-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/mysqld_exporter/mysqld_exporter --config.my-cnf /etc/prometheus/my.cnf --web.listen-address {{ api_interface_address }}:{{ prometheus_mysqld_exporter_port }}",
+    "command": "/opt/mysqld_exporter/mysqld_exporter --config.my-cnf /etc/prometheus/my.cnf --web.listen-address {{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_mysqld_exporter_port }}",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/my.cnf",
diff --git a/ansible/roles/prometheus/templates/prometheus-node-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-node-exporter.json.j2
index 22758ad986..74f8964ac8 100644
--- a/ansible/roles/prometheus/templates/prometheus-node-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-node-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/node_exporter/node_exporter --path.procfs /host/proc --path.sysfs /host/sys --web.listen-address {{ api_interface_address }}:{{ prometheus_node_exporter_port }}",
+    "command": "/opt/node_exporter/node_exporter --path.procfs /host/proc --path.sysfs /host/sys --web.listen-address {{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_node_exporter_port }}",
     "config_files": [],
     "permissions": [
         {
diff --git a/ansible/roles/prometheus/templates/prometheus-openstack-exporter.json.j2 b/ansible/roles/prometheus/templates/prometheus-openstack-exporter.json.j2
index 5b620f779d..7f1c2a4972 100644
--- a/ansible/roles/prometheus/templates/prometheus-openstack-exporter.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-openstack-exporter.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/openstack-exporter/openstack-exporter --os-client-config=/etc/openstack/clouds.yml --web.listen-address={{ api_interface_address }}:{{ prometheus_openstack_exporter_port }} default",
+    "command": "/opt/openstack-exporter/openstack-exporter --os-client-config=/etc/openstack/clouds.yml --web.listen-address={{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_openstack_exporter_port }} default",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/clouds.yml",
diff --git a/ansible/roles/prometheus/templates/prometheus-server.json.j2 b/ansible/roles/prometheus/templates/prometheus-server.json.j2
index 3849cf9611..1db33fe438 100644
--- a/ansible/roles/prometheus/templates/prometheus-server.json.j2
+++ b/ansible/roles/prometheus/templates/prometheus-server.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "/opt/prometheus/prometheus -config.file /etc/prometheus/prometheus.yml -web.listen-address {{ api_interface_address }}:{{ prometheus_port }} -web.external-url={{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ prometheus_port }} -log.format logger:stdout -storage.local.path /var/lib/prometheus{% if prometheus_cmdline_extras %} {{ prometheus_cmdline_extras }}{% endif %}",
+    "command": "/opt/prometheus/prometheus -config.file /etc/prometheus/prometheus.yml -web.listen-address {{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_port }} -web.external-url={{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ prometheus_port }} -log.format logger:stdout -storage.local.path /var/lib/prometheus{% if prometheus_cmdline_extras %} {{ prometheus_cmdline_extras }}{% endif %}",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/prometheus.yml",
diff --git a/ansible/roles/prometheus/templates/prometheus.yml.j2 b/ansible/roles/prometheus/templates/prometheus.yml.j2
index e72ab31a13..f74eadb2bc 100644
--- a/ansible/roles/prometheus/templates/prometheus.yml.j2
+++ b/ansible/roles/prometheus/templates/prometheus.yml.j2
@@ -17,7 +17,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups['prometheus'] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ prometheus_port }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ prometheus_port }}'
 {% endfor %}
 
 {% if enable_prometheus_node_exporter | bool %}
@@ -25,7 +25,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups['prometheus-node-exporter'] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_node_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_node_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -34,7 +34,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups['prometheus-mysqld-exporter'] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_mysqld_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_mysqld_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -43,7 +43,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups['prometheus-haproxy-exporter'] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_haproxy_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_haproxy_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -52,7 +52,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups['prometheus-memcached-exporter'] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_memcached_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_memcached_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -61,7 +61,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups["prometheus-cadvisor"] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_cadvisor_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_cadvisor_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -71,7 +71,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups["ceph-mgr"] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_ceph_mgr_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_ceph_mgr_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -82,7 +82,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups["prometheus-openstack-exporter"] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_openstack_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_openstack_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -92,7 +92,7 @@ scrape_configs:
     static_configs:
       - targets:
 {% for host in groups["prometheus-elasticsearch-exporter"] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_elasticsearch_exporter_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_elasticsearch_exporter_port'] }}'
 {% endfor %}
 {% endif %}
 
@@ -123,7 +123,7 @@ scrape_configs:
       - source_labels: [__param_target]
         target_label: instance
       - target_label: __address__
-        replacement: '{{ api_interface_address }}:{{ prometheus_blackbox_exporter_port }}'
+        replacement: '{{ api_interface_address | put_address_in_context('url') }}:{{ prometheus_blackbox_exporter_port }}'
 {% endif %}
 
 {% if enable_prometheus_alertmanager | bool %}
@@ -132,6 +132,6 @@ alerting:
   - static_configs:
     - targets:
 {% for host in groups["prometheus-alertmanager"] %}
-        - '{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ hostvars[host]['prometheus_alertmanager_port'] }}'
+        - '{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ hostvars[host]['prometheus_alertmanager_port'] }}'
 {% endfor %}
 {% endif %}
diff --git a/ansible/roles/qdrouterd/tasks/precheck.yml b/ansible/roles/qdrouterd/tasks/precheck.yml
index 18fdb1935c..2faf15c16a 100644
--- a/ansible/roles/qdrouterd/tasks/precheck.yml
+++ b/ansible/roles/qdrouterd/tasks/precheck.yml
@@ -17,8 +17,20 @@
     - container_facts['qdrouterd'] is not defined
     - inventory_hostname in groups['qdrouterd']
 
+- name: Set NSS database for IPv4
+  set_fact:
+    nss_database: 'ahostsv4'
+  when:
+    - api_address_family == 'ipv4'
+
+- name: Set NSS database for IPv6
+  set_fact:
+    nss_database: 'ahostsv6'
+  when:
+    - api_address_family == 'ipv6'
+
 - name: Check if all qdrouterd hostnames are resolvable
-  command: "getent ahostsv4 {{ hostvars[item]['ansible_hostname'] }}"
+  command: "getent {{ nss_database }} {{ hostvars[item]['ansible_hostname'] }}"
   changed_when: false
   register: qdrouterd_hostnames
   with_items: "{{ groups['qdrouterd'] }}"
@@ -26,4 +38,4 @@
 - fail: msg="Hostname has to resolve to IP address of api_interface"
   with_items: "{{ qdrouterd_hostnames.results }}"
   when:
-    - "item.stdout.find(hostvars[item['item']]['ansible_' ~ hostvars[item['item']]['api_interface']]['ipv4']['address']) == -1"
+    - "item.stdout.find('api' | kolla_address(item['item'])) == -1"
diff --git a/ansible/roles/qinling/defaults/main.yml b/ansible/roles/qinling/defaults/main.yml
index bf626b6d0d..e1a0313536 100644
--- a/ansible/roles/qinling/defaults/main.yml
+++ b/ansible/roles/qinling/defaults/main.yml
@@ -34,7 +34,7 @@ qinling_services:
 ####################
 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 }}"
+qinling_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -73,9 +73,9 @@ qinling_engine_extra_volumes: "{{ qinling_extra_volumes }}"
 ####################
 # 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_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ qinling_api_port }}"
+qinling_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ qinling_api_port }}"
+qinling_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ qinling_api_port }}"
 
 qinling_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/qinling/templates/qinling.conf.j2 b/ansible/roles/qinling/templates/qinling.conf.j2
index 230030812b..03a1ee5f5e 100644
--- a/ansible/roles/qinling/templates/qinling.conf.j2
+++ b/ansible/roles/qinling/templates/qinling.conf.j2
@@ -31,7 +31,7 @@ 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [storage]
 file_system_dir = /var/lib/qinling/package
diff --git a/ansible/roles/qinling/templates/wsgi-qinling.conf.j2 b/ansible/roles/qinling/templates/wsgi-qinling.conf.j2
index b642d0e033..955e27f712 100644
--- a/ansible/roles/qinling/templates/wsgi-qinling.conf.j2
+++ b/ansible/roles/qinling/templates/wsgi-qinling.conf.j2
@@ -4,7 +4,7 @@
     {% set python_path = '/usr/lib/python2.7/site-packages' if qinling_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
 {% endif %}
 
-Listen {{ api_interface_address }}:{{ qinling_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ qinling_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/rabbitmq/tasks/config.yml b/ansible/roles/rabbitmq/tasks/config.yml
index 05e315eec0..b4193c1189 100644
--- a/ansible/roles/rabbitmq/tasks/config.yml
+++ b/ansible/roles/rabbitmq/tasks/config.yml
@@ -65,6 +65,25 @@
     - Restart rabbitmq container (first node)
     - Restart rabbitmq container (rest of nodes)
 
+- name: Copying over erl_inetrc
+  become: true
+  vars:
+    service: "{{ rabbitmq_services['rabbitmq'] }}"
+  template:
+    src: "{{ item }}"
+    dest: "{{ node_config_directory }}/{{ project_name }}/erl_inetrc"
+    mode: "0660"
+  with_first_found:
+    - "{{ node_custom_config }}/rabbitmq/{{ inventory_hostname }}/erl_inetrc"
+    - "{{ node_custom_config }}/rabbitmq/erl_inetrc"
+    - "erl_inetrc.j2"
+  when:
+    - inventory_hostname in groups[service.group]
+    - service.enabled | bool
+  notify:
+    - Restart rabbitmq container (first node)
+    - Restart rabbitmq container (rest of nodes)
+
 - name: Copying over definitions.json
   become: true
   vars:
diff --git a/ansible/roles/rabbitmq/tasks/precheck.yml b/ansible/roles/rabbitmq/tasks/precheck.yml
index 1f9e4853c7..7afa8f687b 100644
--- a/ansible/roles/rabbitmq/tasks/precheck.yml
+++ b/ansible/roles/rabbitmq/tasks/precheck.yml
@@ -51,8 +51,20 @@
     - container_facts['rabbitmq'] is not defined
     - inventory_hostname in groups['rabbitmq']
 
+- name: Set NSS database for IPv4
+  set_fact:
+    nss_database: 'ahostsv4'
+  when:
+    - api_address_family == 'ipv4'
+
+- name: Set NSS database for IPv6
+  set_fact:
+    nss_database: 'ahostsv6'
+  when:
+    - api_address_family == 'ipv6'
+
 - name: Check if all rabbit hostnames are resolvable
-  command: "getent ahostsv4 {{ hostvars[item]['ansible_hostname'] }}"
+  command: "getent {{ nss_database }} {{ hostvars[item]['ansible_hostname'] }}"
   changed_when: false
   register: rabbitmq_hostnames
   with_items: "{{ groups['rabbitmq'] }}"
@@ -60,7 +72,7 @@
 - fail: msg="Hostname has to resolve to IP address of api_interface"
   with_items: "{{ rabbitmq_hostnames.results }}"
   when:
-    - "item.stdout.find(hostvars[item['item']]['ansible_' ~ hostvars[item['item']]['api_interface']]['ipv4']['address']) == -1"
+    - "item.stdout.find('api' | kolla_address(item['item'])) == -1"
 
 - name: Checking free port for outward RabbitMQ
   wait_for:
@@ -107,7 +119,7 @@
     - container_facts['outward_rabbitmq'] is not defined
 
 - name: Check if all outward rabbit hostnames are resolvable
-  command: "getent ahostsv4 {{ hostvars[item]['ansible_hostname'] }}"
+  command: "getent {{ nss_database }} {{ hostvars[item]['ansible_hostname'] }}"
   changed_when: false
   register: outward_rabbitmq_hostnames
   with_items: "{{ groups['outward-rabbitmq'] }}"
@@ -118,4 +130,4 @@
   with_items: "{{ outward_rabbitmq_hostnames.results }}"
   when:
     - enable_outward_rabbitmq | bool
-    - "item.stdout.find(hostvars[item['item']]['ansible_' ~ hostvars[item['item']]['api_interface']]['ipv4']['address']) == -1"
+    - "item.stdout.find('api' | kolla_address(item['item'])) == -1"
diff --git a/ansible/roles/rabbitmq/templates/erl_inetrc.j2 b/ansible/roles/rabbitmq/templates/erl_inetrc.j2
new file mode 100644
index 0000000000..81755d8a3f
--- /dev/null
+++ b/ansible/roles/rabbitmq/templates/erl_inetrc.j2
@@ -0,0 +1,3 @@
+{% if api_address_family == 'ipv6' %}
+{inet6,true}.
+{% endif %}
diff --git a/ansible/roles/rabbitmq/templates/rabbitmq-env.conf.j2 b/ansible/roles/rabbitmq/templates/rabbitmq-env.conf.j2
index 3d750fdd99..8d6069b507 100644
--- a/ansible/roles/rabbitmq/templates/rabbitmq-env.conf.j2
+++ b/ansible/roles/rabbitmq/templates/rabbitmq-env.conf.j2
@@ -2,6 +2,8 @@ RABBITMQ_NODENAME=rabbit@{{ ansible_hostname }}
 RABBITMQ_LOG_BASE=/var/log/kolla/{{ project_name }}
 RABBITMQ_DIST_PORT={{ role_rabbitmq_cluster_port }}
 RABBITMQ_PID_FILE={{ rabbitmq_pid_file }}
+RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-kernel inetrc '/etc/rabbitmq/erl_inetrc' {% if api_address_family == 'ipv6' %}-proto_dist inet6_tcp {% endif %}"
+RABBITMQ_CTL_ERL_ARGS="{% if api_address_family == 'ipv6' %}-proto_dist inet6_tcp {% endif %}"
 
 export ERL_EPMD_ADDRESS={{ api_interface_address }}
 export ERL_EPMD_PORT={{ role_rabbitmq_epmd_port }}
diff --git a/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2 b/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2
index e95b7cccab..e54ec346c5 100644
--- a/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2
+++ b/ansible/roles/rabbitmq/templates/rabbitmq.conf.j2
@@ -1,3 +1,5 @@
+# NOTE(yoctozepto): rabbitmq uses the raw format (e.g. fd::) of IPv6 address;
+# despite specifying port via colon, the url format (e.g. [fd::]) is not accepted
 listeners.tcp.1 = {{ api_interface_address }}:{{ role_rabbitmq_port }}
 {% if rabbitmq_hipe_compile|bool %}
 hipe_compile = true
diff --git a/ansible/roles/rabbitmq/templates/rabbitmq.json.j2 b/ansible/roles/rabbitmq/templates/rabbitmq.json.j2
index 46f59f1088..91e67e15b4 100644
--- a/ansible/roles/rabbitmq/templates/rabbitmq.json.j2
+++ b/ansible/roles/rabbitmq/templates/rabbitmq.json.j2
@@ -13,6 +13,12 @@
             "owner": "rabbitmq",
             "perm": "0600"
         },
+        {
+            "source": "{{ container_config_directory }}/erl_inetrc",
+            "dest": "/etc/rabbitmq/erl_inetrc",
+            "owner": "rabbitmq",
+            "perm": "0600"
+        },
         {
             "source": "{{ container_config_directory }}/definitions.json",
             "dest": "/etc/rabbitmq/definitions.json",
diff --git a/ansible/roles/rally/defaults/main.yml b/ansible/roles/rally/defaults/main.yml
index ae510548a9..da93f0699b 100644
--- a/ansible/roles/rally/defaults/main.yml
+++ b/ansible/roles/rally/defaults/main.yml
@@ -32,4 +32,4 @@ rally_extra_volumes: "{{ default_extra_volumes }}"
 ####################
 rally_database_name: "rally"
 rally_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}rally{% endif %}"
-rally_database_address: "{{ database_address }}:{{ database_port }}"
+rally_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
diff --git a/ansible/roles/redis/templates/redis-sentinel.conf.j2 b/ansible/roles/redis/templates/redis-sentinel.conf.j2
index efe719b107..34ef13a3dd 100644
--- a/ansible/roles/redis/templates/redis-sentinel.conf.j2
+++ b/ansible/roles/redis/templates/redis-sentinel.conf.j2
@@ -1,4 +1,4 @@
-{% set redis_master_address = hostvars[groups['redis'][0]]['ansible_' + hostvars[groups['redis'][0]]['api_interface']]['ipv4']['address'] %}
+{% set redis_master_address = 'api' | kolla_address(groups['redis'][0]) %}
 daemonize no
 pidfile "/var/run/redis/redis-sentinel.pid"
 logfile "/var/log/kolla/redis/redis-sentinel.log"
diff --git a/ansible/roles/redis/templates/redis.conf.j2 b/ansible/roles/redis/templates/redis.conf.j2
index e96f359d59..fb0fb2b5d1 100644
--- a/ansible/roles/redis/templates/redis.conf.j2
+++ b/ansible/roles/redis/templates/redis.conf.j2
@@ -50,6 +50,6 @@ requirepass {{ redis_master_password }}
 masterauth {{ redis_master_password }}
 
 {% if inventory_hostname != groups['redis'][0] %}
-{% set redis_master_address = hostvars[groups['redis'][0]]['ansible_' + hostvars[groups['redis'][0]]['api_interface']]['ipv4']['address'] %}
+{% set redis_master_address = 'api' | kolla_address(groups['redis'][0]) %}
 slaveof {{ redis_master_address }} 6379
 {% endif %}
diff --git a/ansible/roles/sahara/defaults/main.yml b/ansible/roles/sahara/defaults/main.yml
index 89c094cc69..758e89a8e3 100644
--- a/ansible/roles/sahara/defaults/main.yml
+++ b/ansible/roles/sahara/defaults/main.yml
@@ -35,7 +35,7 @@ sahara_services:
 ####################
 sahara_database_name: "sahara"
 sahara_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}sahara{% endif %}"
-sahara_database_address: "{{ database_address }}:{{ database_port }}"
+sahara_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -76,9 +76,9 @@ sahara_engine_extra_volumes: "{{ sahara_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-sahara_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ sahara_api_port }}"
-sahara_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ sahara_api_port }}"
-sahara_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ sahara_api_port }}"
+sahara_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ sahara_api_port }}"
+sahara_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ sahara_api_port }}"
+sahara_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ sahara_api_port }}"
 
 sahara_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/sahara/templates/sahara.conf.j2 b/ansible/roles/sahara/templates/sahara.conf.j2
index b49b5c0c3e..c2b6036d53 100644
--- a/ansible/roles/sahara/templates/sahara.conf.j2
+++ b/ansible/roles/sahara/templates/sahara.conf.j2
@@ -24,7 +24,7 @@ password = {{ sahara_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/searchlight/defaults/main.yml b/ansible/roles/searchlight/defaults/main.yml
index cf5f62f994..0874a955e2 100644
--- a/ansible/roles/searchlight/defaults/main.yml
+++ b/ansible/roles/searchlight/defaults/main.yml
@@ -31,7 +31,7 @@ searchlight_services:
 ####################
 # Elasticsearch
 ####################
-searchlight_elasticsearch_url: "{{ kolla_internal_fqdn }}:{{ elasticsearch_port }}"
+searchlight_elasticsearch_url: "{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ elasticsearch_port }}"
 
 ####################
 # Docker
@@ -66,9 +66,9 @@ searchlight_listener_extra_volumes: "{{ searchlight_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-searchlight_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ searchlight_api_port }}"
-searchlight_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ searchlight_api_port }}"
-searchlight_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ searchlight_api_port }}"
+searchlight_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ searchlight_api_port }}"
+searchlight_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ searchlight_api_port }}"
+searchlight_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ searchlight_api_port }}"
 
 searchlight_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/searchlight/templates/searchlight.conf.j2 b/ansible/roles/searchlight/templates/searchlight.conf.j2
index fe3025952c..a8f11b766c 100644
--- a/ansible/roles/searchlight/templates/searchlight.conf.j2
+++ b/ansible/roles/searchlight/templates/searchlight.conf.j2
@@ -7,7 +7,7 @@ transport_url = {{ rpc_transport_url }}
 [api]
 port = {{ searchlight_api_port }}
 bind_host = {{ api_interface_address }}
-public_endpoint = {{ public_protocol }}://{{ kolla_external_fqdn }}:{{ searchlight_api_port }}
+public_endpoint = {{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ searchlight_api_port }}
 workers = {{ openstack_service_workers }}
 
 [elasticsearch]
@@ -32,7 +32,7 @@ auth_type = password
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
@@ -63,7 +63,7 @@ auth_plugin = password
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [resource_plugin:os_cinder_volume]
 enabled = {{ enable_cinder | bool }}
diff --git a/ansible/roles/senlin/defaults/main.yml b/ansible/roles/senlin/defaults/main.yml
index 2b6d972e2f..91bce201ae 100644
--- a/ansible/roles/senlin/defaults/main.yml
+++ b/ansible/roles/senlin/defaults/main.yml
@@ -35,7 +35,7 @@ senlin_services:
 ####################
 senlin_database_name: "senlin"
 senlin_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}senlin{% endif %}"
-senlin_database_address: "{{ database_address }}:{{ database_port }}"
+senlin_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -73,9 +73,9 @@ senlin_engine_extra_volumes: "{{ senlin_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-senlin_admin_endpoint: "{{ admin_protocol }}://{{ senlin_internal_fqdn }}:{{ senlin_api_port }}"
-senlin_internal_endpoint: "{{ internal_protocol }}://{{ senlin_internal_fqdn }}:{{ senlin_api_port }}"
-senlin_public_endpoint: "{{ public_protocol }}://{{ senlin_external_fqdn }}:{{ senlin_api_port }}"
+senlin_admin_endpoint: "{{ admin_protocol }}://{{ senlin_internal_fqdn | put_address_in_context('url') }}:{{ senlin_api_port }}"
+senlin_internal_endpoint: "{{ internal_protocol }}://{{ senlin_internal_fqdn | put_address_in_context('url') }}:{{ senlin_api_port }}"
+senlin_public_endpoint: "{{ public_protocol }}://{{ senlin_external_fqdn | put_address_in_context('url') }}:{{ senlin_api_port }}"
 
 senlin_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/senlin/templates/senlin.conf.j2 b/ansible/roles/senlin/templates/senlin.conf.j2
index fe33dcaa77..baa0a52eb9 100644
--- a/ansible/roles/senlin/templates/senlin.conf.j2
+++ b/ansible/roles/senlin/templates/senlin.conf.j2
@@ -41,7 +41,7 @@ service_token_roles_required = False
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/skydive/templates/skydive-agent.conf.j2 b/ansible/roles/skydive/templates/skydive-agent.conf.j2
index 8daa53549b..0867951108 100644
--- a/ansible/roles/skydive/templates/skydive-agent.conf.j2
+++ b/ansible/roles/skydive/templates/skydive-agent.conf.j2
@@ -15,21 +15,21 @@ etcd:
   servers:
 {% if enable_etcd | bool %}
 {% for host in groups['etcd'] %}
-    - http://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ etcd_client_port }}
+    - http://{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endfor %}
 {% else %}
 {% for host in groups['skydive-analyzer'] %}
-  - http://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ etcd_client_port }}
+  - http://{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endfor %}
 {% endif %}
 
 analyzers:
 {% for host in groups['skydive-analyzer'] %}
-  - {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ skydive_analyzer_port }}
+  - {{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ skydive_analyzer_port }}
 {% endfor %}
 
 agent:
-  listen: {{ hostvars[inventory_hostname]['ansible_' + hostvars[inventory_hostname]['api_interface']]['ipv4']['address'] }}:{{ skydive_agents_port }}
+  listen: {{ 'api' | kolla_address | put_address_in_context('url') }}:{{ skydive_agents_port }}
   flow:
     probes:
       - gopacket
diff --git a/ansible/roles/skydive/templates/skydive-agent.json.j2 b/ansible/roles/skydive/templates/skydive-agent.json.j2
index 1d4a0f4fea..2ba3bbd5ad 100644
--- a/ansible/roles/skydive/templates/skydive-agent.json.j2
+++ b/ansible/roles/skydive/templates/skydive-agent.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "skydive agent --conf /etc/skydive/skydive.conf --listen={{ api_interface_address }}:{{ skydive_agents_port }}",
+    "command": "skydive agent --conf /etc/skydive/skydive.conf --listen={{ api_interface_address | put_address_in_context('url') }}:{{ skydive_agents_port }}",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/skydive.conf",
diff --git a/ansible/roles/skydive/templates/skydive-analyzer.conf.j2 b/ansible/roles/skydive/templates/skydive-analyzer.conf.j2
index c19ab0ec2e..e128deb80a 100644
--- a/ansible/roles/skydive/templates/skydive-analyzer.conf.j2
+++ b/ansible/roles/skydive/templates/skydive-analyzer.conf.j2
@@ -19,7 +19,7 @@ logging:
 
 analyzers:
 {% for host in groups['skydive-analyzer'] %}
-  - {{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ skydive_analyzer_port }}
+  - {{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ skydive_analyzer_port }}
 {% endfor %}
 
 etcd:
@@ -28,19 +28,19 @@ etcd:
   embedded: false
   servers:
 {% for host in groups['etcd'] %}
-    - http://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ etcd_client_port }}
+    - http://{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endfor %}
 {% else %}
   embedded: true
   servers:
 {% for host in groups['skydive-analyzer'] %}
-  - http://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ etcd_client_port }}
+  - http://{{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endfor %}
-  listen: {{ api_interface_address }}:{{ etcd_client_port }}
+  listen: {{ api_interface_address | put_address_in_context('url') }}:{{ etcd_client_port }}
 {% endif %}
 
 analyzer:
-  listen: {{ api_interface_address }}:{{ skydive_analyzer_port }}
+  listen: {{ api_interface_address | put_address_in_context('url') }}:{{ skydive_analyzer_port }}
   storage:
     backend: elasticsearch
 {% if groups['skydive-agent'] | length > 1 %}
@@ -57,7 +57,7 @@ analyzer:
 
 storage:
   elasticsearch:
-    host: {{ elasticsearch_address }}:{{ elasticsearch_port }}
+    host: {{ elasticsearch_address | put_address_in_context('url') }}:{{ elasticsearch_port }}
     maxconns: 10
     retry: 60
 
diff --git a/ansible/roles/skydive/templates/skydive-analyzer.json.j2 b/ansible/roles/skydive/templates/skydive-analyzer.json.j2
index b773a272c9..ce8b525ec4 100644
--- a/ansible/roles/skydive/templates/skydive-analyzer.json.j2
+++ b/ansible/roles/skydive/templates/skydive-analyzer.json.j2
@@ -1,5 +1,5 @@
 {
-    "command": "skydive analyzer --conf /etc/skydive/skydive.conf --listen={{ api_interface_address }}:{{ skydive_analyzer_port }}",
+    "command": "skydive analyzer --conf /etc/skydive/skydive.conf --listen={{ api_interface_address | put_address_in_context('url') }}:{{ skydive_analyzer_port }}",
     "config_files": [
         {
             "source": "{{ container_config_directory }}/skydive.conf",
diff --git a/ansible/roles/solum/defaults/main.yml b/ansible/roles/solum/defaults/main.yml
index a74a82e135..57e9763bde 100644
--- a/ansible/roles/solum/defaults/main.yml
+++ b/ansible/roles/solum/defaults/main.yml
@@ -61,7 +61,7 @@ solum_services:
 ####################
 solum_database_name: "solum"
 solum_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}solum{% endif %}"
-solum_database_address: "{{ database_address }}:{{ database_port }}"
+solum_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -121,13 +121,13 @@ solum_conductor_extra_volumes: "{{ solum_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-solum_image_builder_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ solum_image_builder_port }}"
-solum_image_builder_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ solum_image_builder_port }}"
-solum_image_builder_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ solum_image_builder_port }}"
+solum_image_builder_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ solum_image_builder_port }}"
+solum_image_builder_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ solum_image_builder_port }}"
+solum_image_builder_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ solum_image_builder_port }}"
 
-solum_application_deployment_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ solum_application_deployment_port }}"
-solum_application_deployment_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ solum_application_deployment_port }}"
-solum_application_deployment_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ solum_application_deployment_port }}"
+solum_application_deployment_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ solum_application_deployment_port }}"
+solum_application_deployment_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ solum_application_deployment_port }}"
+solum_application_deployment_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ solum_application_deployment_port }}"
 
 solum_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/solum/templates/solum.conf.j2 b/ansible/roles/solum/templates/solum.conf.j2
index 33afdec09f..12083a76a8 100644
--- a/ansible/roles/solum/templates/solum.conf.j2
+++ b/ansible/roles/solum/templates/solum.conf.j2
@@ -54,7 +54,7 @@ password = {{ solum_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/storm/defaults/main.yml b/ansible/roles/storm/defaults/main.yml
index 80a2e65256..901e939b7c 100644
--- a/ansible/roles/storm/defaults/main.yml
+++ b/ansible/roles/storm/defaults/main.yml
@@ -27,7 +27,7 @@ storm_services:
 # Storm
 ####################
 storm_log_settings: 'INFO,ROLLINGFILE'
-storm_nimbus_servers: "{% for host in groups['storm-nimbus'] %}'{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}'{% if not loop.last %},{% endif %}{% endfor %}"
+storm_nimbus_servers: "{% for host in groups['storm-nimbus'] %}'{{ 'api' | kolla_address(host) }}'{% if not loop.last %},{% endif %}{% endfor %}"
 
 ####################
 # Docker
diff --git a/ansible/roles/storm/templates/storm.yml.j2 b/ansible/roles/storm/templates/storm.yml.j2
index 965dd11796..44158341bc 100644
--- a/ansible/roles/storm/templates/storm.yml.j2
+++ b/ansible/roles/storm/templates/storm.yml.j2
@@ -4,7 +4,7 @@ nimbus.seeds: [{{ storm_nimbus_servers }}]
 storm.zookeeper.port: {{ zookeeper_client_port }}
 storm.zookeeper.servers:
 {% for host in groups['zookeeper'] %}
-  - "{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}"
+  - "{{ 'api' | kolla_address(host) }}"
 {% endfor %}
 supervisor.slots.ports:
 {% for port in range(storm_worker_port_range.start|int, storm_worker_port_range.end|int + 1) %}
diff --git a/ansible/roles/swift/defaults/main.yml b/ansible/roles/swift/defaults/main.yml
index 1533670648..115f0da884 100644
--- a/ansible/roles/swift/defaults/main.yml
+++ b/ansible/roles/swift/defaults/main.yml
@@ -52,9 +52,9 @@ swift_log_level: "{{ 'DEBUG' if openstack_logging_debug | bool else 'INFO'}}"
 ####################
 # OpenStack
 ####################
-swift_admin_endpoint: "{{ admin_protocol }}://{{ swift_internal_fqdn }}:{{ swift_proxy_server_port }}/v1"
-swift_internal_endpoint: "{{ internal_protocol }}://{{ swift_internal_fqdn }}:{{ swift_proxy_server_port }}/v1/AUTH_%(tenant_id)s"
-swift_public_endpoint: "{{ public_protocol }}://{{ swift_external_fqdn }}:{{ swift_proxy_server_port }}/v1/AUTH_%(tenant_id)s"
+swift_admin_endpoint: "{{ admin_protocol }}://{{ swift_internal_fqdn | put_address_in_context('url') }}:{{ swift_proxy_server_port }}/v1"
+swift_internal_endpoint: "{{ internal_protocol }}://{{ swift_internal_fqdn | put_address_in_context('url') }}:{{ swift_proxy_server_port }}/v1/AUTH_%(tenant_id)s"
+swift_public_endpoint: "{{ public_protocol }}://{{ swift_external_fqdn | put_address_in_context('url') }}:{{ swift_proxy_server_port }}/v1/AUTH_%(tenant_id)s"
 
 swift_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/swift/tasks/precheck.yml b/ansible/roles/swift/tasks/precheck.yml
index 6b6767972e..aba043fffd 100644
--- a/ansible/roles/swift/tasks/precheck.yml
+++ b/ansible/roles/swift/tasks/precheck.yml
@@ -11,7 +11,7 @@
 
 - name: Checking free port for Swift Account Server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_storage_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_storage' | kolla_address }}"
     port: "{{ swift_account_server_port }}"
     connect_timeout: 1
     timeout: 1
@@ -22,7 +22,7 @@
 
 - name: Checking free port for Swift Container Server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_storage_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_storage' | kolla_address }}"
     port: "{{ swift_container_server_port }}"
     connect_timeout: 1
     timeout: 1
@@ -33,7 +33,7 @@
 
 - name: Checking free port for Swift Object Server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_storage_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_storage' | kolla_address }}"
     port: "{{ swift_object_server_port }}"
     connect_timeout: 1
     timeout: 1
@@ -44,7 +44,7 @@
 
 - name: Checking free port for Swift Account Replication Server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_replication_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_replication' | kolla_address }}"
     port: "{{ swift_account_server_port }}"
     connect_timeout: 1
     timeout: 1
@@ -55,7 +55,7 @@
 
 - name: Checking free port for Swift Container Replication Server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_replication_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_replication' | kolla_address }}"
     port: "{{ swift_container_server_port }}"
     connect_timeout: 1
     timeout: 1
@@ -66,7 +66,7 @@
 
 - name: Checking free port for Swift Object Replication Server
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_replication_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_replication' | kolla_address }}"
     port: "{{ swift_object_server_port }}"
     connect_timeout: 1
     timeout: 1
@@ -77,7 +77,7 @@
 
 - name: Checking free port for Rsync
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + swift_replication_interface]['ipv4']['address'] }}"
+    host: "{{ 'swift_replication' | kolla_address }}"
     port: "873"
     connect_timeout: 1
     timeout: 1
diff --git a/ansible/roles/swift/templates/account.conf.j2 b/ansible/roles/swift/templates/account.conf.j2
index 78f73bdbec..e546df988a 100644
--- a/ansible/roles/swift/templates/account.conf.j2
+++ b/ansible/roles/swift/templates/account.conf.j2
@@ -1,6 +1,6 @@
-{% set interface = swift_replication_interface if 'replicat' in service_name else swift_storage_interface %}
+{% set network = 'swift_replication' if 'replicat' in service_name else 'swift_storage' %}
 [DEFAULT]
-bind_ip = {{ hostvars[inventory_hostname]['ansible_' + interface]['ipv4']['address'] }}
+bind_ip = {{ network | kolla_address }}
 bind_port = {{ swift_account_server_port }}
 devices = {{ swift_devices_mount_point }}
 mount_check = false
diff --git a/ansible/roles/swift/templates/container.conf.j2 b/ansible/roles/swift/templates/container.conf.j2
index 62cf38aa1e..97ff93ed1a 100644
--- a/ansible/roles/swift/templates/container.conf.j2
+++ b/ansible/roles/swift/templates/container.conf.j2
@@ -1,6 +1,6 @@
-{% set interface = swift_replication_interface if 'replicat' in service_name else swift_storage_interface %}
+{% set network = 'swift_replication' if 'replicat' in service_name else 'swift_storage' %}
 [DEFAULT]
-bind_ip = {{ hostvars[inventory_hostname]['ansible_' + interface]['ipv4']['address'] }}
+bind_ip = {{ network | kolla_address }}
 bind_port = {{ swift_container_server_port }}
 devices = {{ swift_devices_mount_point }}
 mount_check = false
diff --git a/ansible/roles/swift/templates/object.conf.j2 b/ansible/roles/swift/templates/object.conf.j2
index 537c952db8..bc62f9a51e 100644
--- a/ansible/roles/swift/templates/object.conf.j2
+++ b/ansible/roles/swift/templates/object.conf.j2
@@ -1,6 +1,6 @@
-{% set interface = swift_replication_interface if 'replicat' in service_name else swift_storage_interface %}
+{% set network = 'swift_replication' if 'replicat' in service_name else 'swift_storage' %}
 [DEFAULT]
-bind_ip = {{ hostvars[inventory_hostname]['ansible_' + interface]['ipv4']['address'] }}
+bind_ip = {{ network | kolla_address }}
 bind_port = {{ swift_object_server_port }}
 devices = {{ swift_devices_mount_point }}
 mount_check = false
diff --git a/ansible/roles/swift/templates/proxy-server.conf.j2 b/ansible/roles/swift/templates/proxy-server.conf.j2
index 685a6935b8..958b4bf535 100644
--- a/ansible/roles/swift/templates/proxy-server.conf.j2
+++ b/ansible/roles/swift/templates/proxy-server.conf.j2
@@ -22,7 +22,7 @@ use = egg:swift#tempurl
 
 [filter:cache]
 use = egg:swift#memcache
-memcache_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcache_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [filter:catch_errors]
 use = egg:swift#catch_errors
@@ -47,7 +47,7 @@ delay_auth_decision = {{ swift_delay_auth_decision }}
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 
 [filter:keystoneauth]
diff --git a/ansible/roles/swift/templates/rsyncd.conf.j2 b/ansible/roles/swift/templates/rsyncd.conf.j2
index 3831415f57..d23291330d 100644
--- a/ansible/roles/swift/templates/rsyncd.conf.j2
+++ b/ansible/roles/swift/templates/rsyncd.conf.j2
@@ -1,4 +1,4 @@
-address = {{ hostvars[inventory_hostname]['ansible_' + swift_replication_interface]['ipv4']['address'] }}
+address = {{ 'swift_replication' | kolla_address }}
 
 {% if inventory_hostname in groups['swift-account-server'] %}
 [account]
diff --git a/ansible/roles/tacker/defaults/main.yml b/ansible/roles/tacker/defaults/main.yml
index 4341145f1e..0b0a2c5330 100644
--- a/ansible/roles/tacker/defaults/main.yml
+++ b/ansible/roles/tacker/defaults/main.yml
@@ -33,7 +33,7 @@ tacker_services:
 ####################
 tacker_database_name: "tacker"
 tacker_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}tacker{% endif %}"
-tacker_database_address: "{{ database_address }}:{{ database_port }}"
+tacker_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ########
 # Docker
@@ -70,9 +70,9 @@ tacker_conductor_extra_volumes: "{{ tacker_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-tacker_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ tacker_server_port }}"
-tacker_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ tacker_server_port }}"
-tacker_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ tacker_server_port }}"
+tacker_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ tacker_server_port }}"
+tacker_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ tacker_server_port }}"
+tacker_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ tacker_server_port }}"
 
 tacker_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/tacker/templates/tacker.conf.j2 b/ansible/roles/tacker/templates/tacker.conf.j2
index 733745d14c..46eb1cc4d6 100644
--- a/ansible/roles/tacker/templates/tacker.conf.j2
+++ b/ansible/roles/tacker/templates/tacker.conf.j2
@@ -41,7 +41,7 @@ password = {{ tacker_keystone_password }}
 
 memcache_security_strategy = ENCRYPT
 memcache_secret_key = {{ memcache_secret_key }}
-memcached_servers = {% for host in groups['memcached'] %}{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [alarm_auth]
 username = {{ tacker_keystone_user }}
diff --git a/ansible/roles/telegraf/templates/telegraf.conf.j2 b/ansible/roles/telegraf/templates/telegraf.conf.j2
index 76030cfb94..2dceae53e7 100644
--- a/ansible/roles/telegraf/templates/telegraf.conf.j2
+++ b/ansible/roles/telegraf/templates/telegraf.conf.j2
@@ -14,7 +14,7 @@
 {% if enable_influxdb | bool %}
 {% for host in groups['influxdb'] %}
 [[outputs.influxdb]]
-  urls = ["{{ influxdb_proto }}://{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address']}}:{{ influxdb_http_port }}"]
+  urls = ["{{ influxdb_proto }}://{{ 'api' | kolla_address(host) }}:{{ influxdb_http_port }}"]
   database = "telegraf" # required
   retention_policy = "autogen"
   write_consistency = "any"
@@ -27,7 +27,7 @@
   fielddrop = ["time_*"]
 {% if enable_collectd | bool and inventory_hostname in groups['collectd'] %}
 [[inputs.socket_listener]]
-  service_address = "udp://{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}:{{ collectd_udp_port }}"
+  service_address = "udp://{{ 'api' | kolla_address | put_address_in_context('url') }}:{{ collectd_udp_port }}"
   name_prefix = "collectd_"
   data_format = "collectd"
   collectd_typesdb = ["/usr/share/collectd/types.db"]
@@ -37,7 +37,7 @@
 [[inputs.diskio]]
 {% if inventory_hostname in groups['influxdb'] and enable_influxdb | bool %}
 [[inputs.influxdb]]
-  urls = ["{{ influxdb_proto }}://{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}:{{ influxdb_http_port }}/debug/vars"]
+  urls = ["{{ influxdb_proto }}://{{ 'api' | kolla_address | put_address_in_context('url') }}:{{ influxdb_http_port }}/debug/vars"]
 {% endif %}
 [[inputs.kernel]]
 [[inputs.mem]]
@@ -52,45 +52,45 @@
 {% endif %}
 {% if inventory_hostname in groups['haproxy'] and enable_haproxy | bool %}
 [[inputs.haproxy]]
-  servers = ["{{ haproxy_proto }}://{{ haproxy_user }}:{{ haproxy_password }}@{{ api_interface_address }}:{{ haproxy_stats_port }}"]
+  servers = ["{{ haproxy_proto }}://{{ haproxy_user }}:{{ haproxy_password }}@{{ api_interface_address | put_address_in_context('url') }}:{{ haproxy_stats_port }}"]
 {% endif %}
 {% if inventory_hostname in groups['memcached'] and enable_memcached | bool %}
 [[inputs.memcached]]
-  servers = ["{{ api_interface_address }}:{{ memcached_port }}"]
+  servers = ["{{ api_interface_address | put_address_in_context('url') }}:{{ memcached_port }}"]
 {% endif %}
 {% if inventory_hostname in groups['elasticsearch'] and enable_elasticsearch | bool %}
 [[inputs.elasticsearch]]
-  servers = ["{{ elasticsearch_proto }}://{{ api_interface_address }}:{{ elasticsearch_port }}"]
+  servers = ["{{ elasticsearch_proto }}://{{ api_interface_address | put_address_in_context('url') }}:{{ elasticsearch_port }}"]
   local = true
   cluster_health = true
 {% endif %}
 {% if inventory_hostname in groups['rabbitmq'] and enable_rabbitmq | bool %}
 [[inputs.rabbitmq]]
-  url = "{{ rabbitmq_proto }}://{{ api_interface_address }}:{{ rabbitmq_management_port }}"
+  url = "{{ rabbitmq_proto }}://{{ api_interface_address | put_address_in_context('url') }}:{{ rabbitmq_management_port }}"
   username = "{{ rabbitmq_user }}"
   password = "{{ rabbitmq_password }}"
 {% endif %}
 {% if inventory_hostname in groups['outward-rabbitmq'] and enable_outward_rabbitmq | bool %}
 [[inputs.rabbitmq]]
-  url = "{{ outward_rabbitmq_proto }}://{{ api_interface_address }}:{{ outward_rabbitmq_management_port }}"
+  url = "{{ outward_rabbitmq_proto }}://{{ api_interface_address | put_address_in_context('url') }}:{{ outward_rabbitmq_management_port }}"
   username = "{{ outward_rabbitmq_user }}"
   password = "{{ outward_rabbitmq_password }}"
 {% endif %}
 {% if inventory_hostname in groups['redis'] and enable_redis | bool %}
 [[inputs.redis]]
-  servers = ["tcp://:{{ redis_master_password }}@{{ api_interface_address }}:{{ redis_port }}"]
+  servers = ["tcp://:{{ redis_master_password }}@{{ api_interface_address | put_address_in_context('url') }}:{{ redis_port }}"]
 {% endif %}
 {% if inventory_hostname in groups['zookeeper'] and enable_zookeeper | bool %}
 [[inputs.zookeeper]]
-  servers = ["{{ api_interface_address }}:{{ zookeeper_port }}"]
+  servers = ["{{ api_interface_address | put_address_in_context('url') }}:{{ zookeeper_port }}"]
 {% endif %}
 {% if inventory_hostname in groups['kafka'] and enable_kafka | bool %}
 [[inputs.kafka_consumer]]
-  brokers = ["{{ api_interface_address }}:{{ kafka_port }}"]
+  brokers = ["{{ api_interface_address | put_address_in_context('url') }}:{{ kafka_port }}"]
 {% endif %}
 {% if inventory_hostname in groups['mariadb'] and (enable_mariadb or enable_external_mariadb_load_balancer) | bool %}
 [[inputs.mysql]]
-  servers = ["{{ database_user }}:{{ database_password }}@{{ mariadb_proto }}({{ api_interface_address }}:{{ database_port }})/"]
+  servers = ["{{ database_user }}:{{ database_password }}@{{ mariadb_proto }}({{ api_interface_address | put_address_in_context('url') }}:{{ database_port }})/"]
   perf_events_statements_digest_text_limit  = 120
   perf_events_statements_limit              = 250
   perf_events_statements_time_limit         = 86400
diff --git a/ansible/roles/trove/defaults/main.yml b/ansible/roles/trove/defaults/main.yml
index 4e001d9e4e..01e2b51ab5 100644
--- a/ansible/roles/trove/defaults/main.yml
+++ b/ansible/roles/trove/defaults/main.yml
@@ -41,7 +41,7 @@ trove_services:
 ####################
 trove_database_name: "trove"
 trove_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}trove{% endif %}"
-trove_database_address: "{{ database_address }}:{{ database_port }}"
+trove_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -93,9 +93,9 @@ trove_taskmanager_extra_volumes: "{{ trove_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-trove_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
-trove_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
-trove_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
+trove_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
+trove_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
+trove_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ trove_api_port }}/v1.0/%(tenant_id)s"
 
 trove_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/vitrage/defaults/main.yml b/ansible/roles/vitrage/defaults/main.yml
index 423ef1226b..48815f8fd4 100644
--- a/ansible/roles/vitrage/defaults/main.yml
+++ b/ansible/roles/vitrage/defaults/main.yml
@@ -47,7 +47,7 @@ vitrage_services:
 #####################
 vitrage_database_name: "vitrage"
 vitrage_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}vitrage{% endif %}"
-vitrage_database_address: "{{ database_address }}:{{ database_port }}"
+vitrage_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 ####################
 # Docker
@@ -107,9 +107,9 @@ vitrage_ml_extra_volumes: "{{ vitrage_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-vitrage_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ vitrage_api_port }}"
-vitrage_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ vitrage_api_port }}"
-vitrage_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ vitrage_api_port }}"
+vitrage_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ vitrage_api_port }}"
+vitrage_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ vitrage_api_port }}"
+vitrage_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ vitrage_api_port }}"
 
 vitrage_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/vitrage/tasks/precheck.yml b/ansible/roles/vitrage/tasks/precheck.yml
index 95e4566447..f1d508768c 100644
--- a/ansible/roles/vitrage/tasks/precheck.yml
+++ b/ansible/roles/vitrage/tasks/precheck.yml
@@ -8,7 +8,7 @@
 
 - name: Checking free port for vitrage API
   wait_for:
-    host: "{{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}"
+    host: "{{ 'api' | kolla_address }}"
     port: "{{ vitrage_api_port }}"
     connect_timeout: 1
     state: stopped
diff --git a/ansible/roles/vitrage/templates/vitrage.conf.j2 b/ansible/roles/vitrage/templates/vitrage.conf.j2
index d876f33950..260a986c54 100644
--- a/ansible/roles/vitrage/templates/vitrage.conf.j2
+++ b/ansible/roles/vitrage/templates/vitrage.conf.j2
@@ -42,7 +42,7 @@ service_token_roles_required = True
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [service_credentials]
 auth_url = {{ keystone_internal_url }}/v3
@@ -57,7 +57,7 @@ interface = internal
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [oslo_messaging_notifications]
 transport_url = {{ notify_transport_url }}
diff --git a/ansible/roles/vitrage/templates/wsgi-vitrage.conf.j2 b/ansible/roles/vitrage/templates/wsgi-vitrage.conf.j2
index 99eed8c311..238525f327 100644
--- a/ansible/roles/vitrage/templates/wsgi-vitrage.conf.j2
+++ b/ansible/roles/vitrage/templates/wsgi-vitrage.conf.j2
@@ -1,5 +1,5 @@
 {% set python_path = '/usr/lib/python2.7/site-packages' if vitrage_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
-Listen {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}:{{ vitrage_api_port }}
+Listen {{ 'api' | kolla_address | put_address_in_context('url') }}:{{ vitrage_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/watcher/defaults/main.yml b/ansible/roles/watcher/defaults/main.yml
index 9e4db9844e..368b4fd947 100644
--- a/ansible/roles/watcher/defaults/main.yml
+++ b/ansible/roles/watcher/defaults/main.yml
@@ -41,7 +41,7 @@ watcher_services:
 ####################
 watcher_database_name: "watcher"
 watcher_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}watcher{% endif %}"
-watcher_database_address: "{{ database_address }}:{{ database_port }}"
+watcher_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -90,9 +90,9 @@ watcher_engine_extra_volumes: "{{ watcher_extra_volumes }}"
 ####################
 # OpenStack
 ####################
-watcher_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ watcher_api_port }}"
-watcher_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ watcher_api_port }}"
-watcher_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ watcher_api_port }}"
+watcher_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ watcher_api_port }}"
+watcher_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ watcher_api_port }}"
+watcher_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ watcher_api_port }}"
 
 watcher_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/watcher/templates/watcher.conf.j2 b/ansible/roles/watcher/templates/watcher.conf.j2
index ec837cb0ab..a73cae7e87 100644
--- a/ansible/roles/watcher/templates/watcher.conf.j2
+++ b/ansible/roles/watcher/templates/watcher.conf.j2
@@ -29,7 +29,7 @@ service_token_roles_required = True
 
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 
 [watcher_clients_auth]
 auth_uri = {{ keystone_internal_url }}
diff --git a/ansible/roles/zookeeper/templates/zookeeper.cfg.j2 b/ansible/roles/zookeeper/templates/zookeeper.cfg.j2
index f7ab93d647..c0a8a52e50 100644
--- a/ansible/roles/zookeeper/templates/zookeeper.cfg.j2
+++ b/ansible/roles/zookeeper/templates/zookeeper.cfg.j2
@@ -4,5 +4,5 @@ syncLimit=5
 dataDir=/var/lib/zookeeper/data
 clientPort={{ zookeeper_client_port }}
 {% for host in groups['zookeeper'] %}
-server.{{ loop.index }}={{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}:{{ zookeeper_peer_port }}:{{ zookeeper_quorum_port }}
+server.{{ loop.index }}={{ 'api' | kolla_address(host) | put_address_in_context('url') }}:{{ zookeeper_peer_port }}:{{ zookeeper_quorum_port }}
 {% endfor %}
diff --git a/ansible/roles/zun/defaults/main.yml b/ansible/roles/zun/defaults/main.yml
index 3a4a62b6e5..cc2eba603f 100644
--- a/ansible/roles/zun/defaults/main.yml
+++ b/ansible/roles/zun/defaults/main.yml
@@ -52,7 +52,7 @@ zun_services:
 ####################
 zun_database_name: "zun"
 zun_database_user: "{% if use_preconfigured_databases | bool and use_common_mariadb_user | bool %}{{ database_user }}{% else %}zun{% endif %}"
-zun_database_address: "{{ database_address }}:{{ database_port }}"
+zun_database_address: "{{ database_address | put_address_in_context('url') }}:{{ database_port }}"
 
 
 ####################
@@ -104,9 +104,9 @@ zun_compute_extra_volumes: "{{ zun_extra_volumes }}"
 ####################
 ## OpenStack
 ####################
-zun_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn }}:{{ zun_api_port }}/v1/"
-zun_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn }}:{{ zun_api_port }}/v1/"
-zun_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn }}:{{ zun_api_port }}/v1/"
+zun_admin_endpoint: "{{ admin_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ zun_api_port }}/v1/"
+zun_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ zun_api_port }}/v1/"
+zun_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ zun_api_port }}/v1/"
 
 zun_logging_debug: "{{ openstack_logging_debug }}"
 
diff --git a/ansible/roles/zun/templates/wsgi-zun.conf.j2 b/ansible/roles/zun/templates/wsgi-zun.conf.j2
index 4b4010b8c9..7639a5dbf5 100644
--- a/ansible/roles/zun/templates/wsgi-zun.conf.j2
+++ b/ansible/roles/zun/templates/wsgi-zun.conf.j2
@@ -1,5 +1,5 @@
 {% set python_path = '/usr/lib/python2.7/site-packages' if zun_install_type == 'binary' else '/var/lib/kolla/venv/lib/python2.7/site-packages' %}
-Listen {{ api_interface_address }}:{{ zun_api_port }}
+Listen {{ api_interface_address | put_address_in_context('url') }}:{{ zun_api_port }}
 
 ServerSignature Off
 ServerTokens Prod
diff --git a/ansible/roles/zun/templates/zun.conf.j2 b/ansible/roles/zun/templates/zun.conf.j2
index d38b3c90af..e2de4277a4 100644
--- a/ansible/roles/zun/templates/zun.conf.j2
+++ b/ansible/roles/zun/templates/zun.conf.j2
@@ -42,7 +42,7 @@ region_name = {{ openstack_region_name }}
 {% if enable_memcached | bool %}
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 {% endif %}
 
 # NOTE(yoctozepto): despite what the docs say, both keystone_auth and
@@ -63,7 +63,7 @@ region_name = {{ openstack_region_name }}
 {% if enable_memcached | bool %}
 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 %}
+memcached_servers = {% for host in groups['memcached'] %}{{ 'api' | kolla_address(host) | put_address_in_context('memcache') }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
 {% endif %}
 
 [zun_client]
@@ -104,10 +104,10 @@ host_shared_with_nova = {{ inventory_hostname in groups['compute'] and enable_no
 [websocket_proxy]
 wsproxy_host = {{ api_interface_address }}
 wsproxy_port = {{ zun_wsproxy_port }}
-base_url = ws://{{ kolla_external_fqdn }}:{{ zun_wsproxy_port }}
+base_url = ws://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ zun_wsproxy_port }}
 
 [docker]
 docker_remote_api_version = 1.24
-api_url = tcp://{{ api_interface_address }}:2375
+api_url = tcp://{{ api_interface_address | put_address_in_context('url') }}:2375
 docker_remote_api_host = {{ api_interface_address }}
 docker_remote_api_port = 2375
diff --git a/doc/source/reference/compute/masakari-guide.rst b/doc/source/reference/compute/masakari-guide.rst
index 49e3a75e6a..bd85b6500b 100644
--- a/doc/source/reference/compute/masakari-guide.rst
+++ b/doc/source/reference/compute/masakari-guide.rst
@@ -31,4 +31,4 @@ The setting is overridable using custom config, put the content in
 .. code-block:: ini
 
    [libvirt]
-   connection_uri = "xen://{{ migration_interface_address }}/system"
+   connection_uri = "xen://{{ migration_interface_address | put_address_in_context('url') }}/system"
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index d8fa8feb0c..eadf3606d9 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -78,7 +78,7 @@
 ##############################
 # This interface is what all your api services will be bound to by default.
 # Additionally, all vxlan/tunnel and storage network traffic will go over this
-# interface by default. This interface must contain an IPv4 address.
+# interface by default. This interface must contain an IP address.
 # It is possible for hosts to have non-matching names of interfaces - these can
 # be set in an inventory file per host or per group or stored separately, see
 #     http://docs.ansible.com/ansible/intro_inventory.html
@@ -88,7 +88,7 @@
 #network_interface: "eth0"
 
 # These can be adjusted for even more customization. The default is the same as
-# the 'network_interface'. These interfaces must contain an IPv4 address.
+# the 'network_interface'. These interfaces must contain an IP address.
 #kolla_external_vip_interface: "{{ network_interface }}"
 #api_interface: "{{ network_interface }}"
 #storage_interface: "{{ network_interface }}"
@@ -99,6 +99,20 @@
 #dns_interface: "{{ network_interface }}"
 #octavia_network_interface: "{{ api_interface }}"
 
+# Configure the address family (AF) per network.
+# Valid options are [ ipv4, ipv6 ]
+#network_address_family: "ipv4"
+#api_address_family: "{{ network_address_family }}"
+#storage_address_family: "{{ network_address_family }}"
+#cluster_address_family: "{{ network_address_family }}"
+#swift_storage_address_family: "{{ storage_address_family }}"
+#swift_replication_address_family: "{{ swift_storage_address_family }}"
+#migration_address_family: "{{ network_address_family }}"
+#tunnel_address_family: "{{ network_address_family }}"
+#octavia_network_address_family: "{{ api_address_family }}"
+#bifrost_network_address_family: "{{ network_address_family }}"
+#dns_address_family: "{{ network_address_family }}"
+
 # This is the raw interface given to neutron as its external network port. Even
 # though an IP address can exist on this interface, it will be unusable in most
 # configurations. It is recommended this interface not be configured with any IP
diff --git a/kolla_ansible/kolla_address.py b/kolla_ansible/kolla_address.py
new file mode 100644
index 0000000000..c1082973e3
--- /dev/null
+++ b/kolla_ansible/kolla_address.py
@@ -0,0 +1,122 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 Radosław Piliszek (yoctozepto)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from jinja2.filters import contextfilter
+from jinja2.runtime import Undefined
+
+from kolla_ansible.exception import FilterError
+
+
+@contextfilter
+def kolla_address(context, network_name, hostname=None):
+    """returns IP address on the requested network
+
+    The output is affected by '<network_name>_*' variables:
+    '<network_name>_interface' sets the interface to obtain address for.
+    '<network_name>_address_family' controls the address family (ipv4/ipv6).
+
+    :param context: Jinja2 Context
+    :param network_name: string denoting the name of the network to get IP
+                         address for, e.g. 'api'
+    :param hostname: to override host which address is retrieved for
+    :returns: string with IP address
+    """
+
+    # NOTE(yoctozepto): watch out as Jinja2 'context' behaves not exactly like
+    # the python 'dict' (but mimics it most of the time)
+    # for example it returns a special object of type 'Undefined' instead of
+    # 'None' or value specified as default for 'get' method
+    # 'HostVars' shares this behavior
+
+    if hostname is None:
+        hostname = context.get('inventory_hostname')
+        if isinstance(hostname, Undefined):
+            raise FilterError("'inventory_hostname' variable is unavailable")
+
+    hostvars = context.get('hostvars')
+    if isinstance(hostvars, Undefined):
+        raise FilterError("'hostvars' variable is unavailable")
+
+    del context  # remove for sanity
+
+    host = hostvars.get(hostname)
+    if isinstance(host, Undefined):
+        raise FilterError("'{hostname}' not in 'hostvars'"
+                          .format(hostname=hostname))
+
+    del hostvars  # remove for sanity (no 'Undefined' beyond this point)
+
+    interface_name = host.get(network_name + '_interface')
+    if interface_name is None:
+        raise FilterError("Interface name undefined "
+                          "for network '{network_name}' "
+                          "(set '{network_name}_interface')"
+                          .format(network_name=network_name))
+
+    address_family = host.get(network_name + '_address_family')
+    if address_family is None:
+        raise FilterError("Address family undefined "
+                          "for network '{network_name}' "
+                          "(set '{network_name}_address_family')"
+                          .format(network_name=network_name))
+    address_family = address_family.lower()
+    if address_family not in ['ipv4', 'ipv6']:
+        raise FilterError("Unknown address family '{address_family}' "
+                          "for network '{network_name}'"
+                          .format(address_family=address_family,
+                                  network_name=network_name))
+
+    ansible_interface_name = interface_name.replace('-', '_')
+    interface = host.get('ansible_' + ansible_interface_name)
+    if interface is None:
+        raise FilterError("Interface '{interface_name}' "
+                          "not present "
+                          "on host '{hostname}'"
+                          .format(interface_name=interface_name,
+                                  hostname=hostname))
+
+    af_interface = interface.get(address_family)
+    if af_interface is None:
+        raise FilterError("Address family '{address_family}' undefined "
+                          "on interface '{interface_name}' "
+                          "for host: '{hostname}'"
+                          .format(address_family=address_family,
+                                  interface_name=interface_name,
+                                  hostname=hostname))
+
+    if address_family == 'ipv4':
+        address = af_interface.get('address')
+    elif address_family == 'ipv6':
+        # ipv6 has no concept of a secondary address
+        # prefix 128 is the default from keepalived
+        # it needs to be excluded here
+        global_ipv6_addresses = [x for x in af_interface if
+                                 x['scope'] == 'global' and
+                                 x['prefix'] != '128']
+        if global_ipv6_addresses:
+            address = global_ipv6_addresses[0]['address']
+        else:
+            address = None
+
+    if address is None:
+        raise FilterError("{address_family} address missing "
+                          "on interface '{interface_name}' "
+                          "for host '{hostname}'"
+                          .format(address_family=address_family,
+                                  interface_name=interface_name,
+                                  hostname=hostname))
+
+    return address
diff --git a/kolla_ansible/put_address_in_context.py b/kolla_ansible/put_address_in_context.py
new file mode 100644
index 0000000000..d176a5611b
--- /dev/null
+++ b/kolla_ansible/put_address_in_context.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 Radosław Piliszek (yoctozepto)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from kolla_ansible.exception import FilterError
+
+
+def put_address_in_context(address, context):
+    """puts address in context
+
+    :param address: the address to contextify
+    :param context: describes context in which the address appears,
+                    either 'url' or 'memcache',
+                    affects only IPv6 addresses format
+    :returns: string with address in proper context
+    """
+
+    if context not in ['url', 'memcache']:
+        raise FilterError("Unknown context '{context}'"
+                          .format(context=context))
+
+    if ':' not in address:
+        return address
+
+    # must be IPv6 raw address
+
+    if context == 'url':
+        return '[{address}]'.format(address=address)
+    elif context == 'memcache':
+        return 'inet6:[{address}]'.format(address=address)
+
+    return address
diff --git a/kolla_ansible/tests/unit/test_address_filters.py b/kolla_ansible/tests/unit/test_address_filters.py
new file mode 100644
index 0000000000..5f157ab9bc
--- /dev/null
+++ b/kolla_ansible/tests/unit/test_address_filters.py
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 Radosław Piliszek (yoctozepto)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+import jinja2
+
+from kolla_ansible.exception import FilterError
+from kolla_ansible.kolla_address import kolla_address
+from kolla_ansible.put_address_in_context import put_address_in_context
+
+
+class TestAddressContextFilter(unittest.TestCase):
+
+    def test_url_context(self):
+        context = 'url'
+        addr = '192.168.1.1'
+        self.assertEqual(put_address_in_context(addr, context),
+                         addr)
+        addr = 'www.example.com'
+        self.assertEqual(put_address_in_context(addr, context),
+                         addr)
+        addr = 'fd::'
+        self.assertEqual(put_address_in_context(addr, context),
+                         '[{}]'.format(addr))
+
+    def test_memcache_context(self):
+        context = 'memcache'
+        addr = '192.168.1.1'
+        self.assertEqual(put_address_in_context(addr, context),
+                         addr)
+        addr = 'www.example.com'
+        self.assertEqual(put_address_in_context(addr, context),
+                         addr)
+        addr = 'fd::'
+        self.assertEqual(put_address_in_context(addr, context),
+                         'inet6:[{}]'.format(addr))
+
+    def test_unknown_context(self):
+        self.assertRaises(FilterError, put_address_in_context, '', 'lol')
+
+
+class TestKollaAddressFilter(unittest.TestCase):
+
+    def setUp(self):
+        # Bandit complains about Jinja2 autoescaping without nosec.
+        self.env = jinja2.Environment()  # nosec
+
+    def _make_context(self, parent):
+        return self.env.context_class(
+            self.env, parent=parent, name='dummy', blocks={})
+
+    def test_missing_config(self):
+        context = self._make_context({
+            'inventory_hostname': 'primary',
+            'hostvars': {
+                'primary': {
+                }
+            }
+        })
+        self.assertRaises(FilterError, kolla_address, context, 'api')
+
+    def test_valid_ipv4_config(self):
+        addr = '192.0.2.1'
+        context = self._make_context({
+            'inventory_hostname': 'primary',
+            'hostvars': {
+                'primary': {
+                    'api_address_family': 'ipv4',
+                    'api_interface': 'fake-interface',
+                    'ansible_fake_interface': {
+                        'ipv4': {
+                            'address': addr,
+                        },
+                    },
+                },
+            },
+        })
+        self.assertEqual(addr, kolla_address(context, 'api'))
+
+    def test_valid_ipv6_config(self):
+        addr = 'fd::'
+        context = self._make_context({
+            'inventory_hostname': 'primary',
+            'hostvars': {
+                'primary': {
+                    'api_address_family': 'ipv6',
+                    'api_interface': 'fake-interface',
+                    'ansible_fake_interface': {
+                        'ipv6': [
+                            {
+                                'address': addr,
+                                'scope': 'global',
+                                'prefix': 64,
+                            },
+                        ],
+                    },
+                },
+            },
+        })
+        self.assertEqual(addr, kolla_address(context, 'api'))
+
+    def test_missing_ipv6_config(self):
+        context = self._make_context({
+            'inventory_hostname': 'primary',
+            'hostvars': {
+                'primary': {
+                    'api_address_family': 'ipv6',
+                    'api_interface': 'fake-interface',
+                    'ansible_fake_interface': {
+                    },
+                },
+            },
+        })
+        self.assertRaises(FilterError, kolla_address, context, 'api')
+
+    def test_valid_ipv6_config_many_addr(self):
+        addr = 'fd::'
+        context = self._make_context({
+            'inventory_hostname': 'primary',
+            'hostvars': {
+                'primary': {
+                    'api_address_family': 'ipv6',
+                    'api_interface': 'fake-interface',
+                    'ansible_fake_interface': {
+                        'ipv6': [
+                            {
+                                'address': addr,
+                                'scope': 'global',
+                                'prefix': 64,
+                            },
+                            {
+                                'address': addr+'1',
+                                'scope': 'link',
+                                'prefix': 64,
+                            },
+                            {
+                                'address': addr+'2',
+                                'scope': 'global',
+                                'prefix': 64,
+                            },
+                        ],
+                    },
+                },
+            },
+        })
+        self.assertEqual(addr, kolla_address(context, 'api'))
+
+    def test_invalid_ipv6_config_no_global(self):
+        addr = 'fd::'
+        context = self._make_context({
+            'inventory_hostname': 'primary',
+            'hostvars': {
+                'primary': {
+                    'api_address_family': 'ipv6',
+                    'api_interface': 'fake-interface',
+                    'ansible_fake_interface': {
+                        'ipv6': [
+                            {
+                                'address': addr,
+                                'scope': 'link',
+                                'prefix': 64,
+                            },
+                            {
+                                'address': addr+'1',
+                                'scope': 'link',
+                                'prefix': 64,
+                            },
+                        ],
+                    },
+                },
+            },
+        })
+        self.assertRaises(FilterError, kolla_address, context, 'api')
diff --git a/tests/get_logs.sh b/tests/get_logs.sh
index e07d2a06f5..b6e450db4c 100644
--- a/tests/get_logs.sh
+++ b/tests/get_logs.sh
@@ -10,7 +10,7 @@ copy_logs() {
     # Don't save the IPA images.
     rm ${LOG_DIR}/kolla_configs/config/ironic/ironic-agent.{kernel,initramfs}
     mkdir ${LOG_DIR}/system_configs/
-    cp -rL /etc/{hostname,hosts,resolv.conf,nsswitch.conf,docker,systemd} ${LOG_DIR}/system_configs/
+    cp -rL /etc/{hostname,hosts,host.conf,resolv.conf,nsswitch.conf,docker,systemd} ${LOG_DIR}/system_configs/
     cp -rvnL /var/log/* ${LOG_DIR}/system_logs/
 
 
@@ -32,9 +32,15 @@ copy_logs() {
 
     (set -x
     ip a
+    ip m
     ip l
     ip r
-    ping -c 4 ${KOLLA_INTERNAL_VIP_ADDRESS}) &> ${LOG_DIR}/system_logs/ip.txt
+    ip -6 r
+    ip neigh
+    ping -c 4 $(hostname)
+    ping6 -c 4 $(hostname)
+    ping -c 4 ${KOLLA_INTERNAL_VIP_ADDRESS}
+    ping6 -c 4 ${KOLLA_INTERNAL_VIP_ADDRESS}) &> ${LOG_DIR}/system_logs/ip.txt
 
     (set -x
     iptables -t raw -v -n -L
@@ -48,7 +54,15 @@ copy_logs() {
     ip6tables -t nat -v -n -L
     ip6tables -t filter -v -n -L) &> ${LOG_DIR}/system_logs/ip6tables.txt
 
-    ss -putona > ${LOG_DIR}/system_logs/ss.txt
+    ss -nep > ${LOG_DIR}/system_logs/ss.txt
+
+    ss -nep -l > ${LOG_DIR}/system_logs/ss_l.txt
+
+    (set -x
+    getent ahostsv4 $(hostname)
+    getent ahostsv6 $(hostname)) &> ${LOG_DIR}/system_logs/getent_ahostsvX.txt
+
+    sysctl -a &> ${LOG_DIR}/system_logs/sysctl.txt
 
     if [ `command -v dpkg` ]; then
         dpkg -l > ${LOG_DIR}/system_logs/dpkg-l.txt
diff --git a/tests/pre.yml b/tests/pre.yml
index 772f4f0714..f42fb8ebec 100644
--- a/tests/pre.yml
+++ b/tests/pre.yml
@@ -51,9 +51,12 @@
         api_interface_tunnel_vni: 10001
         tunnel_local_address: "{{ nodepool.private_ipv4 }}"
 
+    # NOTE(yoctozepto): CI VXLAN must use a different port than neutron-openvswitch-agent
+    # which defaults to 4789 (the default is used in CI)
+    # hence using port 4790
     - name: Create VXLAN interface
       become: true
-      command: ip link add {{ api_interface_name }} type vxlan id {{ api_interface_tunnel_vni }} local {{ tunnel_local_address }} dstport 4789
+      command: ip link add {{ api_interface_name }} type vxlan id {{ api_interface_tunnel_vni }} local {{ tunnel_local_address }} dstport 4790
 
     - name: Set VXLAN interface MTU
       become: true
@@ -78,7 +81,7 @@
       with_inventory_hostnames: all
       when: item != inventory_hostname
 
-    - name: Add IP address for VXLAN network
+    - name: Add IPv4 address for VXLAN network
       become: true
       vars:
         api_network_cidr: "{{ api_interface_address }}/{{ api_network_prefix_length }}"
@@ -88,21 +91,65 @@
         # broadcast address which fails checks logic
         api_network_broadcast_address: "{{ api_network_cidr | ipaddr('broadcast') }}"
       command: ip address add {{ api_network_cidr }} broadcast {{ api_network_broadcast_address }} dev {{ api_interface_name }}
+      when: address_family == 'ipv4'
 
-    - name: Accept traffic on the VXLAN network
+    # NOTE(yoctozepto): IPv6 has no broadcast address, let's not create confusion by setting it
+    - name: Add IPv6 address for VXLAN network
+      become: true
+      command: ip address add {{ api_interface_address }}/{{ api_network_prefix_length }} dev {{ api_interface_name }}
+      when: address_family == 'ipv6'
+
+    - name: Accept traffic on the VXLAN network (IN)
       become: true
       iptables:
         state: present
         action: insert
         chain: INPUT
-        ip_version: ipv4
+        ip_version: "{{ address_family }}"
         in_interface: "{{ api_interface_name }}"
         jump: ACCEPT
 
+    # NOTE(yoctozepto): the default policy is ACCEPT but it is nicer to get statistics
+    - name: Accept traffic on the VXLAN network (OUT)
+      become: true
+      iptables:
+        state: present
+        action: insert
+        chain: OUTPUT
+        ip_version: "{{ address_family }}"
+        out_interface: "{{ api_interface_name }}"
+        jump: ACCEPT
+
     - name: Bring VXLAN interface up
       become: true
       command: ip link set {{ api_interface_name }} up
 
+    # NOTE(yoctozepto): IPv6 DAD may delay proper address assignment
+    # this task will wait until DAD is done and addresses are no longer tentative
+    # we assign addresses uniquely so DAD can only move it to preferred
+    # hence we only check whether it's no longer tentative
+    - name: Ensure IPv6 addresses on VXLAN are no longer tentative
+      become: true
+      command: ip -o address show tentative dev {{ api_interface_name }}
+      register: tentative_addresses
+      until: tentative_addresses.stdout == ''
+      retries: 30
+      delay: 2
+      when:
+        - address_family == 'ipv6'
+
     - name: Ping across VXLAN
-      command: ping -c1 {{ hostvars[item].api_interface_address }}
+      vars:
+        ping_command: "{{ 'ping' if address_family == 'ipv4' else 'ping6' }}"
+      command: "{{ ping_command }} -c1 {{ hostvars[item].api_interface_address }}"
       with_inventory_hostnames: all
+
+    # NOTE(yoctozepto): CentOS 7 image uses myhostname plugin for NSS
+    # which creates issues with IPv6-only deployment by providing
+    # an IPv4 address for the current hostname (affects rabbitmq)
+    - name: Disable myhostname NSS plugin
+      become: true
+      replace:
+        path: /etc/nsswitch.conf
+        regexp: ' myhostname'
+        replace: ''
diff --git a/tests/run.yml b/tests/run.yml
index 8af8157636..769b5a9ec0 100644
--- a/tests/run.yml
+++ b/tests/run.yml
@@ -18,7 +18,6 @@
         build_image_tag: "change_{{ zuul.change | default('none') }}"
         is_upgrade: "{{ 'upgrade' in scenario }}"
         is_ceph: "{{ 'ceph' in scenario }}"
-        primary_address: "{{ hostvars.primary['ansible_' + api_interface_name].ipv4.address }}"
 
     - name: Prepare disks for Ceph or LVM
       script: "setup_disks.sh {{ disk_type }}"
diff --git a/tests/templates/globals-default.j2 b/tests/templates/globals-default.j2
index 1eb62f6922..890afb509a 100644
--- a/tests/templates/globals-default.j2
+++ b/tests/templates/globals-default.j2
@@ -2,6 +2,7 @@
 kolla_base_distro: "{{ base_distro }}"
 kolla_install_type: "{{ install_type }}"
 network_interface: "{{ api_interface_name }}"
+network_address_family: "{{ address_family }}"
 docker_restart_policy: "no"
 # TODO(mgoddard): Always do this in Ussuri cycle.
 {% if not is_previous_release or previous_release != 'stein' %}
@@ -25,7 +26,10 @@ openstack_service_workers: "1"
 {% if need_build_image and not is_previous_release %}
 # NOTE(Jeffrey4l): use different a docker namespace name in case it pull image from hub.docker.io when deplying
 docker_namespace: "lokolla"
-docker_registry: "{{ primary_address }}:4000"
+# NOTE(yoctozepto): use hostname or FQDN to be compatible between IPv4 and IPv6
+# docker does not support referencing registry via an IPv6 address
+# see: https://github.com/moby/moby/issues/39033
+docker_registry: "primary:4000"
 openstack_release: "{{ build_image_tag }}"
 {% else %}
 # use docker hub images
@@ -35,7 +39,7 @@ docker_namespace: "kolla"
 # will be the source of images during the upgrade.
 # NOTE(yoctozepto): this is required here for CI because we run templating
 # of docker systemd command only once
-docker_custom_option: "--insecure-registry {{ primary_address }}:4000"
+docker_custom_option: "--insecure-registry primary:4000"
 {% endif %}
 {% if not is_previous_release %}
 openstack_release: "{{ zuul.branch | basename }}"
@@ -53,6 +57,13 @@ enable_ceph_nfs: "yes"
 enable_cinder: "yes"
 ceph_pool_pg_num: 8
 ceph_pool_pgp_num: 8
+{% if address_family == 'ipv6' %}
+# NOTE(yoctozepto): ceph won't accept IPv6 address as hostname (due to ':', '.' were fine)
+# hence use inventory name as the others do
+# this is Train feature so would fail upgrades from Stein
+ceph_mon_host_type: "INVENTORY"
+ceph_osd_host_type: "INVENTORY"
+{% endif %}
 # This is experimental feature, disable if gate fail.
 # In multinode jobs without ceph rolling upgrade fails.
 glance_enable_rolling_upgrade: "yes"
diff --git a/tools/validate-all-file.py b/tools/validate-all-file.py
index 33177b54d5..2d87b16449 100755
--- a/tools/validate-all-file.py
+++ b/tools/validate-all-file.py
@@ -24,6 +24,9 @@ import jinja2
 import yaml
 
 
+from kolla_ansible.put_address_in_context import put_address_in_context
+
+
 PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
 
 NEWLINE_EOF_INCLUDE_PATTERNS = ['*.j2', '*.yml', '*.py', '*.sh']
@@ -37,6 +40,19 @@ YAML_INCLUDE_PATTERNS = ['*.yml']
 YAML_EXCLUDE_PATTERNS = ['.tox', '.testrepository', '.git',
                          'defaults', 'templates', 'vars']
 
+KOLLA_NETWORKS = [
+    'api',
+    'storage',
+    'cluster',
+    'swift_storage',
+    'swift_replication',
+    'migration',
+    'tunnel',
+    'octavia_network',
+    'bifrost_network',
+    'dns',  # designate
+]
+
 logging.basicConfig()
 LOG = logging.getLogger(__name__)
 
@@ -78,6 +94,15 @@ def check_json_j2():
     def basename_filter(text):
         return text.split('\\')[-1]
 
+    def kolla_address_filter_mock(network_name, hostname=None):
+        # no validation is possible for the hostname
+
+        if network_name not in KOLLA_NETWORKS:
+            raise ValueError("{network_name} not in KOLLA_NETWORKS"
+                             .format(network_name=network_name))
+
+        return "127.0.0.1"
+
     # Mock ansible hostvars variable, which is a nested dict
     def hostvars():
         return collections.defaultdict(hostvars)
@@ -91,6 +116,9 @@ def check_json_j2():
             loader=jinja2.FileSystemLoader(root))
         env.filters['bool'] = bool_filter
         env.filters['basename'] = basename_filter
+        env.filters['kolla_address'] = kolla_address_filter_mock
+        env.filters['put_address_in_context'] = \
+            put_address_in_context
         template = env.get_template(filename)
         # Mock ansible variables.
         context = {
diff --git a/zuul.d/base.yaml b/zuul.d/base.yaml
index 8f42824e79..932876233e 100644
--- a/zuul.d/base.yaml
+++ b/zuul.d/base.yaml
@@ -23,6 +23,7 @@
       api_network_prefix_length: "24"
       api_interface_name: vxlan0
       kolla_internal_vip_address: "192.0.2.10"
+      address_family: 'ipv4'
     roles:
       - zuul: zuul/zuul-jobs
 
@@ -33,6 +34,16 @@
       previous_release: stein
       scenario: upgrade
 
+- job:
+    name: kolla-ansible-ipv6-base
+    parent: kolla-ansible-base
+    voting: false
+    vars:
+      api_network_prefix: "fd::"
+      api_network_prefix_length: "64"
+      kolla_internal_vip_address: "fd::ff:0"
+      address_family: 'ipv6'
+
 - job:
     name: kolla-ansible-bifrost-base
     parent: kolla-ansible-base
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index 72a88dcf9e..657109a3fa 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -26,6 +26,14 @@
       base_distro: ubuntu
       install_type: source
 
+- job:
+    name: kolla-ansible-ubuntu-source-multinode-ipv6
+    parent: kolla-ansible-ipv6-base
+    nodeset: kolla-ansible-bionic-multi
+    vars:
+      base_distro: ubuntu
+      install_type: source
+
 - job:
     name: kolla-ansible-centos-binary
     parent: kolla-ansible-base
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index a19ba56088..8c97b7c65d 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -15,6 +15,7 @@
         - kolla-ansible-centos-source
         - kolla-ansible-debian-source
         - kolla-ansible-ubuntu-source
+        - kolla-ansible-ubuntu-source-multinode-ipv6
         - kolla-ansible-ubuntu-source-ceph
         - kolla-ansible-centos-source-ceph
         - kolla-ansible-ubuntu-source-cinder-lvm: