Add heat patches and short README on how to apply them.

Change-Id: Ia1a63cf7ed520e43bb6cd7d9f9e5abb747b484e9
This commit is contained in:
Timur Sufiev 2013-12-20 21:48:48 +04:00
parent 5df216c9c1
commit 8f36087c08
3 changed files with 689 additions and 0 deletions

View File

@ -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

View File

@ -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

17
heat-patches/README.rst Normal file
View File

@ -0,0 +1,17 @@
How to apply Heat patches
==========================
1. Copy both patches from this dir to the machine with Openstack
installation.
2. Login to the machine with Openstack installation and gain root
rights.
2. Go to the location where the heat python package is installed:
cd /usr/lib/python2.7/dist-package
3. Apply each patch by issuing the following command:
patch -b -p1 < /path/to/the/patch-file
4. Restart the heat service:
service heat-engine restart