]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Add new patches instead of 1 old openstack-ci/build/havana openstack-ci/fuel-4.1.1/2013.2.2 openstack-ci/fuel-4.1/2013.2.2
authorIgor Yozhikov <iyozhikov@mirantis.com>
Mon, 23 Dec 2013 12:21:49 +0000 (16:21 +0400)
committerIgor Yozhikov <iyozhikov@mirantis.com>
Mon, 23 Dec 2013 12:57:13 +0000 (16:57 +0400)
debian/patches/Ability-to-configure-various-clients-used-by-the-Heat.patch [new file with mode: 0644]
debian/patches/Allow-Address-Pairs-feature.patch [new file with mode: 0644]
debian/patches/series
rpm/SOURCES/Ability-to-configure-various-clients-used-by-the-Heat.patch [new file with mode: 0644]
rpm/SOURCES/Allow-Address-Pairs-feature.patch [new file with mode: 0644]
rpm/SPECS/openstack-heat.spec

diff --git a/debian/patches/Ability-to-configure-various-clients-used-by-the-Heat.patch b/debian/patches/Ability-to-configure-various-clients-used-by-the-Heat.patch
new file mode 100644 (file)
index 0000000..dad7d14
--- /dev/null
@@ -0,0 +1,504 @@
+From ac190f0da6ce367cc833b92677266e7bbf7e2270 Mon Sep 17 00:00:00 2001
+From: Timur Sufiev <tsufiev@mirantis.com>
+Date: Thu, 5 Dec 2013 19:46:28 +0400
+Subject: [PATCH] Adds ability to configure various clients used by the Heat
+
+This commit adds config sections [clients_nova], [clients_swift],
+[clients_neutron], [clients_cinder], [clients_ceilometer] and
+[clients_keystone]. These sections contain additional configuration
+options for corresponding OpenStack clients.
+Currently those are only SSL-related setting ca_file, cert_file,
+key_file and insecure. Note, than not every client library is
+currently capable of utilizing all of the SSL settings.
+
+There is also a plain [clients] section that holds shared client
+options. Each option searched first at specific group (clients_xxx)
+and if it not found there then the value from [clients] group
+are taken (or default values if there is no such setting in this
+group). This allows defining shared configuration that would be
+used by most (or all) clients without repeating the same settings
+for each and every client separately
+
+Closes-Bug: #1213122
+Implements: blueprint clients-ssl-options
+Ported from: icehouse.
+---
+ etc/heat/heat.conf.sample          | 182 ++++++++++++++++++++++++++++++++++---
+ heat/common/config.py              |  28 +++++-
+ heat/common/heat_keystoneclient.py |  17 ++++
+ heat/engine/clients.py             |  30 +++++-
+ heat/tests/test_heatclient.py      |  36 ++++++--
+ 5 files changed, 268 insertions(+), 25 deletions(-)
+
+diff --git a/etc/heat/heat.conf.sample b/etc/heat/heat.conf.sample
+index 1444f9b..20dadd3 100644
+--- a/etc/heat/heat.conf.sample
++++ b/etc/heat/heat.conf.sample
+@@ -473,6 +473,43 @@
+ #matchmaker_heartbeat_ttl=600
++[clients_swift]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[auth_password]
++
++#
++# Options defined in heat.common.config
++#
++
++# Allow orchestration of multiple clouds (boolean value)
++#multi_cloud=false
++
++# Allowed keystone endpoints for auth_uri when multi_cloud is
++# enabled. At least one endpoint needs to be specified. (list
++# value)
++#allowed_auth_uris=
++
++
+ [ssl]
+ #
+@@ -568,6 +605,104 @@
+ #api_paste_config=api-paste.ini
++[clients_cinder]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[clients]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[clients_nova]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[matchmaker_ring]
++
++#
++# Options defined in heat.openstack.common.rpc.matchmaker_ring
++#
++
++# Matchmaker ring file (JSON) (string value)
++#ringfile=/etc/oslo/matchmaker_ring.json
++
++
++[clients_ceilometer]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
+ [rpc_notifier2]
+ #
+@@ -683,29 +818,26 @@
+ #workers=0
+-[auth_password]
++[clients_neutron]
+ #
+ # Options defined in heat.common.config
+ #
+-# Allow orchestration of multiple clouds (boolean value)
+-#multi_cloud=false
+-
+-# Allowed keystone endpoints for auth_uri when multi_cloud is
+-# enabled. At least one endpoint needs to be specified. (list
++# Optional CA cert file to use in SSL connections (string
+ # value)
+-#allowed_auth_uris=
++#ca_file=<None>
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
+-[matchmaker_ring]
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
+-#
+-# Options defined in heat.openstack.common.rpc.matchmaker_ring
+-#
+-
+-# Matchmaker ring file (JSON) (string value)
+-#ringfile=/etc/oslo/matchmaker_ring.json
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
+ [matchmaker_redis]
+@@ -724,3 +856,25 @@
+ #password=<None>
++[clients_keystone]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
+diff --git a/heat/common/config.py b/heat/common/config.py
+index 82b4ca5..b115f20 100644
+--- a/heat/common/config.py
++++ b/heat/common/config.py
+@@ -17,7 +17,7 @@
+ """
+ Routines for configuring Heat
+ """
+-
++import copy
+ import logging as sys_logging
+ import os
+@@ -133,6 +133,31 @@ auth_password_opts = [
+                 help=_('Allowed keystone endpoints for auth_uri when '
+                        'multi_cloud is enabled. At least one endpoint needs '
+                        'to be specified.'))]
++clients_opts = [
++    cfg.StrOpt('ca_file',
++               help=_('Optional CA cert file to use in SSL connections')),
++    cfg.StrOpt('cert_file',
++               help=_('Optional PEM-formatted certificate chain file')),
++    cfg.StrOpt('key_file',
++               help=_('Optional PEM-formatted file that contains the '
++                      'private key')),
++    cfg.BoolOpt('insecure',
++                default=False,
++                help=_("If set then the server's certificate will not "
++                       "be verified"))]
++
++
++def register_clients_opts():
++    cfg.CONF.register_opts(clients_opts, group='clients')
++    for client in ('nova', 'swift', 'neutron', 'cinder',
++                   'ceilometer', 'keystone'):
++        client_specific_group = 'clients_' + client
++        # register opts copy and put it to globals in order to
++        # generate_sample.sh to work
++        opts_copy = copy.deepcopy(clients_opts)
++        globals()[client_specific_group + '_opts'] = opts_copy
++        cfg.CONF.register_opts(opts_copy, group=client_specific_group)
++
+ cfg.CONF.register_opts(db_opts)
+ cfg.CONF.register_opts(engine_opts)
+@@ -142,6 +167,7 @@ cfg.CONF.register_group(paste_deploy_group)
+ cfg.CONF.register_opts(paste_deploy_opts, group=paste_deploy_group)
+ cfg.CONF.register_group(auth_password_group)
+ cfg.CONF.register_opts(auth_password_opts, group=auth_password_group)
++register_clients_opts()
+ def rpc_set_default():
+diff --git a/heat/common/heat_keystoneclient.py b/heat/common/heat_keystoneclient.py
+index 8fb13f7..d052a67 100644
+--- a/heat/common/heat_keystoneclient.py
++++ b/heat/common/heat_keystoneclient.py
+@@ -100,6 +100,10 @@ class KeystoneClient(object):
+             logger.error("Keystone v2 API connection failed, no password or "
+                          "auth_token!")
+             raise exception.AuthorizationFailure()
++        kwargs['cacert'] = self._get_client_option('ca_file')
++        kwargs['insecure'] = self._get_client_option('insecure')
++        kwargs['cert'] = self._get_client_option('cert_file')
++        kwargs['key'] = self._get_client_option('key_file')
+         client_v2 = kc.Client(**kwargs)
+         client_v2.authenticate(**auth_kwargs)
+@@ -161,12 +165,25 @@ class KeystoneClient(object):
+                          "auth_token!")
+             raise exception.AuthorizationFailure()
++        kwargs['cacert'] = self._get_client_option('ca_file')
++        kwargs['insecure'] = self._get_client_option('insecure')
++        kwargs['cert'] = self._get_client_option('cert_file')
++        kwargs['key'] = self._get_client_option('key_file')
+         client = kc_v3.Client(**kwargs)
+         # Have to explicitly authenticate() or client.auth_ref is None
+         client.authenticate()
+         return client
++    def _get_client_option(self, option):
++        try:
++            cfg.CONF.import_opt(option, 'heat.common.config',
++                                group='clients_keystone')
++            return getattr(cfg.CONF.clients_keystone, option)
++        except (cfg.NoSuchGroupError, cfg.NoSuchOptError):
++            cfg.CONF.import_opt(option, 'heat.common.config', group='clients')
++            return getattr(cfg.CONF.clients, option)
++
+     def create_trust_context(self):
+         """
+         If cfg.CONF.deferred_auth_method is trusts, we create a
+diff --git a/heat/engine/clients.py b/heat/engine/clients.py
+index 6deae5b..a749cd2 100644
+--- a/heat/engine/clients.py
++++ b/heat/engine/clients.py
+@@ -103,7 +103,9 @@ class OpenStackClients(object):
+             'service_type': service_type,
+             'username': None,
+             'api_key': None,
+-            'extensions': extensions
++            'extensions': extensions,
++            'cacert': self._get_client_option('nova', 'ca_file'),
++            'insecure': self._get_client_option('nova', 'insecure')
+         }
+         client = novaclient.Client(1.1, **args)
+@@ -133,7 +135,9 @@ class OpenStackClients(object):
+             'key': None,
+             'authurl': None,
+             'preauthtoken': self.auth_token,
+-            'preauthurl': self.url_for(service_type='object-store')
++            'preauthurl': self.url_for(service_type='object-store'),
++            'cacert': self._get_client_option('swift', 'ca_file'),
++            'insecure': self._get_client_option('swift', 'insecure')
+         }
+         self._swift = swiftclient.Connection(**args)
+         return self._swift
+@@ -153,7 +157,9 @@ class OpenStackClients(object):
+             'auth_url': con.auth_url,
+             'service_type': 'network',
+             'token': self.auth_token,
+-            'endpoint_url': self.url_for(service_type='network')
++            'endpoint_url': self.url_for(service_type='network'),
++            'ca_cert': self._get_client_option('neutron', 'ca_file'),
++            'insecure': self._get_client_option('neutron', 'insecure')
+         }
+         self._neutron = neutronclient.Client(**args)
+@@ -176,7 +182,9 @@ class OpenStackClients(object):
+             'auth_url': con.auth_url,
+             'project_id': con.tenant,
+             'username': None,
+-            'api_key': None
++            'api_key': None,
++            'cacert': self._get_client_option('cinder', 'ca_file'),
++            'insecure': self._get_client_option('cinder', 'insecure')
+         }
+         self._cinder = cinderclient.Client('1', **args)
+@@ -202,6 +210,10 @@ class OpenStackClients(object):
+             'project_id': con.tenant,
+             'token': lambda: self.auth_token,
+             'endpoint': self.url_for(service_type='metering'),
++            'ca_file': self._get_client_option('ceilometer', 'ca_file'),
++            'cert_file': self._get_client_option('ceilometer', 'cert_file'),
++            'key_file': self._get_client_option('ceilometer', 'key_file'),
++            'insecure': self._get_client_option('ceilometer', 'insecure')
+         }
+         client = ceilometerclient.Client(**args)
+@@ -209,6 +221,16 @@ class OpenStackClients(object):
+         self._ceilometer = client
+         return self._ceilometer
++    def _get_client_option(self, client, option):
++        try:
++            group_name = 'clients_' + client
++            cfg.CONF.import_opt(option, 'heat.common.config',
++                                group=group_name)
++            return getattr(getattr(cfg.CONF, group_name), option)
++        except (cfg.NoSuchGroupError, cfg.NoSuchOptError):
++            cfg.CONF.import_opt(option, 'heat.common.config', group='clients')
++            return getattr(cfg.CONF.clients, option)
++
+ if cfg.CONF.cloud_backend:
+     cloud_backend_module = importutils.import_module(cfg.CONF.cloud_backend)
+diff --git a/heat/tests/test_heatclient.py b/heat/tests/test_heatclient.py
+index 7e195dc..712ffa5 100644
+--- a/heat/tests/test_heatclient.py
++++ b/heat/tests/test_heatclient.py
+@@ -51,7 +51,11 @@ class KeystoneClientTest(HeatTestCase):
+             self.mock_ks_client = heat_keystoneclient.kc.Client(
+                 auth_url=mox.IgnoreArg(),
+                 tenant_name='test_tenant',
+-                token='abcd1234')
++                token='abcd1234',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+             self.mock_ks_client.authenticate().AndReturn(auth_ok)
+         elif method == 'password':
+             self.mock_ks_client = heat_keystoneclient.kc.Client(
+@@ -59,14 +63,22 @@ class KeystoneClientTest(HeatTestCase):
+                 tenant_name='test_tenant',
+                 tenant_id='test_tenant_id',
+                 username='test_username',
+-                password='password')
++                password='password',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+             self.mock_ks_client.authenticate().AndReturn(auth_ok)
+         if method == 'trust':
+             self.mock_ks_client = heat_keystoneclient.kc.Client(
+                 auth_url='http://server.test:5000/v2.0',
+                 password='verybadpass',
+                 tenant_name='service',
+-                username='heat')
++                username='heat',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+             self.mock_ks_client.authenticate(trust_id='atrust123',
+                                              tenant_id='test_tenant_id'
+                                              ).AndReturn(auth_ok)
+@@ -81,7 +93,11 @@ class KeystoneClientTest(HeatTestCase):
+             self.mock_ks_v3_client = heat_keystoneclient.kc_v3.Client(
+                 token='abcd1234', project_name='test_tenant',
+                 auth_url='http://server.test:5000/v3',
+-                endpoint='http://server.test:5000/v3')
++                endpoint='http://server.test:5000/v3',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+         elif method == 'password':
+             self.mock_ks_v3_client = heat_keystoneclient.kc_v3.Client(
+                 username='test_username',
+@@ -89,13 +105,21 @@ class KeystoneClientTest(HeatTestCase):
+                 project_name='test_tenant',
+                 project_id='test_tenant_id',
+                 auth_url='http://server.test:5000/v3',
+-                endpoint='http://server.test:5000/v3')
++                endpoint='http://server.test:5000/v3',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+         elif method == 'trust':
+             self.mock_ks_v3_client = heat_keystoneclient.kc_v3.Client(
+                 username='heat',
+                 password='verybadpass',
+                 project_name='service',
+-                auth_url='http://server.test:5000/v3')
++                auth_url='http://server.test:5000/v3',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+         self.mock_ks_v3_client.authenticate().AndReturn(auth_ok)
+     def test_username_length(self):
+-- 
+1.8.3.2
+
diff --git a/debian/patches/Allow-Address-Pairs-feature.patch b/debian/patches/Allow-Address-Pairs-feature.patch
new file mode 100644 (file)
index 0000000..105a2f6
--- /dev/null
@@ -0,0 +1,168 @@
+From 94350f3206d067e7c9012ac5fb4c40555949d95c Mon Sep 17 00:00:00 2001
+From: Timur Sufiev <tsufiev@mirantis.com>
+Date: Thu, 5 Dec 2013 19:43:57 +0400
+Subject: [PATCH] Added support for Allow-Address-Pairs feature
+
+Added support for feature called Allow-Address-Pairs introduced in
+Ie73b3886c5be8e1fc4ade86a0cfb854267f345ac
+
+Implements: blueprint allowed-address-pairs
+Ported from: icehouse.
+---
+ heat/engine/resources/neutron/port.py | 19 +++++++-
+ heat/tests/test_neutron.py            | 84 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 102 insertions(+), 1 deletion(-)
+
+diff --git a/heat/engine/resources/neutron/port.py b/heat/engine/resources/neutron/port.py
+index 06c219a..b12b74a 100644
+--- a/heat/engine/resources/neutron/port.py
++++ b/heat/engine/resources/neutron/port.py
+@@ -29,6 +29,9 @@ class Port(neutron.NeutronResource):
+     fixed_ip_schema = {'subnet_id': {'Type': 'String'},
+                        'ip_address': {'Type': 'String'}}
++    address_pair_schema = {'mac_address': {'Type': 'String'},
++                           'ip_address': {'Type': 'String', 'Required': True}}
++
+     properties_schema = {'network_id': {'Type': 'String',
+                                         'Required': True},
+                          'name': {'Type': 'String'},
+@@ -41,7 +44,13 @@ class Port(neutron.NeutronResource):
+                                                   'Schema': fixed_ip_schema}},
+                          'mac_address': {'Type': 'String'},
+                          'device_id': {'Type': 'String'},
+-                         'security_groups': {'Type': 'List'}}
++                         'security_groups': {'Type': 'List'},
++                         'allowed_address_pairs': {
++                             'Type': 'List',
++                             'Schema': {'Type': 'Map',
++                                        'Schema': address_pair_schema}}
++                         }
++
+     attributes_schema = {
+         "admin_state_up": _("The administrative state of this port."),
+         "device_id": _("Unique identifier for the device."),
+@@ -53,6 +62,8 @@ class Port(neutron.NeutronResource):
+         "security_groups": _("A list of security groups for the port."),
+         "status": _("The status of the port."),
+         "tenant_id": _("Tenant owning the port"),
++        "allowed_address_pairs": _("Additional mac/ip address pairs allowed "
++                                   "to pass through a port"),
+         "show": _("All attributes."),
+     }
+@@ -79,6 +90,12 @@ class Port(neutron.NeutronResource):
+                 if value is None:
+                     fixed_ip.pop(key)
++        # delete empty MAC addresses so that Neutron validation code
++        # wouldn't fail as it not accepts Nones
++        for pair in props.get('allowed_address_pairs', []):
++            if 'mac_address' in pair and pair['mac_address'] is None:
++                del pair['mac_address']
++
+         if self.properties['security_groups']:
+             props['security_groups'] = self.get_secgroup_uuids(
+                 self.stack, self.properties, 'security_groups', self.name,
+diff --git a/heat/tests/test_neutron.py b/heat/tests/test_neutron.py
+index eb5b295..15d9a2d 100644
+--- a/heat/tests/test_neutron.py
++++ b/heat/tests/test_neutron.py
+@@ -171,6 +171,26 @@ neutron_port_template = '''
+ }
+ '''
++neutron_port_with_address_pair_template = '''
++{
++  "AWSTemplateFormatVersion" : "2010-09-09",
++  "Description" : "Template to test Neutron resources",
++  "Parameters" : {},
++  "Resources" : {
++    "port": {
++      "Type": "OS::Neutron::Port",
++      "Properties": {
++        "network_id": "abcd1234",
++        "allowed_address_pairs": [{
++          "ip_address": "10.0.3.21",
++          "mac_address": "00-B0-D0-86-BB-F7"
++        }]
++      }
++    }
++  }
++}
++'''
++
+ class NeutronTest(HeatTestCase):
+@@ -1147,3 +1167,67 @@ class NeutronPortTest(HeatTestCase):
+         port = stack['port']
+         scheduler.TaskRunner(port.create)()
+         self.m.VerifyAll()
++
++    def test_allowed_address_pair(self):
++        clients.OpenStackClients.keystone().AndReturn(
++            fakes.FakeKeystoneClient())
++        neutronclient.Client.create_port({'port': {
++            'network_id': u'abcd1234',
++            'allowed_address_pairs': [{
++                'ip_address': u'10.0.3.21',
++                'mac_address': u'00-B0-D0-86-BB-F7'
++            }],
++            'name': utils.PhysName('test_stack', 'port'),
++            'admin_state_up': True}}
++        ).AndReturn({'port': {
++            "status": "BUILD",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++        neutronclient.Client.show_port(
++            'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
++        ).AndReturn({'port': {
++            "status": "ACTIVE",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++
++        self.m.ReplayAll()
++
++        t = template_format.parse(neutron_port_with_address_pair_template)
++        stack = utils.parse_stack(t)
++
++        port = stack['port']
++        scheduler.TaskRunner(port.create)()
++        self.m.VerifyAll()
++
++    def test_missing_mac_address(self):
++        clients.OpenStackClients.keystone().AndReturn(
++            fakes.FakeKeystoneClient())
++        neutronclient.Client.create_port({'port': {
++            'network_id': u'abcd1234',
++            'allowed_address_pairs': [{
++                'ip_address': u'10.0.3.21',
++            }],
++            'name': utils.PhysName('test_stack', 'port'),
++            'admin_state_up': True}}
++        ).AndReturn({'port': {
++            "status": "BUILD",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++        neutronclient.Client.show_port(
++            'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
++        ).AndReturn({'port': {
++            "status": "ACTIVE",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++
++        self.m.ReplayAll()
++
++        t = template_format.parse(neutron_port_with_address_pair_template)
++        t['Resources']['port']['Properties']['allowed_address_pairs'][0].pop(
++            'mac_address'
++        )
++        stack = utils.parse_stack(t)
++
++        port = stack['port']
++        scheduler.TaskRunner(port.create)()
++        self.m.VerifyAll()
+-- 
+1.8.3.2
+
index f1f090a3457d8f1818eb9e502f2c06598b706e97..7ac31d7d4252848fd3290c31e2f2d417c3d56c84 100644 (file)
@@ -1,2 +1,3 @@
 default-sqlite.patch
-SSL_parameters_for_various_clients_used_in_Heat_Havana_stable.patch
+Ability-to-configure-various-clients-used-by-the-Heat.patch
+Allow-Address-Pairs-feature.patch
diff --git a/rpm/SOURCES/Ability-to-configure-various-clients-used-by-the-Heat.patch b/rpm/SOURCES/Ability-to-configure-various-clients-used-by-the-Heat.patch
new file mode 100644 (file)
index 0000000..dad7d14
--- /dev/null
@@ -0,0 +1,504 @@
+From ac190f0da6ce367cc833b92677266e7bbf7e2270 Mon Sep 17 00:00:00 2001
+From: Timur Sufiev <tsufiev@mirantis.com>
+Date: Thu, 5 Dec 2013 19:46:28 +0400
+Subject: [PATCH] Adds ability to configure various clients used by the Heat
+
+This commit adds config sections [clients_nova], [clients_swift],
+[clients_neutron], [clients_cinder], [clients_ceilometer] and
+[clients_keystone]. These sections contain additional configuration
+options for corresponding OpenStack clients.
+Currently those are only SSL-related setting ca_file, cert_file,
+key_file and insecure. Note, than not every client library is
+currently capable of utilizing all of the SSL settings.
+
+There is also a plain [clients] section that holds shared client
+options. Each option searched first at specific group (clients_xxx)
+and if it not found there then the value from [clients] group
+are taken (or default values if there is no such setting in this
+group). This allows defining shared configuration that would be
+used by most (or all) clients without repeating the same settings
+for each and every client separately
+
+Closes-Bug: #1213122
+Implements: blueprint clients-ssl-options
+Ported from: icehouse.
+---
+ etc/heat/heat.conf.sample          | 182 ++++++++++++++++++++++++++++++++++---
+ heat/common/config.py              |  28 +++++-
+ heat/common/heat_keystoneclient.py |  17 ++++
+ heat/engine/clients.py             |  30 +++++-
+ heat/tests/test_heatclient.py      |  36 ++++++--
+ 5 files changed, 268 insertions(+), 25 deletions(-)
+
+diff --git a/etc/heat/heat.conf.sample b/etc/heat/heat.conf.sample
+index 1444f9b..20dadd3 100644
+--- a/etc/heat/heat.conf.sample
++++ b/etc/heat/heat.conf.sample
+@@ -473,6 +473,43 @@
+ #matchmaker_heartbeat_ttl=600
++[clients_swift]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[auth_password]
++
++#
++# Options defined in heat.common.config
++#
++
++# Allow orchestration of multiple clouds (boolean value)
++#multi_cloud=false
++
++# Allowed keystone endpoints for auth_uri when multi_cloud is
++# enabled. At least one endpoint needs to be specified. (list
++# value)
++#allowed_auth_uris=
++
++
+ [ssl]
+ #
+@@ -568,6 +605,104 @@
+ #api_paste_config=api-paste.ini
++[clients_cinder]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[clients]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[clients_nova]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
++[matchmaker_ring]
++
++#
++# Options defined in heat.openstack.common.rpc.matchmaker_ring
++#
++
++# Matchmaker ring file (JSON) (string value)
++#ringfile=/etc/oslo/matchmaker_ring.json
++
++
++[clients_ceilometer]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
+ [rpc_notifier2]
+ #
+@@ -683,29 +818,26 @@
+ #workers=0
+-[auth_password]
++[clients_neutron]
+ #
+ # Options defined in heat.common.config
+ #
+-# Allow orchestration of multiple clouds (boolean value)
+-#multi_cloud=false
+-
+-# Allowed keystone endpoints for auth_uri when multi_cloud is
+-# enabled. At least one endpoint needs to be specified. (list
++# Optional CA cert file to use in SSL connections (string
+ # value)
+-#allowed_auth_uris=
++#ca_file=<None>
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
+-[matchmaker_ring]
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
+-#
+-# Options defined in heat.openstack.common.rpc.matchmaker_ring
+-#
+-
+-# Matchmaker ring file (JSON) (string value)
+-#ringfile=/etc/oslo/matchmaker_ring.json
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
+ [matchmaker_redis]
+@@ -724,3 +856,25 @@
+ #password=<None>
++[clients_keystone]
++
++#
++# Options defined in heat.common.config
++#
++
++# Optional CA cert file to use in SSL connections (string
++# value)
++#ca_file=<None>
++
++# Optional PEM-formatted certificate chain file (string value)
++#cert_file=<None>
++
++# Optional PEM-formatted file that contains the private key
++# (string value)
++#key_file=<None>
++
++# If set then the server's certificate will not be verified
++# (boolean value)
++#insecure=false
++
++
+diff --git a/heat/common/config.py b/heat/common/config.py
+index 82b4ca5..b115f20 100644
+--- a/heat/common/config.py
++++ b/heat/common/config.py
+@@ -17,7 +17,7 @@
+ """
+ Routines for configuring Heat
+ """
+-
++import copy
+ import logging as sys_logging
+ import os
+@@ -133,6 +133,31 @@ auth_password_opts = [
+                 help=_('Allowed keystone endpoints for auth_uri when '
+                        'multi_cloud is enabled. At least one endpoint needs '
+                        'to be specified.'))]
++clients_opts = [
++    cfg.StrOpt('ca_file',
++               help=_('Optional CA cert file to use in SSL connections')),
++    cfg.StrOpt('cert_file',
++               help=_('Optional PEM-formatted certificate chain file')),
++    cfg.StrOpt('key_file',
++               help=_('Optional PEM-formatted file that contains the '
++                      'private key')),
++    cfg.BoolOpt('insecure',
++                default=False,
++                help=_("If set then the server's certificate will not "
++                       "be verified"))]
++
++
++def register_clients_opts():
++    cfg.CONF.register_opts(clients_opts, group='clients')
++    for client in ('nova', 'swift', 'neutron', 'cinder',
++                   'ceilometer', 'keystone'):
++        client_specific_group = 'clients_' + client
++        # register opts copy and put it to globals in order to
++        # generate_sample.sh to work
++        opts_copy = copy.deepcopy(clients_opts)
++        globals()[client_specific_group + '_opts'] = opts_copy
++        cfg.CONF.register_opts(opts_copy, group=client_specific_group)
++
+ cfg.CONF.register_opts(db_opts)
+ cfg.CONF.register_opts(engine_opts)
+@@ -142,6 +167,7 @@ cfg.CONF.register_group(paste_deploy_group)
+ cfg.CONF.register_opts(paste_deploy_opts, group=paste_deploy_group)
+ cfg.CONF.register_group(auth_password_group)
+ cfg.CONF.register_opts(auth_password_opts, group=auth_password_group)
++register_clients_opts()
+ def rpc_set_default():
+diff --git a/heat/common/heat_keystoneclient.py b/heat/common/heat_keystoneclient.py
+index 8fb13f7..d052a67 100644
+--- a/heat/common/heat_keystoneclient.py
++++ b/heat/common/heat_keystoneclient.py
+@@ -100,6 +100,10 @@ class KeystoneClient(object):
+             logger.error("Keystone v2 API connection failed, no password or "
+                          "auth_token!")
+             raise exception.AuthorizationFailure()
++        kwargs['cacert'] = self._get_client_option('ca_file')
++        kwargs['insecure'] = self._get_client_option('insecure')
++        kwargs['cert'] = self._get_client_option('cert_file')
++        kwargs['key'] = self._get_client_option('key_file')
+         client_v2 = kc.Client(**kwargs)
+         client_v2.authenticate(**auth_kwargs)
+@@ -161,12 +165,25 @@ class KeystoneClient(object):
+                          "auth_token!")
+             raise exception.AuthorizationFailure()
++        kwargs['cacert'] = self._get_client_option('ca_file')
++        kwargs['insecure'] = self._get_client_option('insecure')
++        kwargs['cert'] = self._get_client_option('cert_file')
++        kwargs['key'] = self._get_client_option('key_file')
+         client = kc_v3.Client(**kwargs)
+         # Have to explicitly authenticate() or client.auth_ref is None
+         client.authenticate()
+         return client
++    def _get_client_option(self, option):
++        try:
++            cfg.CONF.import_opt(option, 'heat.common.config',
++                                group='clients_keystone')
++            return getattr(cfg.CONF.clients_keystone, option)
++        except (cfg.NoSuchGroupError, cfg.NoSuchOptError):
++            cfg.CONF.import_opt(option, 'heat.common.config', group='clients')
++            return getattr(cfg.CONF.clients, option)
++
+     def create_trust_context(self):
+         """
+         If cfg.CONF.deferred_auth_method is trusts, we create a
+diff --git a/heat/engine/clients.py b/heat/engine/clients.py
+index 6deae5b..a749cd2 100644
+--- a/heat/engine/clients.py
++++ b/heat/engine/clients.py
+@@ -103,7 +103,9 @@ class OpenStackClients(object):
+             'service_type': service_type,
+             'username': None,
+             'api_key': None,
+-            'extensions': extensions
++            'extensions': extensions,
++            'cacert': self._get_client_option('nova', 'ca_file'),
++            'insecure': self._get_client_option('nova', 'insecure')
+         }
+         client = novaclient.Client(1.1, **args)
+@@ -133,7 +135,9 @@ class OpenStackClients(object):
+             'key': None,
+             'authurl': None,
+             'preauthtoken': self.auth_token,
+-            'preauthurl': self.url_for(service_type='object-store')
++            'preauthurl': self.url_for(service_type='object-store'),
++            'cacert': self._get_client_option('swift', 'ca_file'),
++            'insecure': self._get_client_option('swift', 'insecure')
+         }
+         self._swift = swiftclient.Connection(**args)
+         return self._swift
+@@ -153,7 +157,9 @@ class OpenStackClients(object):
+             'auth_url': con.auth_url,
+             'service_type': 'network',
+             'token': self.auth_token,
+-            'endpoint_url': self.url_for(service_type='network')
++            'endpoint_url': self.url_for(service_type='network'),
++            'ca_cert': self._get_client_option('neutron', 'ca_file'),
++            'insecure': self._get_client_option('neutron', 'insecure')
+         }
+         self._neutron = neutronclient.Client(**args)
+@@ -176,7 +182,9 @@ class OpenStackClients(object):
+             'auth_url': con.auth_url,
+             'project_id': con.tenant,
+             'username': None,
+-            'api_key': None
++            'api_key': None,
++            'cacert': self._get_client_option('cinder', 'ca_file'),
++            'insecure': self._get_client_option('cinder', 'insecure')
+         }
+         self._cinder = cinderclient.Client('1', **args)
+@@ -202,6 +210,10 @@ class OpenStackClients(object):
+             'project_id': con.tenant,
+             'token': lambda: self.auth_token,
+             'endpoint': self.url_for(service_type='metering'),
++            'ca_file': self._get_client_option('ceilometer', 'ca_file'),
++            'cert_file': self._get_client_option('ceilometer', 'cert_file'),
++            'key_file': self._get_client_option('ceilometer', 'key_file'),
++            'insecure': self._get_client_option('ceilometer', 'insecure')
+         }
+         client = ceilometerclient.Client(**args)
+@@ -209,6 +221,16 @@ class OpenStackClients(object):
+         self._ceilometer = client
+         return self._ceilometer
++    def _get_client_option(self, client, option):
++        try:
++            group_name = 'clients_' + client
++            cfg.CONF.import_opt(option, 'heat.common.config',
++                                group=group_name)
++            return getattr(getattr(cfg.CONF, group_name), option)
++        except (cfg.NoSuchGroupError, cfg.NoSuchOptError):
++            cfg.CONF.import_opt(option, 'heat.common.config', group='clients')
++            return getattr(cfg.CONF.clients, option)
++
+ if cfg.CONF.cloud_backend:
+     cloud_backend_module = importutils.import_module(cfg.CONF.cloud_backend)
+diff --git a/heat/tests/test_heatclient.py b/heat/tests/test_heatclient.py
+index 7e195dc..712ffa5 100644
+--- a/heat/tests/test_heatclient.py
++++ b/heat/tests/test_heatclient.py
+@@ -51,7 +51,11 @@ class KeystoneClientTest(HeatTestCase):
+             self.mock_ks_client = heat_keystoneclient.kc.Client(
+                 auth_url=mox.IgnoreArg(),
+                 tenant_name='test_tenant',
+-                token='abcd1234')
++                token='abcd1234',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+             self.mock_ks_client.authenticate().AndReturn(auth_ok)
+         elif method == 'password':
+             self.mock_ks_client = heat_keystoneclient.kc.Client(
+@@ -59,14 +63,22 @@ class KeystoneClientTest(HeatTestCase):
+                 tenant_name='test_tenant',
+                 tenant_id='test_tenant_id',
+                 username='test_username',
+-                password='password')
++                password='password',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+             self.mock_ks_client.authenticate().AndReturn(auth_ok)
+         if method == 'trust':
+             self.mock_ks_client = heat_keystoneclient.kc.Client(
+                 auth_url='http://server.test:5000/v2.0',
+                 password='verybadpass',
+                 tenant_name='service',
+-                username='heat')
++                username='heat',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+             self.mock_ks_client.authenticate(trust_id='atrust123',
+                                              tenant_id='test_tenant_id'
+                                              ).AndReturn(auth_ok)
+@@ -81,7 +93,11 @@ class KeystoneClientTest(HeatTestCase):
+             self.mock_ks_v3_client = heat_keystoneclient.kc_v3.Client(
+                 token='abcd1234', project_name='test_tenant',
+                 auth_url='http://server.test:5000/v3',
+-                endpoint='http://server.test:5000/v3')
++                endpoint='http://server.test:5000/v3',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+         elif method == 'password':
+             self.mock_ks_v3_client = heat_keystoneclient.kc_v3.Client(
+                 username='test_username',
+@@ -89,13 +105,21 @@ class KeystoneClientTest(HeatTestCase):
+                 project_name='test_tenant',
+                 project_id='test_tenant_id',
+                 auth_url='http://server.test:5000/v3',
+-                endpoint='http://server.test:5000/v3')
++                endpoint='http://server.test:5000/v3',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+         elif method == 'trust':
+             self.mock_ks_v3_client = heat_keystoneclient.kc_v3.Client(
+                 username='heat',
+                 password='verybadpass',
+                 project_name='service',
+-                auth_url='http://server.test:5000/v3')
++                auth_url='http://server.test:5000/v3',
++                cacert=None,
++                cert=None,
++                insecure=False,
++                key=None)
+         self.mock_ks_v3_client.authenticate().AndReturn(auth_ok)
+     def test_username_length(self):
+-- 
+1.8.3.2
+
diff --git a/rpm/SOURCES/Allow-Address-Pairs-feature.patch b/rpm/SOURCES/Allow-Address-Pairs-feature.patch
new file mode 100644 (file)
index 0000000..105a2f6
--- /dev/null
@@ -0,0 +1,168 @@
+From 94350f3206d067e7c9012ac5fb4c40555949d95c Mon Sep 17 00:00:00 2001
+From: Timur Sufiev <tsufiev@mirantis.com>
+Date: Thu, 5 Dec 2013 19:43:57 +0400
+Subject: [PATCH] Added support for Allow-Address-Pairs feature
+
+Added support for feature called Allow-Address-Pairs introduced in
+Ie73b3886c5be8e1fc4ade86a0cfb854267f345ac
+
+Implements: blueprint allowed-address-pairs
+Ported from: icehouse.
+---
+ heat/engine/resources/neutron/port.py | 19 +++++++-
+ heat/tests/test_neutron.py            | 84 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 102 insertions(+), 1 deletion(-)
+
+diff --git a/heat/engine/resources/neutron/port.py b/heat/engine/resources/neutron/port.py
+index 06c219a..b12b74a 100644
+--- a/heat/engine/resources/neutron/port.py
++++ b/heat/engine/resources/neutron/port.py
+@@ -29,6 +29,9 @@ class Port(neutron.NeutronResource):
+     fixed_ip_schema = {'subnet_id': {'Type': 'String'},
+                        'ip_address': {'Type': 'String'}}
++    address_pair_schema = {'mac_address': {'Type': 'String'},
++                           'ip_address': {'Type': 'String', 'Required': True}}
++
+     properties_schema = {'network_id': {'Type': 'String',
+                                         'Required': True},
+                          'name': {'Type': 'String'},
+@@ -41,7 +44,13 @@ class Port(neutron.NeutronResource):
+                                                   'Schema': fixed_ip_schema}},
+                          'mac_address': {'Type': 'String'},
+                          'device_id': {'Type': 'String'},
+-                         'security_groups': {'Type': 'List'}}
++                         'security_groups': {'Type': 'List'},
++                         'allowed_address_pairs': {
++                             'Type': 'List',
++                             'Schema': {'Type': 'Map',
++                                        'Schema': address_pair_schema}}
++                         }
++
+     attributes_schema = {
+         "admin_state_up": _("The administrative state of this port."),
+         "device_id": _("Unique identifier for the device."),
+@@ -53,6 +62,8 @@ class Port(neutron.NeutronResource):
+         "security_groups": _("A list of security groups for the port."),
+         "status": _("The status of the port."),
+         "tenant_id": _("Tenant owning the port"),
++        "allowed_address_pairs": _("Additional mac/ip address pairs allowed "
++                                   "to pass through a port"),
+         "show": _("All attributes."),
+     }
+@@ -79,6 +90,12 @@ class Port(neutron.NeutronResource):
+                 if value is None:
+                     fixed_ip.pop(key)
++        # delete empty MAC addresses so that Neutron validation code
++        # wouldn't fail as it not accepts Nones
++        for pair in props.get('allowed_address_pairs', []):
++            if 'mac_address' in pair and pair['mac_address'] is None:
++                del pair['mac_address']
++
+         if self.properties['security_groups']:
+             props['security_groups'] = self.get_secgroup_uuids(
+                 self.stack, self.properties, 'security_groups', self.name,
+diff --git a/heat/tests/test_neutron.py b/heat/tests/test_neutron.py
+index eb5b295..15d9a2d 100644
+--- a/heat/tests/test_neutron.py
++++ b/heat/tests/test_neutron.py
+@@ -171,6 +171,26 @@ neutron_port_template = '''
+ }
+ '''
++neutron_port_with_address_pair_template = '''
++{
++  "AWSTemplateFormatVersion" : "2010-09-09",
++  "Description" : "Template to test Neutron resources",
++  "Parameters" : {},
++  "Resources" : {
++    "port": {
++      "Type": "OS::Neutron::Port",
++      "Properties": {
++        "network_id": "abcd1234",
++        "allowed_address_pairs": [{
++          "ip_address": "10.0.3.21",
++          "mac_address": "00-B0-D0-86-BB-F7"
++        }]
++      }
++    }
++  }
++}
++'''
++
+ class NeutronTest(HeatTestCase):
+@@ -1147,3 +1167,67 @@ class NeutronPortTest(HeatTestCase):
+         port = stack['port']
+         scheduler.TaskRunner(port.create)()
+         self.m.VerifyAll()
++
++    def test_allowed_address_pair(self):
++        clients.OpenStackClients.keystone().AndReturn(
++            fakes.FakeKeystoneClient())
++        neutronclient.Client.create_port({'port': {
++            'network_id': u'abcd1234',
++            'allowed_address_pairs': [{
++                'ip_address': u'10.0.3.21',
++                'mac_address': u'00-B0-D0-86-BB-F7'
++            }],
++            'name': utils.PhysName('test_stack', 'port'),
++            'admin_state_up': True}}
++        ).AndReturn({'port': {
++            "status": "BUILD",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++        neutronclient.Client.show_port(
++            'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
++        ).AndReturn({'port': {
++            "status": "ACTIVE",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++
++        self.m.ReplayAll()
++
++        t = template_format.parse(neutron_port_with_address_pair_template)
++        stack = utils.parse_stack(t)
++
++        port = stack['port']
++        scheduler.TaskRunner(port.create)()
++        self.m.VerifyAll()
++
++    def test_missing_mac_address(self):
++        clients.OpenStackClients.keystone().AndReturn(
++            fakes.FakeKeystoneClient())
++        neutronclient.Client.create_port({'port': {
++            'network_id': u'abcd1234',
++            'allowed_address_pairs': [{
++                'ip_address': u'10.0.3.21',
++            }],
++            'name': utils.PhysName('test_stack', 'port'),
++            'admin_state_up': True}}
++        ).AndReturn({'port': {
++            "status": "BUILD",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++        neutronclient.Client.show_port(
++            'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
++        ).AndReturn({'port': {
++            "status": "ACTIVE",
++            "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
++        }})
++
++        self.m.ReplayAll()
++
++        t = template_format.parse(neutron_port_with_address_pair_template)
++        t['Resources']['port']['Properties']['allowed_address_pairs'][0].pop(
++            'mac_address'
++        )
++        stack = utils.parse_stack(t)
++
++        port = stack['port']
++        scheduler.TaskRunner(port.create)()
++        self.m.VerifyAll()
+-- 
+1.8.3.2
+
index eebc295165a9335b843b3fa7150d4fa2b82a90cf..c899f8b7030ffe10082efccfbe875e5d2057c1c1 100644 (file)
@@ -27,7 +27,8 @@ Patch0: switch-to-using-m2crypto.patch
 Patch1: remove-pbr-runtime-dependency.patch
 # EPEL specific patch, not upstream
 Patch100: heat-newdeps.patch
-Patch200: SSL_parameters_for_various_clients_used_in_Heat_Havana_stable.patch
+Patch200: Ability-to-configure-various-clients-used-by-the-Heat.patch
+Patch300: Allow-Address-Pairs-feature.patch 
 
 BuildArch: noarch
 BuildRequires: git
@@ -79,6 +80,7 @@ Requires: %{name}-api-cloudwatch = %{version}-%{release}
 %patch1 -p1
 %patch100 -p1
 %patch200 -p1
+%patch300 -p1
 
 sed -i s/REDHATHEATVERSION/%{version}/ heat/version.py
 sed -i s/REDHATHEATRELEASE/%{release}/ heat/version.py