From 17d6a09efb67cc9b19cc8a051401100e0896c531 Mon Sep 17 00:00:00 2001 From: Igor Yozhikov Date: Mon, 23 Dec 2013 16:21:49 +0400 Subject: [PATCH] Add new patches instead of 1 old --- ...ure-various-clients-used-by-the-Heat.patch | 504 ++++++++++++++++++ .../patches/Allow-Address-Pairs-feature.patch | 168 ++++++ debian/patches/series | 3 +- ...ure-various-clients-used-by-the-Heat.patch | 504 ++++++++++++++++++ rpm/SOURCES/Allow-Address-Pairs-feature.patch | 168 ++++++ rpm/SPECS/openstack-heat.spec | 4 +- 6 files changed, 1349 insertions(+), 2 deletions(-) create mode 100644 debian/patches/Ability-to-configure-various-clients-used-by-the-Heat.patch create mode 100644 debian/patches/Allow-Address-Pairs-feature.patch create mode 100644 rpm/SOURCES/Ability-to-configure-various-clients-used-by-the-Heat.patch create mode 100644 rpm/SOURCES/Allow-Address-Pairs-feature.patch 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 index 00000000..dad7d148 --- /dev/null +++ b/debian/patches/Ability-to-configure-various-clients-used-by-the-Heat.patch @@ -0,0 +1,504 @@ +From ac190f0da6ce367cc833b92677266e7bbf7e2270 Mon Sep 17 00:00:00 2001 +From: Timur Sufiev +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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= + ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= + +-[matchmaker_ring] ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= + +-# +-# 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= + + ++[clients_keystone] ++ ++# ++# Options defined in heat.common.config ++# ++ ++# Optional CA cert file to use in SSL connections (string ++# value) ++#ca_file= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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 index 00000000..105a2f6d --- /dev/null +++ b/debian/patches/Allow-Address-Pairs-feature.patch @@ -0,0 +1,168 @@ +From 94350f3206d067e7c9012ac5fb4c40555949d95c Mon Sep 17 00:00:00 2001 +From: Timur Sufiev +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 + diff --git a/debian/patches/series b/debian/patches/series index f1f090a3..7ac31d7d 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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 index 00000000..dad7d148 --- /dev/null +++ b/rpm/SOURCES/Ability-to-configure-various-clients-used-by-the-Heat.patch @@ -0,0 +1,504 @@ +From ac190f0da6ce367cc833b92677266e7bbf7e2270 Mon Sep 17 00:00:00 2001 +From: Timur Sufiev +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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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= + ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= + +-[matchmaker_ring] ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= + +-# +-# 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= + + ++[clients_keystone] ++ ++# ++# Options defined in heat.common.config ++# ++ ++# Optional CA cert file to use in SSL connections (string ++# value) ++#ca_file= ++ ++# Optional PEM-formatted certificate chain file (string value) ++#cert_file= ++ ++# Optional PEM-formatted file that contains the private key ++# (string value) ++#key_file= ++ ++# 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 index 00000000..105a2f6d --- /dev/null +++ b/rpm/SOURCES/Allow-Address-Pairs-feature.patch @@ -0,0 +1,168 @@ +From 94350f3206d067e7c9012ac5fb4c40555949d95c Mon Sep 17 00:00:00 2001 +From: Timur Sufiev +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 + diff --git a/rpm/SPECS/openstack-heat.spec b/rpm/SPECS/openstack-heat.spec index eebc2951..c899f8b7 100644 --- a/rpm/SPECS/openstack-heat.spec +++ b/rpm/SPECS/openstack-heat.spec @@ -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 -- 2.45.2