diff --git a/etc/heat/heat.conf.sample b/etc/heat/heat.conf.sample index 6ecc493e7a9..bc490d1ff85 100644 --- a/etc/heat/heat.conf.sample +++ b/etc/heat/heat.conf.sample @@ -465,6 +465,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] # @@ -560,6 +597,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] # @@ -788,29 +923,26 @@ #memcache_secret_key= -[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= -[matchmaker_ring] +# Optional PEM-formatted certificate chain file (string value) +#cert_file= -# -# Options defined in heat.openstack.common.rpc.matchmaker_ring -# +# Optional PEM-formatted file that contains the private key +# (string value) +#key_file= -# 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] @@ -829,3 +961,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 a6dc4462e5e..38240f837a3 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 @@ -124,6 +124,31 @@ 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(engine_opts) cfg.CONF.register_opts(service_opts) @@ -132,6 +157,7 @@ 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 43ca25b5402..664638e9ab7 100644 --- a/heat/common/heat_keystoneclient.py +++ b/heat/common/heat_keystoneclient.py @@ -100,6 +100,10 @@ def _v2_client_init(self): 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 @@ def _v3_client_init(self): "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 405533371a5..f987c573241 100644 --- a/heat/engine/clients.py +++ b/heat/engine/clients.py @@ -103,7 +103,9 @@ def nova(self, service_type='compute'): '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 @@ def swift(self): '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 @@ def neutron(self): '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 @@ def cinder(self): '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 @@ def ceilometer(self): '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 @@ def ceilometer(self): 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) + class ClientBackend(object): '''Delay choosing the backend client module until the client's class needs diff --git a/heat/tests/test_heatclient.py b/heat/tests/test_heatclient.py index 7e195dcfc05..712ffa52dbe 100644 --- a/heat/tests/test_heatclient.py +++ b/heat/tests/test_heatclient.py @@ -51,7 +51,11 @@ def _stubs_v2(self, method='token', auth_ok=True, 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 @@ def _stubs_v2(self, method='token', auth_ok=True, 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 @@ def _stubs_v3(self, method='token', auth_ok=True): 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 @@ def _stubs_v3(self, method='token', auth_ok=True): 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):