Detect neutron endpoint on-the-fly

This change does the following:
- reverts commit 04de60093b
- reimplements Resource.is_using_neutron to check for neutron by
  attempting to create a neutron client
- fix mocking in tests for changes which landed after 04de600

If there is no 'network' entry in the service catalog then
keystoneclient will raise an EndpointNotFound. The context will
already have a keystone client cached which has a full service catalog
locally, so calling is_using_neutron should have no particular overhead.

This fixes a tripleo regression where the autodetection is triggered
before keystone is ready, so that heat-engine fails to start. This
race does not affect devstack as keystone is fully configured before
heat services are started.

Not adding config option networking_service will also prevent
extra work required by downstream installation tools.

Change-Id: I45a6154fa560f672d8d1942bf57f39601110bfc6
Closes-Bug: #1362812
This commit is contained in:
Steve Baker 2014-08-29 11:11:59 +12:00
parent 99a11f8633
commit d70da16894
8 changed files with 32 additions and 42 deletions

View File

@ -46,31 +46,12 @@ gettextutils.install('heat', lazy=True)
LOG = logging.getLogger('heat.engine') LOG = logging.getLogger('heat.engine')
def discover_networking_service():
from heat.common import heat_keystoneclient as hkc
# create empty context
ctxt = hkc.context.RequestContext()
# create admin client
admin_client = hkc.KeystoneClient(ctxt).admin_client
services = admin_client.services.list()
if 'network' in [s.type for s in services]:
cfg.CONF.set_override('networking_service', 'neutron')
else:
cfg.CONF.set_override('networking_service', 'nova')
if __name__ == '__main__': if __name__ == '__main__':
cfg.CONF(project='heat', prog='heat-engine') cfg.CONF(project='heat', prog='heat-engine')
logging.setup('heat') logging.setup('heat')
messaging.setup() messaging.setup()
if not cfg.CONF.networking_service:
discover_networking_service()
from heat.engine import service as engine from heat.engine import service as engine
srv = engine.EngineService(cfg.CONF.host, rpc_api.ENGINE_TOPIC) srv = engine.EngineService(cfg.CONF.host, rpc_api.ENGINE_TOPIC)

View File

@ -73,10 +73,6 @@
# Deprecated. (string value) # Deprecated. (string value)
#onready=<None> #onready=<None>
# Select OpenStack component responsible for networking - nova
# or neutron. (string value)
#networking_service=<None>
# #
# Options defined in heat.common.config # Options defined in heat.common.config

View File

