Nova driver implementation
Added nova_driver for amphora creation through nova Added amphora types list and an entry for Virtual Machine type to constants Added nova version list and available versions to constants Added amphora status list and UP/DOWN statuses to constants Added to amphora data_model for reuse in response from nova_driver Added testing for nova_driver Change-Id: I6c45dae5dbdd39515f9db02e8765d68871da2762 Partially-Implements: blueprint nova-compute-driver
This commit is contained in:
parent
9b989e2f8a
commit
0053509489
|
@ -36,3 +36,7 @@
|
|||
# ca_private_key_passphrase =
|
||||
# signing_digest = sha265
|
||||
# storage_path = /var/lib/octavia/certificates/
|
||||
|
||||
[networking]
|
||||
# Network to communicate with amphora
|
||||
# lb_network_id =
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from wsme import types as wtypes
|
||||
|
||||
from octavia.api.v1.types import base
|
||||
|
|
|
@ -82,11 +82,16 @@ keystone_authtoken_opts = [
|
|||
cfg.StrOpt('admin_project_id'),
|
||||
]
|
||||
|
||||
networking_opts = [
|
||||
cfg.StrOpt('lb_network_id', help=_('ID of amphora internal network')),
|
||||
]
|
||||
|
||||
core_cli_opts = []
|
||||
|
||||
# Register the configuration options
|
||||
cfg.CONF.register_opts(core_opts)
|
||||
cfg.CONF.register_opts(keystone_authtoken_opts, group='keystone_authtoken')
|
||||
cfg.CONF.register_opts(networking_opts, group='networking')
|
||||
cfg.CONF.register_cli_opts(core_cli_opts)
|
||||
|
||||
# Ensure that the control exchange is set correctly
|
||||
|
|
|
@ -53,4 +53,13 @@ ERROR = 'ERROR'
|
|||
SUPPORTED_OPERATING_STATUSES = (ONLINE, OFFLINE, DEGRADED, ERROR)
|
||||
|
||||
AMPHORA_VM = 'VM'
|
||||
AMPHORA_TYPES = (AMPHORA_VM, )
|
||||
SUPPORTED_AMPHORA_TYPES = (AMPHORA_VM,)
|
||||
|
||||
AMPHORA_UP = 'UP'
|
||||
AMPHORA_DOWN = 'DOWN'
|
||||
SUPPORTED_AMPHORA_STATUSES = (AMPHORA_UP, AMPHORA_DOWN)
|
||||
|
||||
NOVA_1 = '1.1'
|
||||
NOVA_2 = '2'
|
||||
NOVA_3 = '3'
|
||||
NOVA_VERSIONS = (NOVA_1, NOVA_2, NOVA_3)
|
||||
|
|
|
@ -193,9 +193,10 @@ class SNI(BaseDataModel):
|
|||
class Amphora(BaseDataModel):
|
||||
|
||||
def __init__(self, id=None, load_balancer_id=None, compute_id=None,
|
||||
status=None, load_balancer=None):
|
||||
status=None, lb_network_ip=None, load_balancer=None):
|
||||
self.id = id
|
||||
self.load_balancer_id = load_balancer_id
|
||||
self.compute_id = compute_id
|
||||
self.status = status
|
||||
self.lb_network_ip = lb_network_ip
|
||||
self.load_balancer = load_balancer
|
||||
|
|
|
@ -20,6 +20,8 @@ Octavia base exception handling.
|
|||
from oslo.utils import excutils
|
||||
from webob import exc
|
||||
|
||||
from octavia.i18n import _LE
|
||||
|
||||
|
||||
class OctaviaException(Exception):
|
||||
"""Base Octavia Exception.
|
||||
|
@ -112,3 +114,19 @@ class DuplicatePoolEntry(APIException):
|
|||
class ImmutableObject(APIException):
|
||||
msg = _("%(resource)s %(id)s is immutable and cannot be updated.")
|
||||
code = 409
|
||||
|
||||
|
||||
class ComputeBuildException(OctaviaException):
|
||||
message = _LE('Failed to build nova instance.')
|
||||
|
||||
|
||||
class ComputeDeleteException(OctaviaException):
|
||||
message = _LE('Failed to delete nova instance.')
|
||||
|
||||
|
||||
class ComputeGetException(OctaviaException):
|
||||
message = _LE('Failed to retrieve nova instance.')
|
||||
|
||||
|
||||
class ComputeStatusException(OctaviaException):
|
||||
message = _LE('Failed to retrieve nova instance status.')
|
||||
|
|
|
@ -16,8 +16,6 @@ import abc
|
|||
|
||||
import six
|
||||
|
||||
import octavia.common.constants as constants
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ComputeBase(object):
|
||||
|
@ -31,16 +29,15 @@ class ComputeBase(object):
|
|||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def build(self, amphora_type=constants.AMPHORA_VM, amphora_flavor=None,
|
||||
image_id=None, keys=None, sec_groups=None, network_ids=None):
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
key_name=None, sec_groups=None, network_ids=None):
|
||||
"""Build a new amphora.
|
||||
|
||||
:param amphora_type: The type of amphora to create (ex: VM)
|
||||
:param name: Optional name for Amphora
|
||||
:param amphora_flavor: Optionally specify a flavor
|
||||
:param image_id: ID of the base image for the amphora instance
|
||||
:param keys: Optionally specify a list of ssh public keys
|
||||
:param sec_groups: Optionally specify list of security
|
||||
groups
|
||||
:param key_name: Optionally specify a keypair
|
||||
:param sec_groups: Optionally specify list of security groups
|
||||
:param network_ids: A list of network IDs to attach to the amphora
|
||||
:returns: The id of the new instance.
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# 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 keystoneclient.auth.identity import v3 as keystone_client
|
||||
from keystoneclient import session
|
||||
from novaclient import client as nova_client
|
||||
from oslo.config import cfg
|
||||
from oslo.utils import excutils
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models as models
|
||||
from octavia.common import exceptions
|
||||
from octavia.compute import compute_base
|
||||
from octavia.i18n import _LE
|
||||
from octavia.openstack.common import log
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_group('keystone_authtoken', 'octavia.common.config')
|
||||
CONF.import_group('networking', 'octavia.common.config')
|
||||
|
||||
|
||||
class VirtualMachineManager(compute_base.ComputeBase):
|
||||
'''Compute implementation of virtual machines via nova.'''
|
||||
|
||||
def __init__(self):
|
||||
super(VirtualMachineManager, self).__init__()
|
||||
# Must initialize nova api
|
||||
self._nova_client = NovaKeystoneAuth.get_nova_client()
|
||||
self.manager = self._nova_client.servers
|
||||
|
||||
def get_logger(self):
|
||||
'''Retrieve a custom logger.'''
|
||||
pass
|
||||
|
||||
def build(self, name="amphora_name", amphora_flavor=None, image_id=None,
|
||||
key_name=None, sec_groups=None, network_ids=None):
|
||||
'''Create a new virtual machine.
|
||||
|
||||
:param name: optional name for amphora
|
||||
:param amphora_flavor: image flavor for virtual machine
|
||||
:param image_id: image ID for virtual machine
|
||||
:param key_name: keypair to add to the virtual machine
|
||||
:param sec_groups: Security group IDs for virtual machine
|
||||
:param network_ids: Network IDs to include on virtual machine
|
||||
:raises NovaBuildException: if nova failed to build virtual machine
|
||||
:returns: UUID of amphora
|
||||
'''
|
||||
try:
|
||||
amphora = self.manager.create(
|
||||
name=name, image=image_id, flavor=amphora_flavor,
|
||||
key_name=key_name, security_groups=sec_groups,
|
||||
nics=network_ids)
|
||||
return amphora.get('id')
|
||||
except Exception:
|
||||
LOG.exception(_LE("Error building nova virtual machine."))
|
||||
raise exceptions.ComputeBuildException()
|
||||
|
||||
def delete(self, amphora_id):
|
||||
'''Delete a virtual machine.
|
||||
|
||||
:param amphora_id: virtual machine UUID
|
||||
'''
|
||||
try:
|
||||
self.manager.delete(server=amphora_id)
|
||||
except Exception:
|
||||
LOG.exception(_LE("Error deleting nova virtual machine."))
|
||||
raise exceptions.ComputeDeleteException()
|
||||
|
||||
def status(self, amphora_id):
|
||||
'''Retrieve the status of a virtual machine.
|
||||
|
||||
:param amphora_id: virtual machine UUID
|
||||
:returns: constant of amphora status
|
||||
'''
|
||||
try:
|
||||
if self.get_amphora(amphora_id=amphora_id):
|
||||
return constants.AMPHORA_UP
|
||||
except Exception:
|
||||
LOG.exception(_LE("Error retrieving nova virtual machine status."))
|
||||
raise exceptions.ComputeStatusException()
|
||||
return constants.AMPHORA_DOWN
|
||||
|
||||
def get_amphora(self, amphora_id):
|
||||
'''Retrieve the information in nova of a virtual machine.
|
||||
|
||||
:param amphora_id: virtual machine UUID
|
||||
:returns: an amphora object
|
||||
'''
|
||||
# utilize nova client ServerManager 'get' method to retrieve info
|
||||
try:
|
||||
amphora = self.manager.get(amphora_id)
|
||||
except Exception:
|
||||
LOG.exception(_LE("Error retrieving nova virtual machine."))
|
||||
raise exceptions.ComputeGetException()
|
||||
return self._translate_amphora(amphora)
|
||||
|
||||
def _translate_amphora(self, nova_response):
|
||||
'''Convert a nova virtual machine into an amphora object.
|
||||
|
||||
:param nova_response: JSON response from nova
|
||||
:returns: an amphora object
|
||||
'''
|
||||
# Extract information from nova response to populate desired amphora
|
||||
# fields
|
||||
lb_network_ip = None
|
||||
for interface in nova_response.get('interface_list'):
|
||||
if interface.get('net_id') is CONF.networking.lb_network_id:
|
||||
lb_network_ip = interface.get('fixed_ips')[0].get('ip_address')
|
||||
response = models.Amphora(
|
||||
compute_id=nova_response.get('id'),
|
||||
status=nova_response.get('status'),
|
||||
lb_network_ip=lb_network_ip
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
class NovaKeystoneAuth(object):
|
||||
_keystone_session = None
|
||||
_nova_client = None
|
||||
|
||||
# TODO(rm_you): refactor for common availability
|
||||
@classmethod
|
||||
def _get_keystone_session(cls):
|
||||
"""Initializes a Keystone session.
|
||||
|
||||
:return: a Keystone Session object
|
||||
:raises Exception: if the session cannot be established
|
||||
"""
|
||||
if not cls._keystone_session:
|
||||
try:
|
||||
kc = keystone_client.Password(
|
||||
auth_url=CONF.keystone_authtoken.auth_uri,
|
||||
username=CONF.keystone_authtoken.admin_user,
|
||||
password=CONF.keystone_authtoken.admin_password,
|
||||
project_id=CONF.keystone_authtoken.admin_project_id
|
||||
)
|
||||
cls._keystone_session = session.Session(auth=kc)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Keystone session."))
|
||||
LOG.info()
|
||||
return cls._keystone_session
|
||||
|
||||
@classmethod
|
||||
def get_nova_client(cls):
|
||||
"""Create nova client object.
|
||||
|
||||
: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_3, session=cls._get_keystone_session()
|
||||
)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception(_LE("Error creating Nova client."))
|
||||
return cls._nova_client
|
|
@ -15,14 +15,14 @@
|
|||
"""update vip
|
||||
|
||||
Revision ID: 14892634e228
|
||||
Revises: 13500e2e978d
|
||||
Revises: 3a1e1cdb7b27
|
||||
Create Date: 2015-01-10 00:53:57.798213
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '14892634e228'
|
||||
down_revision = '13500e2e978d'
|
||||
down_revision = '3a1e1cdb7b27'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright 2015 Rackspace
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""add lb_network_ip to amphora
|
||||
|
||||
Revision ID: 256852d5ff7c
|
||||
Revises: 14892634e228
|
||||
Create Date: 2015-01-13 16:18:57.359290
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '256852d5ff7c'
|
||||
down_revision = '14892634e228'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column(u'amphora', sa.Column(u'lb_network_ip', sa.String(64),
|
||||
nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column(u'amphora', u'lb_network_ip')
|
|
@ -318,6 +318,7 @@ class Amphora(base_models.BASE):
|
|||
name="fk_amphora_load_balancer_id"),
|
||||
nullable=True)
|
||||
compute_id = sa.Column(sa.String(36), nullable=True)
|
||||
lb_network_ip = sa.Column(sa.String(64), nullable=True)
|
||||
status = sa.Column(
|
||||
sa.String(36),
|
||||
sa.ForeignKey("provisioning_status.name",
|
||||
|
|
|
@ -21,6 +21,7 @@ from octavia.tests.functional.db import base
|
|||
|
||||
class ModelTestMixin(object):
|
||||
|
||||
FAKE_IP = '10.0.0.1'
|
||||
FAKE_UUID_1 = uuidutils.generate_uuid()
|
||||
FAKE_UUID_2 = uuidutils.generate_uuid()
|
||||
|
||||
|
@ -114,7 +115,8 @@ class ModelTestMixin(object):
|
|||
def create_amphora(self, session, **overrides):
|
||||
kwargs = {'id': self.FAKE_UUID_1,
|
||||
'compute_id': self.FAKE_UUID_1,
|
||||
'status': constants.ACTIVE}
|
||||
'status': constants.ACTIVE,
|
||||
'lb_network_ip': self.FAKE_IP}
|
||||
kwargs.update(overrides)
|
||||
return self._insert(session, models.Amphora, kwargs)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from octavia.tests.functional.db import base
|
|||
|
||||
class BaseRepositoryTest(base.OctaviaDBTestBase):
|
||||
|
||||
FAKE_IP = "10.0.0.1"
|
||||
FAKE_UUID_1 = uuidutils.generate_uuid()
|
||||
FAKE_UUID_2 = uuidutils.generate_uuid()
|
||||
FAKE_UUID_3 = uuidutils.generate_uuid()
|
||||
|
@ -797,7 +798,8 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
|||
amphora = self.amphora_repo.create(self.session, id=self.FAKE_UUID_1,
|
||||
load_balancer_id=lb.id,
|
||||
compute_id=self.FAKE_UUID_3,
|
||||
status=constants.ACTIVE)
|
||||
status=constants.ACTIVE,
|
||||
lb_network_ip=self.FAKE_IP)
|
||||
new_lb = self.lb_repo.get(self.session, id=lb.id)
|
||||
self.assertIsNotNone(new_lb)
|
||||
self.assertEqual(1, len(new_lb.amphorae))
|
||||
|
@ -817,6 +819,7 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
|||
amphora_2 = self.amphora_repo.create(self.session, id=self.FAKE_UUID_3,
|
||||
load_balancer_id=lb.id,
|
||||
compute_id=self.FAKE_UUID_3,
|
||||
lb_network_ip=self.FAKE_IP,
|
||||
status=constants.ACTIVE)
|
||||
new_lb = self.lb_repo.get(self.session, id=lb.id)
|
||||
self.assertIsNotNone(new_lb)
|
||||
|
@ -895,6 +898,7 @@ class LoadBalancerRepositoryTest(BaseRepositoryTest):
|
|||
amphora = self.amphora_repo.create(self.session, id=self.FAKE_UUID_1,
|
||||
load_balancer_id=lb.id,
|
||||
compute_id=self.FAKE_UUID_3,
|
||||
lb_network_ip=self.FAKE_IP,
|
||||
status=constants.ACTIVE)
|
||||
vip = self.vip_repo.create(self.session, load_balancer_id=lb.id,
|
||||
ip_address="10.0.0.1")
|
||||
|
@ -1055,7 +1059,8 @@ class AmphoraRepositoryTest(BaseRepositoryTest):
|
|||
def create_amphora(self, amphora_id):
|
||||
amphora = self.amphora_repo.create(self.session, id=amphora_id,
|
||||
compute_id=self.FAKE_UUID_3,
|
||||
status=constants.ACTIVE)
|
||||
status=constants.ACTIVE,
|
||||
lb_network_ip=self.FAKE_IP)
|
||||
return amphora
|
||||
|
||||
def test_get(self):
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# 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 keystoneclient import session
|
||||
import mock
|
||||
import novaclient
|
||||
from oslo.config import cfg
|
||||
|
||||
from octavia.common import constants
|
||||
from octavia.common import data_models as models
|
||||
from octavia.common import exceptions
|
||||
import octavia.compute.drivers.nova_driver as nova_common
|
||||
from octavia.openstack.common import uuidutils
|
||||
import octavia.tests.unit.base as base
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_group('networking', 'octavia.common.config')
|
||||
|
||||
|
||||
class TestNovaClient(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
net_id = uuidutils.generate_uuid()
|
||||
CONF.set_override(group='networking', name='lb_network_id',
|
||||
override=net_id)
|
||||
self.amphora = models.Amphora(
|
||||
compute_id=uuidutils.generate_uuid(),
|
||||
status='ONLINE',
|
||||
lb_network_ip='10.0.0.1'
|
||||
)
|
||||
self.nova_response = {'id': self.amphora.compute_id,
|
||||
'type': constants.AMPHORA_VM,
|
||||
'image': uuidutils.generate_uuid(),
|
||||
'flavor': 1,
|
||||
'keys': [uuidutils.generate_uuid()],
|
||||
'security_groups': ['admin'],
|
||||
'nics': [uuidutils.generate_uuid()],
|
||||
'status': 'ONLINE',
|
||||
'interface_list': [{
|
||||
"fixed_ips": [
|
||||
{'ip_address': '10.0.0.1',
|
||||
'subnet_id': uuidutils.generate_uuid()}
|
||||
],
|
||||
'net_id': net_id}]}
|
||||
self.manager = nova_common.VirtualMachineManager()
|
||||
self.manager.manager = mock.MagicMock()
|
||||
self.manager.manager.get.return_value = self.nova_response
|
||||
self.manager.manager.create.return_value = self.nova_response
|
||||
super(TestNovaClient, self).setUp()
|
||||
|
||||
def test_build(self):
|
||||
amphora_id = self.manager.build(amphora_flavor=1, image_id=1,
|
||||
key_name=1, sec_groups=1,
|
||||
network_ids=1)
|
||||
self.assertEqual(self.amphora.compute_id, amphora_id)
|
||||
self.manager.manager.create.assert_called_with(
|
||||
name="amphora_name", image=1, flavor=1, key_name=1,
|
||||
security_groups=1, nics=1
|
||||
)
|
||||
|
||||
def test_bad_build(self):
|
||||
self.manager.manager.create.side_effect = Exception
|
||||
self.assertRaises(exceptions.ComputeBuildException, self.manager.build)
|
||||
|
||||
def test_delete(self):
|
||||
amphora_id = self.manager.build(amphora_flavor=1, image_id=1,
|
||||
key_name=1, sec_groups=1,
|
||||
network_ids=1)
|
||||
self.manager.delete(amphora_id)
|
||||
self.manager.manager.delete.assert_called_with(server=amphora_id)
|
||||
|
||||
def test_bad_delete(self):
|
||||
self.manager.manager.delete.side_effect = Exception
|
||||
amphora_id = self.manager.build(amphora_flavor=1, image_id=1,
|
||||
key_name=1, sec_groups=1,
|
||||
network_ids=1)
|
||||
self.assertRaises(exceptions.ComputeDeleteException,
|
||||
self.manager.delete, amphora_id)
|
||||
|
||||
def test_status(self):
|
||||
status = self.manager.status(self.amphora.id)
|
||||
self.assertEqual(constants.AMPHORA_UP, status)
|
||||
|
||||
def test_bad_status(self):
|
||||
self.manager.manager.get.side_effect = Exception
|
||||
self.assertRaises(exceptions.ComputeStatusException,
|
||||
self.manager.status, self.amphora.id)
|
||||
|
||||
def test_get_amphora(self):
|
||||
amphora = self.manager.get_amphora(self.amphora.compute_id)
|
||||
self.assertEqual(self.amphora, amphora)
|
||||
self.manager.manager.get.called_with(server=amphora.id)
|
||||
|
||||
def test_bad_get_amphora(self):
|
||||
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):
|
||||
# Reset the session and client
|
||||
nova_common.NovaKeystoneAuth._keystone_session = None
|
||||
nova_common.NovaKeystoneAuth._nova_client = None
|
||||
|
||||
super(TestNovaAuth, self).setUp()
|
||||
|
||||
def test_get_keystone_client(self):
|
||||
# There should be no existing session
|
||||
self.assertIsNone(
|
||||
nova_common.NovaKeystoneAuth._keystone_session
|
||||
)
|
||||
|
||||
# Get us a session
|
||||
ks1 = nova_common.NovaKeystoneAuth._get_keystone_session()
|
||||
|
||||
# Our returned session should also be the saved session
|
||||
self.assertIsInstance(
|
||||
nova_common.NovaKeystoneAuth._keystone_session,
|
||||
session.Session
|
||||
)
|
||||
self.assertIs(
|
||||
nova_common.NovaKeystoneAuth._keystone_session,
|
||||
ks1
|
||||
)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
ks2 = nova_common.NovaKeystoneAuth._get_keystone_session()
|
||||
self.assertIs(ks1, ks2)
|
||||
|
||||
def test_get_nova_client(self):
|
||||
# There should be no existing client
|
||||
self.assertIsNone(
|
||||
nova_common.NovaKeystoneAuth._nova_client
|
||||
)
|
||||
|
||||
# Mock out the keystone session and get the client
|
||||
nova_common.NovaKeystoneAuth._keystone_session = (
|
||||
mock.MagicMock()
|
||||
)
|
||||
bc1 = nova_common.NovaKeystoneAuth.get_nova_client()
|
||||
|
||||
# Our returned client should also be the saved client
|
||||
self.assertIsInstance(
|
||||
nova_common.NovaKeystoneAuth._nova_client,
|
||||
novaclient.v3.client.Client
|
||||
)
|
||||
self.assertIs(
|
||||
nova_common.NovaKeystoneAuth._nova_client,
|
||||
bc1
|
||||
)
|
||||
|
||||
# Getting the session again should return the same object
|
||||
bc2 = nova_common.NovaKeystoneAuth.get_nova_client()
|
||||
self.assertIs(bc1, bc2)
|
|
@ -1,5 +1,5 @@
|
|||
actdiag
|
||||
alembic>=0.6.4
|
||||
alembic>=0.7.1
|
||||
blockdiag
|
||||
docutils==0.11
|
||||
nwdiag
|
||||
|
|
Loading…
Reference in New Issue