Added support of multi-region environment
Octavia does not support keystone regions and can choose an incorrect endpoint url when multiple regions provides same services. Change-Id: I5ea9de380419592920555a2c2fe7ac6f6935e700 Closes-Bug: #1487359
This commit is contained in:
parent
cdd03b28eb
commit
0ab265ff41
|
@ -11,8 +11,10 @@
|
|||
#
|
||||
# octavia_plugins = hot_plug_plugin
|
||||
|
||||
# nova_region_name =
|
||||
# Region in Identity service catalog to use for communication with the OpenStack services.
|
||||
#
|
||||
# os_region_name =
|
||||
|
||||
# Hostname to be used by the host machine for services running on it.
|
||||
# The default value is the hostname of the host machine.
|
||||
# host =
|
||||
|
@ -143,4 +145,4 @@
|
|||
# Cleanup interval for Deleted amphora
|
||||
# cleanup_interval = 30
|
||||
# Amphora expiry age in seconds. Default is 1 week
|
||||
# amphora_expiry_age = 604800
|
||||
# amphora_expiry_age = 604800
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutronclient.neutron import client as neutron_client
|
||||
from novaclient import client as nova_client
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from octavia.common import keystone
|
||||
from octavia.i18n import _LE
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NEUTRON_VERSION = '2.0'
|
||||
NOVA_VERSION = '2'
|
||||
|
||||
|
||||
class NovaAuth(object):
|
||||
nova_client = None
|
||||
|
||||
@classmethod
|
||||
def get_nova_client(cls, region):
|
||||
"""Create nova client object.
|
||||
|
||||
:param region: The region of the service
|
||||
:return: a Nova Client object.
|
||||
:raises Exception: if the client cannot be created
|
||||
"""
|
||||
if not cls.nova_client:
|
||||
try:
|
||||
cls.nova_client = nova_client.Client(
|
||||
NOVA_VERSION, session=keystone.get_session(),
|
||||
region_name=region
|
||||
)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Nova client."))
|
||||
return cls.nova_client
|
||||
|
||||
|
||||
class NeutronAuth(object):
|
||||
neutron_client = None
|
||||
|
||||
@classmethod
|
||||
def get_neutron_client(cls, region):
|
||||
"""Create neutron client object.
|
||||
|
||||
:param region: The region of the service
|
||||
:return: a Neutron Client object.
|
||||
:raises Exception: if the client cannot be created
|
||||
"""
|
||||
if not cls.neutron_client:
|
||||
try:
|
||||
cls.neutron_client = neutron_client.Client(
|
||||
NEUTRON_VERSION, session=keystone.get_session(),
|
||||
region_name=region
|
||||
)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Neutron client."))
|
||||
return cls.neutron_client
|
|
@ -72,9 +72,9 @@ core_opts = [
|
|||
help=_('CA file for novaclient to verify server certificates')),
|
||||
cfg.BoolOpt('nova_api_insecure', default=False,
|
||||
help=_("If True, ignore any SSL validation issues")),
|
||||
cfg.StrOpt('nova_region_name',
|
||||
help=_('Name of nova region to use. Useful if keystone manages'
|
||||
' more than one region.')),
|
||||
cfg.StrOpt('os_region_name',
|
||||
help=_('Region in Identity service catalog to use for '
|
||||
'communication with the OpenStack services.')),
|
||||
cfg.StrOpt('octavia_plugins',
|
||||
default='hot_plug_plugin',
|
||||
help=_('Name of the controller plugin to use'))
|
||||
|
|
|
@ -12,15 +12,13 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import client as nova_client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from octavia.common import clients
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models as models
|
||||
from octavia.common import exceptions
|
||||
from octavia.common import keystone
|
||||
from octavia.compute import compute_base
|
||||
from octavia.i18n import _LE
|
||||
|
||||
|
@ -37,7 +35,7 @@ class VirtualMachineManager(compute_base.ComputeBase):
|
|||
def __init__(self, region=None):
|
||||
super(VirtualMachineManager, self).__init__()
|
||||
# Must initialize nova api
|
||||
self._nova_client = NovaAuth.get_nova_client(region)
|
||||
self._nova_client = clients.NovaAuth.get_nova_client(region)
|
||||
self.manager = self._nova_client.servers
|
||||
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
|
@ -144,26 +142,3 @@ class VirtualMachineManager(compute_base.ComputeBase):
|
|||
lb_network_ip=lb_network_ip
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
class NovaAuth(object):
|
||||
_nova_client = None
|
||||
|
||||
@classmethod
|
||||
def get_nova_client(cls, region):
|
||||
"""Create nova client object.
|
||||
|
||||
:param region: The region of the service
|
||||
:return: a Nova Client object.
|
||||
:raises Exception: if the client cannot be created
|
||||
"""
|
||||
if not cls._nova_client:
|
||||
try:
|
||||
cls._nova_client = nova_client.Client(
|
||||
constants.NOVA_2, session=keystone.get_session(),
|
||||
region_name=region
|
||||
)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Nova client."))
|
||||
return cls._nova_client
|
||||
|
|
|
@ -39,7 +39,7 @@ class BaseComputeTask(task.Task):
|
|||
namespace='octavia.compute.drivers',
|
||||
name=CONF.controller_worker.compute_driver,
|
||||
invoke_on_load=True,
|
||||
invoke_kwds={'region': CONF.nova_region_name}
|
||||
invoke_kwds={'region': CONF.os_region_name}
|
||||
).driver
|
||||
|
||||
|
||||
|
|
|
@ -40,7 +40,8 @@ class BaseNetworkTask(task.Task):
|
|||
self.network_driver = stevedore_driver.DriverManager(
|
||||
namespace='octavia.network.drivers',
|
||||
name=CONF.controller_worker.network_driver,
|
||||
invoke_on_load=True
|
||||
invoke_on_load=True,
|
||||
invoke_kwds={'region': CONF.os_region_name}
|
||||
).driver
|
||||
|
||||
|
||||
|
@ -341,4 +342,4 @@ class GetAmphoraeNetworkConfigs(BaseNetworkTask):
|
|||
ha_subnet=ha_subnet,
|
||||
ha_port=ha_port
|
||||
)
|
||||
return amp_net_configs
|
||||
return amp_net_configs
|
||||
|
|
|
@ -13,11 +13,10 @@
|
|||
# under the License.
|
||||
|
||||
from neutronclient.common import exceptions as neutron_client_exceptions
|
||||
from neutronclient.neutron import client as neutron_client
|
||||
from oslo_log import log as logging
|
||||
|
||||
from octavia.common import clients
|
||||
from octavia.common import data_models
|
||||
from octavia.common import keystone
|
||||
from octavia.i18n import _LE, _LI
|
||||
from octavia.network import base
|
||||
from octavia.network import data_models as network_models
|
||||
|
@ -25,16 +24,14 @@ from octavia.network.drivers.neutron import utils
|
|||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NEUTRON_VERSION = '2.0'
|
||||
SEC_GRP_EXT_ALIAS = 'security-group'
|
||||
|
||||
|
||||
class BaseNeutronDriver(base.AbstractNetworkDriver):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, region=None):
|
||||
self.sec_grp_enabled = True
|
||||
self.neutron_client = neutron_client.Client(
|
||||
NEUTRON_VERSION, session=keystone.get_session())
|
||||
self.neutron_client = clients.NeutronAuth.get_neutron_client(region)
|
||||
extensions = self.neutron_client.list_extensions()
|
||||
self._extensions = extensions.get('extensions')
|
||||
self._check_sec_grps()
|
||||
|
@ -172,4 +169,4 @@ class BaseNeutronDriver(base.AbstractNetworkDriver):
|
|||
'(port id: {port_id}.').format(
|
||||
port_id=port_id)
|
||||
LOG.exception(message)
|
||||
raise base.NetworkException(message)
|
||||
raise base.NetworkException(message)
|
||||
|
|
|
@ -94,7 +94,7 @@ class NoopManager(object):
|
|||
|
||||
|
||||
class NoopNetworkDriver(driver_base.AbstractNetworkDriver):
|
||||
def __init__(self):
|
||||
def __init__(self, region=None):
|
||||
super(NoopNetworkDriver, self).__init__()
|
||||
self.driver = NoopManager()
|
||||
|
||||
|
@ -130,4 +130,4 @@ class NoopNetworkDriver(driver_base.AbstractNetworkDriver):
|
|||
self.driver.get_subnet(subnet_id)
|
||||
|
||||
def get_port(self, port_id):
|
||||
self.driver.get_port(port_id)
|
||||
self.driver.get_port(port_id)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import mock
|
||||
import testtools
|
||||
|
||||
from octavia.compute.drivers import nova_driver
|
||||
from octavia.common import clients
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
|
@ -26,4 +26,5 @@ class TestCase(testtools.TestCase):
|
|||
self.addCleanup(self.clean_caches)
|
||||
|
||||
def clean_caches(self):
|
||||
nova_driver.NovaAuth._nova_client = None
|
||||
clients.NovaAuth.nova_client = None
|
||||
clients.NeutronAuth.neutron_client = None
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import neutronclient.v2_0
|
||||
import novaclient.v2
|
||||
from oslo_config import cfg
|
||||
|
||||
from octavia.common import clients
|
||||
from octavia.common import keystone
|
||||
from octavia.tests.unit import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestNovaAuth(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
CONF.set_override(group='keystone_authtoken', name='auth_version',
|
||||
override='2')
|
||||
# Reset the session and client
|
||||
clients.NovaAuth.nova_client = None
|
||||
keystone._SESSION = None
|
||||
|
||||
super(TestNovaAuth, self).setUp()
|
||||
|
||||
def test_get_nova_client(self):
|
||||
# There should be no existing client
|
||||
self.assertIsNone(
|
||||
clients.NovaAuth.nova_client
|
||||
)
|
||||
|
||||
# Mock out the keystone session and get the client
|
||||
keystone._SESSION = mock.MagicMock()
|
||||
bc1 = clients.NovaAuth.get_nova_client(region=None)
|
||||
|
||||
# Our returned client should also be the saved client
|
||||
self.assertIsInstance(
|
||||
clients.NovaAuth.nova_client,
|
||||
novaclient.v2.client.Client
|
||||
)
|
||||
self.assertIs(
|
||||
clients.NovaAuth.nova_client,
|
||||
bc1
|
||||
)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
bc2 = clients.NovaAuth.get_nova_client(
|
||||
region="test-region")
|
||||
self.assertIs(bc1, bc2)
|
||||
|
||||
|
||||
class TestNeutronAuth(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
CONF.set_override(group='keystone_authtoken', name='auth_version',
|
||||
override='2')
|
||||
# Reset the session and client
|
||||
clients.NeutronAuth.neutron_client = None
|
||||
keystone._SESSION = None
|
||||
|
||||
super(TestNeutronAuth, self).setUp()
|
||||
|
||||
def test_get_neutron_client(self):
|
||||
# There should be no existing client
|
||||
self.assertIsNone(
|
||||
clients.NeutronAuth.neutron_client
|
||||
)
|
||||
|
||||
# Mock out the keystone session and get the client
|
||||
keystone._SESSION = mock.MagicMock()
|
||||
bc1 = clients.NeutronAuth.get_neutron_client(region=None)
|
||||
|
||||
# Our returned client should also be the saved client
|
||||
self.assertIsInstance(
|
||||
clients.NeutronAuth.neutron_client,
|
||||
neutronclient.v2_0.client.Client
|
||||
)
|
||||
self.assertIs(
|
||||
clients.NeutronAuth.neutron_client,
|
||||
bc1
|
||||
)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
bc2 = clients.NeutronAuth.get_neutron_client(
|
||||
region="test-region")
|
||||
self.assertIs(bc1, bc2)
|
|
@ -12,8 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystoneclient import session
|
||||
import novaclient.v2
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
|
@ -21,7 +19,6 @@ import six
|
|||
from octavia.common import constants
|
||||
from octavia.common import data_models as models
|
||||
from octavia.common import exceptions
|
||||
from octavia.common import keystone
|
||||
import octavia.compute.drivers.nova_driver as nova_common
|
||||
import octavia.tests.unit.base as base
|
||||
|
||||
|
@ -123,55 +120,3 @@ class TestNovaClient(base.TestCase):
|
|||
self.manager.manager.get.side_effect = Exception
|
||||
self.assertRaises(exceptions.ComputeGetException,
|
||||
self.manager.get_amphora, self.amphora.id)
|
||||
|
||||
|
||||
class TestNovaAuth(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
CONF.set_override(group='keystone_authtoken', name='auth_version',
|
||||
override='2')
|
||||
# Reset the session and client
|
||||
nova_common.NovaAuth._nova_client = None
|
||||
keystone._SESSION = None
|
||||
|
||||
super(TestNovaAuth, self).setUp()
|
||||
|
||||
def test_get_keystone_client(self):
|
||||
# There should be no existing session
|
||||
self.assertIsNone(keystone._SESSION)
|
||||
|
||||
# Get us a session
|
||||
ks1 = keystone.get_session()
|
||||
|
||||
# Our returned session should also be the saved session
|
||||
self.assertIsInstance(keystone._SESSION, session.Session)
|
||||
self.assertIs(keystone._SESSION, ks1)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
ks2 = keystone.get_session()
|
||||
self.assertIs(ks1, ks2)
|
||||
|
||||
def test_get_nova_client(self):
|
||||
# There should be no existing client
|
||||
self.assertIsNone(
|
||||
nova_common.NovaAuth._nova_client
|
||||
)
|
||||
|
||||
# Mock out the keystone session and get the client
|
||||
keystone._SESSION = mock.MagicMock()
|
||||
bc1 = nova_common.NovaAuth.get_nova_client(region=None)
|
||||
|
||||
# Our returned client should also be the saved client
|
||||
self.assertIsInstance(
|
||||
nova_common.NovaAuth._nova_client,
|
||||
novaclient.v2.client.Client
|
||||
)
|
||||
self.assertIs(
|
||||
nova_common.NovaAuth._nova_client,
|
||||
bc1
|
||||
)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
bc2 = nova_common.NovaAuth.get_nova_client(
|
||||
region="test-region")
|
||||
self.assertIs(bc1, bc2)
|
||||
|
|
|
@ -18,6 +18,7 @@ import mock
|
|||
from neutronclient.common import exceptions as neutron_exceptions
|
||||
from novaclient.client import exceptions as nova_exceptions
|
||||
|
||||
from octavia.common import clients
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models
|
||||
from octavia.network import base as network_base
|
||||
|
@ -35,10 +36,11 @@ class TestAllowedAddressPairsDriver(base.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(TestAllowedAddressPairsDriver, self).setUp()
|
||||
with mock.patch('neutronclient.neutron.client.Client',
|
||||
with mock.patch('octavia.common.clients.neutron_client.Client',
|
||||
autospec=True) as neutron_client:
|
||||
with mock.patch('novaclient.client.Client', autospec=True):
|
||||
client = neutron_client(neutron_base.NEUTRON_VERSION)
|
||||
with mock.patch('octavia.common.clients.nova_client.Client',
|
||||
autospec=True):
|
||||
client = neutron_client(clients.NEUTRON_VERSION)
|
||||
client.list_extensions.return_value = {
|
||||
'extensions': [
|
||||
{'alias': allowed_address_pairs.AAP_EXT_ALIAS},
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import mock
|
||||
|
||||
from octavia.common import clients
|
||||
from octavia.common import data_models
|
||||
from octavia.network import data_models as network_models
|
||||
from octavia.network.drivers.neutron import base as neutron_base
|
||||
|
@ -37,9 +38,9 @@ class TestBaseNeutronNetworkDriver(base.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(TestBaseNeutronNetworkDriver, self).setUp()
|
||||
with mock.patch('neutronclient.neutron.client.Client',
|
||||
with mock.patch('octavia.common.clients.neutron_client.Client',
|
||||
autospec=True) as neutron_client:
|
||||
client = neutron_client(neutron_base.NEUTRON_VERSION)
|
||||
client = neutron_client(clients.NEUTRON_VERSION)
|
||||
client.list_extensions.return_value = {
|
||||
'extensions': [
|
||||
{'alias': neutron_base.SEC_GRP_EXT_ALIAS}
|
||||
|
|
Loading…
Reference in New Issue