@ -138,11 +138,7 @@ engine_opts = [
help=_('RPC timeout for the engine liveness check that is used' help=_('RPC timeout for the engine liveness check that is used'
' for stack locking.')), ' for stack locking.')),
cfg.StrOpt('onready', cfg.StrOpt('onready',
help=_('Deprecated.')), help=_('Deprecated.'))]
cfg.StrOpt('networking_service',
choices=['nova', 'neutron'],
help=_('Select OpenStack component '
'responsible for networking - nova or neutron.'))]
rpc_opts = [ rpc_opts = [
cfg.StrOpt('host', cfg.StrOpt('host',

View File

@ -28,12 +28,14 @@ class NeutronClientPlugin(client_plugin.ClientPlugin):
con = self.context con = self.context
endpoint_type = self._get_client_option('neutron', 'endpoint_type') endpoint_type = self._get_client_option('neutron', 'endpoint_type')
endpoint = self.url_for(service_type='network',
endpoint_type=endpoint_type)
args = { args = {
'auth_url': con.auth_url, 'auth_url': con.auth_url,
'service_type': 'network', 'service_type': 'network',
'token': self.auth_token, 'token': self.auth_token,
'endpoint_url': self.url_for(service_type='network', 'endpoint_url': endpoint,
endpoint_type=endpoint_type),
'endpoint_type': endpoint_type, 'endpoint_type': endpoint_type,
'ca_cert': self._get_client_option('neutron', 'ca_file'), 'ca_cert': self._get_client_option('neutron', 'ca_file'),
'insecure': self._get_client_option('neutron', 'insecure') 'insecure': self._get_client_option('neutron', 'insecure')

View File

@ -1116,6 +1116,10 @@ class Resource(object):
self._data = None self._data = None
return True return True
@staticmethod def is_using_neutron(self):
def is_using_neutron(): try:
return cfg.CONF.networking_service == 'neutron' self.client('neutron')
except Exception:
return False
else:
return True

View File

@ -1002,9 +1002,17 @@ class ResourceTest(HeatTestCase):
snippet = rsrc_defn.ResourceDefinition('aresource', snippet = rsrc_defn.ResourceDefinition('aresource',
'GenericResourceType') 'GenericResourceType')
res = resource.Resource('aresource', snippet, self.stack) res = resource.Resource('aresource', snippet, self.stack)
cfg.CONF.set_override('networking_service', 'neutron') self.patch(
'heat.engine.clients.os.neutron.NeutronClientPlugin._create')
self.assertTrue(res.is_using_neutron()) self.assertTrue(res.is_using_neutron())
cfg.CONF.set_override('networking_service', 'nova')
def test_is_not_using_neutron(self):
snippet = rsrc_defn.ResourceDefinition('aresource',
'GenericResourceType')
res = resource.Resource('aresource', snippet, self.stack)
mock_create = self.patch(
'heat.engine.clients.os.neutron.NeutronClientPlugin._create')
mock_create.side_effect = Exception()
self.assertFalse(res.is_using_neutron()) self.assertFalse(res.is_using_neutron())

View File

@ -13,11 +13,11 @@
import collections import collections
from keystoneclient import exceptions as keystone_exc
from neutronclient.common.exceptions import NeutronClientException from neutronclient.common.exceptions import NeutronClientException
from neutronclient.v2_0 import client as neutronclient from neutronclient.v2_0 import client as neutronclient
from novaclient.v1_1 import security_group_rules as nova_sgr from novaclient.v1_1 import security_group_rules as nova_sgr
from novaclient.v1_1 import security_groups as nova_sg from novaclient.v1_1 import security_groups as nova_sg
from oslo.config import cfg
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
@ -145,6 +145,11 @@ Resources:
neutronclient.Client, 'delete_security_group_rule') neutronclient.Client, 'delete_security_group_rule')
self.m.StubOutWithMock(neutronclient.Client, 'delete_security_group') self.m.StubOutWithMock(neutronclient.Client, 'delete_security_group')
def mock_no_neutron(self):
mock_create = self.patch(
'heat.engine.clients.os.neutron.NeutronClientPlugin._create')
mock_create.side_effect = keystone_exc.EndpointNotFound()
def create_stack(self, templ): def create_stack(self, templ):
self.stack = self.parse_stack(template_format.parse(templ)) self.stack = self.parse_stack(template_format.parse(templ))
self.assertIsNone(self.stack.create()) self.assertIsNone(self.stack.create())
@ -166,6 +171,7 @@ Resources:
def test_security_group_nova(self): def test_security_group_nova(self):
#create script #create script
self.mock_no_neutron()
nova.NovaClientPlugin._create().AndReturn(self.fc) nova.NovaClientPlugin._create().AndReturn(self.fc)
nova_sg.SecurityGroupManager.list().AndReturn([NovaSG( nova_sg.SecurityGroupManager.list().AndReturn([NovaSG(
id=1, id=1,
@ -258,6 +264,7 @@ Resources:
def test_security_group_nova_bad_source_group(self): def test_security_group_nova_bad_source_group(self):
#create script #create script
self.mock_no_neutron()
nova.NovaClientPlugin._create().AndReturn(self.fc) nova.NovaClientPlugin._create().AndReturn(self.fc)
nova_sg.SecurityGroupManager.list().AndReturn([NovaSG( nova_sg.SecurityGroupManager.list().AndReturn([NovaSG(
id=1, id=1,
@ -322,6 +329,7 @@ Resources:
def test_security_group_client_exception(self): def test_security_group_client_exception(self):
#create script #create script
self.mock_no_neutron()
nova.NovaClientPlugin._create().AndReturn(self.fc) nova.NovaClientPlugin._create().AndReturn(self.fc)
sg_name = utils.PhysName('test_stack', 'the_sg') sg_name = utils.PhysName('test_stack', 'the_sg')
nova_sg.SecurityGroupManager.list().AndReturn([ nova_sg.SecurityGroupManager.list().AndReturn([
@ -430,6 +438,7 @@ Resources:
self.m.VerifyAll() self.m.VerifyAll()
def test_security_group_nova_with_egress_rules(self): def test_security_group_nova_with_egress_rules(self):
self.mock_no_neutron()
t = template_format.parse(self.test_template_nova_with_egress) t = template_format.parse(self.test_template_nova_with_egress)
stack = self.parse_stack(t) stack = self.parse_stack(t)
@ -437,7 +446,6 @@ Resources:
self.assertRaises(exception.EgressRuleNotAllowed, sg.validate) self.assertRaises(exception.EgressRuleNotAllowed, sg.validate)
def test_security_group_neutron(self): def test_security_group_neutron(self):
cfg.CONF.set_override('networking_service', 'neutron')
#create script #create script
sg_name = utils.PhysName('test_stack', 'the_sg') sg_name = utils.PhysName('test_stack', 'the_sg')
neutronclient.Client.create_security_group({ neutronclient.Client.create_security_group({
@ -683,7 +691,6 @@ Resources:
self.m.VerifyAll() self.m.VerifyAll()
def test_security_group_neutron_exception(self): def test_security_group_neutron_exception(self):
cfg.CONF.set_override('networking_service', 'neutron')
#create script #create script
sg_name = utils.PhysName('test_stack', 'the_sg') sg_name = utils.PhysName('test_stack', 'the_sg')
neutronclient.Client.create_security_group({ neutronclient.Client.create_security_group({

View File

@ -11,8 +11,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo.config import cfg
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
from heat.engine import parser from heat.engine import parser
@ -577,7 +575,6 @@ Resources:
neutronclient.Client.delete_port('dddd').AndReturn(None) neutronclient.Client.delete_port('dddd').AndReturn(None)
def test_network_interface(self): def test_network_interface(self):
cfg.CONF.set_override('networking_service', 'neutron')
self.mock_create_security_group() self.mock_create_security_group()
self.mock_create_network() self.mock_create_network()
self.mock_create_subnet() self.mock_create_subnet()
@ -603,7 +600,6 @@ Resources:
self.m.VerifyAll() self.m.VerifyAll()
def test_network_interface_existing_groupset(self): def test_network_interface_existing_groupset(self):
cfg.CONF.set_override('networking_service', 'neutron')
self.m.StubOutWithMock(parser.Stack, 'resource_by_refid') self.m.StubOutWithMock(parser.Stack, 'resource_by_refid')
self.mock_create_security_group() self.mock_create_security_group()