Create OpenStackClients convenience class
The OpenStackClients class provides a convenient way to create and cache client instances. The idea behind this code comes from Magnum [0]. The OpenStackClients class will act as the manager of other project's clients, providing an easy way to fetch instances of said clients. This will allow the clients to be cached. An instance of OpenStackClients is created for every call that comes into the decision engine and the applier, using the request context to pass needed (domain id) parameters to get a Keystone session. This instance should be shared as much as possible to avoid additional unneccessary connections to the other services. This class will also allow for the version of each client to be configurable via the watcher.conf file. The method by which a Keystone session is also changed to use the keystoneauth1.loading library. In order to avoid DuplicateOptErrors with the keystone_authtoken group used for the keystonemiddleware in the API code, a new conf group named "watcher_clients_auth" is created. A typical configuration using a password authentication scheme will look like: [watcher_clients_auth] auth_type = password auth_url = http://<server-ip>:<port> username = <username> password = <password> project_domain_id = default user_domain_id = default [0]: https://github.com/openstack/magnum/blob/master/magnum/common/clients.py DocImpact Change-Id: Iab9d0b304099686da2e9e2b19e8b1de4332ff378 Implements: blueprint external-api-versioning Closes-Bug: #1530790 Closes-Bug: #1539670 Closes-Bug: #1522774
This commit is contained in:
@@ -53,9 +53,9 @@ class FakeAction(abase.BaseAction):
|
||||
class TestDefaultWorkFlowEngine(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestDefaultWorkFlowEngine, self).setUp()
|
||||
self.engine = tflow.DefaultWorkFlowEngine()
|
||||
self.engine.context = self.context
|
||||
self.engine.applier_manager = mock.MagicMock()
|
||||
self.engine = tflow.DefaultWorkFlowEngine(
|
||||
context=self.context,
|
||||
applier_manager=mock.MagicMock())
|
||||
|
||||
def test_execute(self):
|
||||
actions = mock.MagicMock()
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from mock import MagicMock
|
||||
from mock import mock
|
||||
from oslo_config import cfg
|
||||
from watcher.common.ceilometer import CeilometerClient
|
||||
|
||||
from watcher.tests.base import BaseTestCase
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestCeilometer(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestCeilometer, self).setUp()
|
||||
self.cm = CeilometerClient()
|
||||
|
||||
def test_build_query(self):
|
||||
expected = [{'field': 'user_id', 'op': 'eq', 'value': u'user_id'},
|
||||
{'field': 'project_id', 'op': 'eq', 'value': u'tenant_id'},
|
||||
{'field': 'resource_id', 'op': 'eq',
|
||||
'value': u'resource_id'}]
|
||||
|
||||
query = self.cm.build_query(user_id="user_id",
|
||||
tenant_id="tenant_id",
|
||||
resource_id="resource_id",
|
||||
user_ids=["user_ids"],
|
||||
tenant_ids=["tenant_ids"],
|
||||
resource_ids=["resource_ids"])
|
||||
self.assertEqual(query, expected)
|
||||
|
||||
@mock.patch('keystoneclient.v2_0.client.Client', autospec=True)
|
||||
@mock.patch('ceilometerclient.v2.client.Client', autospec=True)
|
||||
def test_get_ceilometer_v2(self, mock_keystone, mock_ceilometer):
|
||||
cfg.CONF.set_override(
|
||||
'auth_uri', "http://127.0.0.1:9898/v2", group="keystone_authtoken",
|
||||
enforce_type=True
|
||||
)
|
||||
c = CeilometerClient(api_version='2')
|
||||
from ceilometerclient.v2 import Client
|
||||
self.assertIsInstance(c.cmclient, Client)
|
||||
|
||||
@mock.patch.object(CeilometerClient, "cmclient")
|
||||
def test_statistic_aggregation(self, mock_keystone):
|
||||
statistic = MagicMock()
|
||||
expected_result = 100
|
||||
statistic[-1]._info = {'aggregate': {'avg': expected_result}}
|
||||
mock_keystone.statistics.list.return_value = statistic
|
||||
val = self.cm.statistic_aggregation(
|
||||
resource_id="VM_ID",
|
||||
meter_name="cpu_util",
|
||||
period="7300"
|
||||
)
|
||||
self.assertEqual(val, expected_result)
|
||||
|
||||
@mock.patch.object(CeilometerClient, "cmclient")
|
||||
def test_get_last_sample(self, mock_keystone):
|
||||
statistic = MagicMock()
|
||||
expected_result = 100
|
||||
statistic[-1]._info = {'counter_volume': expected_result}
|
||||
mock_keystone.samples.list.return_value = statistic
|
||||
val = self.cm.get_last_sample_value(
|
||||
resource_id="id",
|
||||
meter_name="compute.node.percent"
|
||||
)
|
||||
self.assertEqual(val, expected_result)
|
||||
|
||||
@mock.patch.object(CeilometerClient, "cmclient")
|
||||
def test_get_last_sample_none(self, mock_keystone):
|
||||
expected = []
|
||||
mock_keystone.samples.list.return_value = expected
|
||||
val = self.cm.get_last_sample_values(
|
||||
resource_id="id",
|
||||
meter_name="compute.node.percent"
|
||||
)
|
||||
self.assertEqual(val, expected)
|
||||
|
||||
@mock.patch.object(CeilometerClient, "cmclient")
|
||||
def test_statistic_list(self, mock_keystone):
|
||||
expected_value = []
|
||||
mock_keystone.statistics.list.return_value = expected_value
|
||||
val = self.cm.statistic_list(meter_name="cpu_util")
|
||||
self.assertEqual(val, expected_value)
|
||||
98
watcher/tests/common/test_ceilometer_helper.py
Normal file
98
watcher/tests/common/test_ceilometer_helper.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common import ceilometer_helper
|
||||
from watcher.common import clients
|
||||
from watcher.tests import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'ceilometer')
|
||||
class TestCeilometerHelper(base.BaseTestCase):
|
||||
|
||||
def test_build_query(self, mock_ceilometer):
|
||||
mock_ceilometer.return_value = mock.MagicMock()
|
||||
cm = ceilometer_helper.CeilometerHelper()
|
||||
expected = [{'field': 'user_id', 'op': 'eq', 'value': u'user_id'},
|
||||
{'field': 'project_id', 'op': 'eq', 'value': u'tenant_id'},
|
||||
{'field': 'resource_id', 'op': 'eq',
|
||||
'value': u'resource_id'}]
|
||||
|
||||
query = cm.build_query(user_id="user_id",
|
||||
tenant_id="tenant_id",
|
||||
resource_id="resource_id",
|
||||
user_ids=["user_ids"],
|
||||
tenant_ids=["tenant_ids"],
|
||||
resource_ids=["resource_ids"])
|
||||
self.assertEqual(query, expected)
|
||||
|
||||
def test_statistic_aggregation(self, mock_ceilometer):
|
||||
cm = ceilometer_helper.CeilometerHelper()
|
||||
ceilometer = mock.MagicMock()
|
||||
statistic = mock.MagicMock()
|
||||
expected_result = 100
|
||||
statistic[-1]._info = {'aggregate': {'avg': expected_result}}
|
||||
ceilometer.statistics.list.return_value = statistic
|
||||
mock_ceilometer.return_value = ceilometer
|
||||
cm = ceilometer_helper.CeilometerHelper()
|
||||
val = cm.statistic_aggregation(
|
||||
resource_id="VM_ID",
|
||||
meter_name="cpu_util",
|
||||
period="7300"
|
||||
)
|
||||
self.assertEqual(val, expected_result)
|
||||
|
||||
def test_get_last_sample(self, mock_ceilometer):
|
||||
ceilometer = mock.MagicMock()
|
||||
statistic = mock.MagicMock()
|
||||
expected_result = 100
|
||||
statistic[-1]._info = {'counter_volume': expected_result}
|
||||
ceilometer.samples.list.return_value = statistic
|
||||
mock_ceilometer.return_value = ceilometer
|
||||
cm = ceilometer_helper.CeilometerHelper()
|
||||
val = cm.get_last_sample_value(
|
||||
resource_id="id",
|
||||
meter_name="compute.node.percent"
|
||||
)
|
||||
self.assertEqual(val, expected_result)
|
||||
|
||||
def test_get_last_sample_none(self, mock_ceilometer):
|
||||
ceilometer = mock.MagicMock()
|
||||
expected = []
|
||||
ceilometer.samples.list.return_value = expected
|
||||
mock_ceilometer.return_value = ceilometer
|
||||
cm = ceilometer_helper.CeilometerHelper()
|
||||
val = cm.get_last_sample_values(
|
||||
resource_id="id",
|
||||
meter_name="compute.node.percent"
|
||||
)
|
||||
self.assertEqual(val, expected)
|
||||
|
||||
def test_statistic_list(self, mock_ceilometer):
|
||||
ceilometer = mock.MagicMock()
|
||||
expected_value = []
|
||||
ceilometer.statistics.list.return_value = expected_value
|
||||
mock_ceilometer.return_value = ceilometer
|
||||
cm = ceilometer_helper.CeilometerHelper()
|
||||
val = cm.statistic_list(meter_name="cpu_util")
|
||||
self.assertEqual(val, expected_value)
|
||||
241
watcher/tests/common/test_clients.py
Normal file
241
watcher/tests/common/test_clients.py
Normal file
@@ -0,0 +1,241 @@
|
||||
# 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 ceilometerclient import client as ceclient
|
||||
import ceilometerclient.v2.client as ceclient_v2
|
||||
from cinderclient import client as ciclient
|
||||
from cinderclient.v1 import client as ciclient_v1
|
||||
from glanceclient import client as glclient
|
||||
from keystoneauth1 import loading as ka_loading
|
||||
import mock
|
||||
from neutronclient.neutron import client as netclient
|
||||
from neutronclient.v2_0 import client as netclient_v2
|
||||
from novaclient import client as nvclient
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common import clients
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestClients(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestClients, self).setUp()
|
||||
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='nova_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='glance_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='cinder_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='ceilometer_client')
|
||||
cfg.CONF.import_opt('api_version', 'watcher.common.clients',
|
||||
group='neutron_client')
|
||||
|
||||
def test_get_keystone_session(self):
|
||||
_AUTH_CONF_GROUP = 'watcher_clients_auth'
|
||||
ka_loading.register_auth_conf_options(cfg.CONF, _AUTH_CONF_GROUP)
|
||||
ka_loading.register_session_conf_options(cfg.CONF, _AUTH_CONF_GROUP)
|
||||
|
||||
cfg.CONF.set_override('auth_type', 'password',
|
||||
group=_AUTH_CONF_GROUP)
|
||||
|
||||
# If we don't clean up the _AUTH_CONF_GROUP conf options, then other
|
||||
# tests that run after this one will fail, complaining about required
|
||||
# options that _AUTH_CONF_GROUP wants.
|
||||
def cleanup_conf_from_loading():
|
||||
# oslo_config doesn't seem to allow unregistering groups through a
|
||||
# single method, so we do this instead
|
||||
cfg.CONF.reset()
|
||||
del cfg.CONF._groups[_AUTH_CONF_GROUP]
|
||||
|
||||
self.addCleanup(cleanup_conf_from_loading)
|
||||
|
||||
osc = clients.OpenStackClients()
|
||||
|
||||
expected = {'username': 'foousername',
|
||||
'password': 'foopassword',
|
||||
'auth_url': 'http://server.ip:35357',
|
||||
'user_domain_id': 'foouserdomainid',
|
||||
'project_domain_id': 'fooprojdomainid'}
|
||||
|
||||
def reset_register_opts_mock(conf_obj, original_method):
|
||||
conf_obj.register_opts = original_method
|
||||
|
||||
original_register_opts = cfg.CONF.register_opts
|
||||
self.addCleanup(reset_register_opts_mock,
|
||||
cfg.CONF,
|
||||
original_register_opts)
|
||||
|
||||
# Because some of the conf options for auth plugins are not registered
|
||||
# until right before they are loaded, and because the method that does
|
||||
# the actual loading of the conf option values is an anonymous method
|
||||
# (see _getter method of load_from_conf_options in
|
||||
# keystoneauth1.loading.conf.py), we need to manually monkey patch
|
||||
# the register opts method so that we can override the conf values to
|
||||
# our custom values.
|
||||
def mock_register_opts(*args, **kwargs):
|
||||
ret = original_register_opts(*args, **kwargs)
|
||||
if 'group' in kwargs and kwargs['group'] == _AUTH_CONF_GROUP:
|
||||
for key, value in expected.items():
|
||||
cfg.CONF.set_override(key, value, group=_AUTH_CONF_GROUP)
|
||||
return ret
|
||||
|
||||
cfg.CONF.register_opts = mock_register_opts
|
||||
|
||||
sess = osc.session
|
||||
self.assertEqual(expected['auth_url'], sess.auth.auth_url)
|
||||
self.assertEqual(expected['username'], sess.auth._username)
|
||||
self.assertEqual(expected['password'], sess.auth._password)
|
||||
self.assertEqual(expected['user_domain_id'], sess.auth._user_domain_id)
|
||||
self.assertEqual(expected['project_domain_id'],
|
||||
sess.auth._project_domain_id)
|
||||
|
||||
@mock.patch.object(nvclient, 'Client')
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_nova(self, mock_session, mock_call):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._nova = None
|
||||
osc.nova()
|
||||
mock_call.assert_called_once_with(cfg.CONF.nova_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_nova_diff_vers(self, mock_session):
|
||||
cfg.CONF.set_override('api_version', '2.3',
|
||||
group='nova_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._nova = None
|
||||
osc.nova()
|
||||
self.assertEqual('2.3', osc.nova().api_version.get_string())
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_nova_cached(self, mock_session):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._nova = None
|
||||
nova = osc.nova()
|
||||
nova_cached = osc.nova()
|
||||
self.assertEqual(nova, nova_cached)
|
||||
|
||||
@mock.patch.object(glclient, 'Client')
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_glance(self, mock_session, mock_call):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._glance = None
|
||||
osc.glance()
|
||||
mock_call.assert_called_once_with(cfg.CONF.glance_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_glance_diff_vers(self, mock_session):
|
||||
cfg.CONF.set_override('api_version', '1',
|
||||
group='glance_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._glance = None
|
||||
osc.glance()
|
||||
self.assertEqual(1.0, osc.glance().version)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_glance_cached(self, mock_session):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._glance = None
|
||||
glance = osc.glance()
|
||||
glance_cached = osc.glance()
|
||||
self.assertEqual(glance, glance_cached)
|
||||
|
||||
@mock.patch.object(ciclient, 'Client')
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_cinder(self, mock_session, mock_call):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._cinder = None
|
||||
osc.cinder()
|
||||
mock_call.assert_called_once_with(cfg.CONF.cinder_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_cinder_diff_vers(self, mock_session):
|
||||
cfg.CONF.set_override('api_version', '1',
|
||||
group='cinder_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._cinder = None
|
||||
osc.cinder()
|
||||
self.assertEqual(ciclient_v1.Client, type(osc.cinder()))
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_cinder_cached(self, mock_session):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._cinder = None
|
||||
cinder = osc.cinder()
|
||||
cinder_cached = osc.cinder()
|
||||
self.assertEqual(cinder, cinder_cached)
|
||||
|
||||
@mock.patch.object(ceclient, 'Client')
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_ceilometer(self, mock_session, mock_call):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._ceilometer = None
|
||||
osc.ceilometer()
|
||||
mock_call.assert_called_once_with(
|
||||
cfg.CONF.ceilometer_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
@mock.patch.object(ceclient_v2.Client, '_get_alarm_client')
|
||||
def test_clients_ceilometer_diff_vers(self, mock_get_alarm_client,
|
||||
mock_session):
|
||||
'''ceilometerclient currently only has one version (v2)'''
|
||||
cfg.CONF.set_override('api_version', '2',
|
||||
group='ceilometer_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._ceilometer = None
|
||||
osc.ceilometer()
|
||||
self.assertEqual(ceclient_v2.Client,
|
||||
type(osc.ceilometer()))
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
@mock.patch.object(ceclient_v2.Client, '_get_alarm_client')
|
||||
def test_clients_ceilometer_cached(self, mock_get_alarm_client,
|
||||
mock_session):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._ceilometer = None
|
||||
ceilometer = osc.ceilometer()
|
||||
ceilometer_cached = osc.ceilometer()
|
||||
self.assertEqual(ceilometer, ceilometer_cached)
|
||||
|
||||
@mock.patch.object(netclient, 'Client')
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_neutron(self, mock_session, mock_call):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._neutron = None
|
||||
osc.neutron()
|
||||
mock_call.assert_called_once_with(cfg.CONF.neutron_client.api_version,
|
||||
session=mock_session)
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_neutron_diff_vers(self, mock_session):
|
||||
'''neutronclient currently only has one version (v2)'''
|
||||
cfg.CONF.set_override('api_version', '2',
|
||||
group='neutron_client')
|
||||
osc = clients.OpenStackClients()
|
||||
osc._neutron = None
|
||||
osc.neutron()
|
||||
self.assertEqual(netclient_v2.Client,
|
||||
type(osc.neutron()))
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'session')
|
||||
def test_clients_neutron_cached(self, mock_session):
|
||||
osc = clients.OpenStackClients()
|
||||
osc._neutron = None
|
||||
neutron = osc.neutron()
|
||||
neutron_cached = osc.neutron()
|
||||
self.assertEqual(neutron, neutron_cached)
|
||||
@@ -1,68 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from keystoneclient.auth.identity import Password
|
||||
from keystoneclient.session import Session
|
||||
from mock import mock
|
||||
from oslo_config import cfg
|
||||
from watcher.common.keystone import KeystoneClient
|
||||
from watcher.tests.base import BaseTestCase
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestKeystone(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestKeystone, self).setUp()
|
||||
self.ckeystone = KeystoneClient()
|
||||
|
||||
@mock.patch('keystoneclient.v2_0.client.Client', autospec=True)
|
||||
def test_get_endpoint_v2(self, keystone):
|
||||
expected_endpoint = "http://ip:port/v2"
|
||||
cfg.CONF.set_override(
|
||||
'auth_uri', expected_endpoint, group="keystone_authtoken",
|
||||
enforce_type=True
|
||||
)
|
||||
ks = mock.Mock()
|
||||
ks.service_catalog.url_for.return_value = expected_endpoint
|
||||
keystone.return_value = ks
|
||||
ep = self.ckeystone.get_endpoint(service_type='metering',
|
||||
endpoint_type='publicURL',
|
||||
region_name='RegionOne')
|
||||
|
||||
self.assertEqual(ep, expected_endpoint)
|
||||
|
||||
@mock.patch('watcher.common.keystone.KeystoneClient._is_apiv3')
|
||||
def test_get_session(self, mock_apiv3):
|
||||
mock_apiv3.return_value = True
|
||||
k = KeystoneClient()
|
||||
session = k.get_session()
|
||||
self.assertIsInstance(session.auth, Password)
|
||||
self.assertIsInstance(session, Session)
|
||||
|
||||
@mock.patch('watcher.common.keystone.KeystoneClient._is_apiv3')
|
||||
def test_get_credentials(self, mock_apiv3):
|
||||
mock_apiv3.return_value = True
|
||||
expected_creds = {'auth_url': None,
|
||||
'password': None,
|
||||
'project_domain_name': 'default',
|
||||
'project_name': 'admin',
|
||||
'user_domain_name': 'default',
|
||||
'username': None}
|
||||
creds = self.ckeystone.get_credentials()
|
||||
self.assertEqual(creds, expected_creds)
|
||||
@@ -1,153 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
||||
#
|
||||
# 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 time
|
||||
|
||||
import glanceclient.v2.client as glclient
|
||||
import mock
|
||||
import novaclient.client as nvclient
|
||||
|
||||
from watcher.common import keystone
|
||||
from watcher.common.nova import NovaClient
|
||||
from watcher.common import utils
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestNovaClient(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNovaClient, self).setUp()
|
||||
self.instance_uuid = "fb5311b7-37f3-457e-9cde-6494a3c59bfe"
|
||||
self.source_hypervisor = "ldev-indeedsrv005"
|
||||
self.destination_hypervisor = "ldev-indeedsrv006"
|
||||
|
||||
self.creds = mock.MagicMock()
|
||||
self.session = mock.MagicMock()
|
||||
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
def test_stop_instance(self):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
instance_id = utils.generate_uuid()
|
||||
server = mock.MagicMock()
|
||||
server.id = instance_id
|
||||
setattr(server, 'OS-EXT-STS:vm_state', 'stopped')
|
||||
nova_client.nova.servers = mock.MagicMock()
|
||||
nova_client.nova.servers.find.return_value = server
|
||||
nova_client.nova.servers.list.return_value = [server]
|
||||
|
||||
result = nova_client.stop_instance(instance_id)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
def test_set_host_offline(self):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
host = mock.MagicMock()
|
||||
nova_client.nova.hosts = mock.MagicMock()
|
||||
nova_client.nova.hosts.get.return_value = host
|
||||
result = nova_client.set_host_offline("rennes")
|
||||
self.assertEqual(result, True)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
def test_live_migrate_instance(self):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
server = mock.MagicMock()
|
||||
server.id = self.instance_uuid
|
||||
nova_client.nova.servers = mock.MagicMock()
|
||||
nova_client.nova.servers.list.return_value = [server]
|
||||
instance = nova_client.live_migrate_instance(
|
||||
self.instance_uuid, self.destination_hypervisor
|
||||
)
|
||||
self.assertIsNotNone(instance)
|
||||
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
def test_watcher_non_live_migrate_instance_not_found(self):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
nova_client.nova.servers.list.return_value = []
|
||||
nova_client.nova.servers.find.return_value = None
|
||||
|
||||
is_success = nova_client.watcher_non_live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor)
|
||||
|
||||
self.assertEqual(is_success, False)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
def test_watcher_non_live_migrate_instance_volume(self):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
instance = mock.MagicMock(id=self.instance_uuid)
|
||||
setattr(instance, 'OS-EXT-SRV-ATTR:host', self.source_hypervisor)
|
||||
nova_client.nova.servers.list.return_value = [instance]
|
||||
nova_client.nova.servers.find.return_value = instance
|
||||
instance = nova_client.watcher_non_live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor)
|
||||
self.assertIsNotNone(instance)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
def test_watcher_non_live_migrate_keep_image(self):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
instance = mock.MagicMock(id=self.instance_uuid)
|
||||
setattr(instance, 'OS-EXT-SRV-ATTR:host', self.source_hypervisor)
|
||||
addresses = mock.MagicMock()
|
||||
type = mock.MagicMock()
|
||||
networks = []
|
||||
networks.append(("lan", type))
|
||||
addresses.items.return_value = networks
|
||||
attached_volumes = mock.MagicMock()
|
||||
setattr(instance, 'addresses', addresses)
|
||||
setattr(instance, "os-extended-volumes:volumes_attached",
|
||||
attached_volumes)
|
||||
nova_client.nova.servers.list.return_value = [instance]
|
||||
nova_client.nova.servers.find.return_value = instance
|
||||
instance = nova_client.watcher_non_live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor, keep_original_image_name=False)
|
||||
self.assertIsNotNone(instance)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
@mock.patch.object(keystone, 'KeystoneClient', mock.Mock())
|
||||
@mock.patch.object(nvclient, "Client", mock.Mock())
|
||||
@mock.patch.object(glclient, "Client")
|
||||
def test_create_image_from_instance(self, m_glance_cls):
|
||||
nova_client = NovaClient(creds=self.creds, session=self.session)
|
||||
instance = mock.MagicMock()
|
||||
image = mock.MagicMock()
|
||||
setattr(instance, 'OS-EXT-SRV-ATTR:host', self.source_hypervisor)
|
||||
nova_client.nova.servers.list.return_value = [instance]
|
||||
nova_client.nova.servers.find.return_value = instance
|
||||
image_uuid = 'fake-image-uuid'
|
||||
nova_client.nova.servers.create_image.return_value = image
|
||||
|
||||
m_glance = mock.MagicMock()
|
||||
m_glance_cls.return_value = m_glance
|
||||
|
||||
m_glance.images = {image_uuid: image}
|
||||
instance = nova_client.create_image_from_instance(
|
||||
self.instance_uuid, "Cirros"
|
||||
)
|
||||
self.assertIsNone(instance)
|
||||
144
watcher/tests/common/test_nova_helper.py
Normal file
144
watcher/tests/common/test_nova_helper.py
Normal file
@@ -0,0 +1,144 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# Authors: Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>
|
||||
#
|
||||
# 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 time
|
||||
|
||||
import mock
|
||||
|
||||
from watcher.common import clients
|
||||
from watcher.common import nova_helper
|
||||
from watcher.common import utils
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
@mock.patch.object(clients.OpenStackClients, 'nova')
|
||||
@mock.patch.object(clients.OpenStackClients, 'neutron')
|
||||
@mock.patch.object(clients.OpenStackClients, 'cinder')
|
||||
@mock.patch.object(clients.OpenStackClients, 'glance')
|
||||
class TestNovaHelper(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNovaHelper, self).setUp()
|
||||
self.instance_uuid = "fb5311b7-37f3-457e-9cde-6494a3c59bfe"
|
||||
self.source_hypervisor = "ldev-indeedsrv005"
|
||||
self.destination_hypervisor = "ldev-indeedsrv006"
|
||||
|
||||
def test_stop_instance(self, mock_glance, mock_cinder, mock_neutron,
|
||||
mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
instance_id = utils.generate_uuid()
|
||||
server = mock.MagicMock()
|
||||
server.id = instance_id
|
||||
setattr(server, 'OS-EXT-STS:vm_state', 'stopped')
|
||||
nova_util.nova.servers = mock.MagicMock()
|
||||
nova_util.nova.servers.find.return_value = server
|
||||
nova_util.nova.servers.list.return_value = [server]
|
||||
|
||||
result = nova_util.stop_instance(instance_id)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
def test_set_host_offline(self, mock_glance, mock_cinder, mock_neutron,
|
||||
mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
host = mock.MagicMock()
|
||||
nova_util.nova.hosts = mock.MagicMock()
|
||||
nova_util.nova.hosts.get.return_value = host
|
||||
result = nova_util.set_host_offline("rennes")
|
||||
self.assertEqual(result, True)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
def test_live_migrate_instance(self, mock_glance, mock_cinder,
|
||||
mock_neutron, mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
server = mock.MagicMock()
|
||||
server.id = self.instance_uuid
|
||||
nova_util.nova.servers = mock.MagicMock()
|
||||
nova_util.nova.servers.list.return_value = [server]
|
||||
instance = nova_util.live_migrate_instance(
|
||||
self.instance_uuid, self.destination_hypervisor
|
||||
)
|
||||
self.assertIsNotNone(instance)
|
||||
|
||||
def test_watcher_non_live_migrate_instance_not_found(
|
||||
self, mock_glance, mock_cinder, mock_neutron, mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
nova_util.nova.servers.list.return_value = []
|
||||
nova_util.nova.servers.find.return_value = None
|
||||
|
||||
is_success = nova_util.watcher_non_live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor)
|
||||
|
||||
self.assertEqual(is_success, False)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
def test_watcher_non_live_migrate_instance_volume(
|
||||
self, mock_glance, mock_cinder, mock_neutron, mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
instance = mock.MagicMock(id=self.instance_uuid)
|
||||
setattr(instance, 'OS-EXT-SRV-ATTR:host', self.source_hypervisor)
|
||||
nova_util.nova.servers.list.return_value = [instance]
|
||||
nova_util.nova.servers.find.return_value = instance
|
||||
instance = nova_util.watcher_non_live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor)
|
||||
self.assertIsNotNone(instance)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
def test_watcher_non_live_migrate_keep_image(
|
||||
self, mock_glance, mock_cinder, mock_neutron, mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
instance = mock.MagicMock(id=self.instance_uuid)
|
||||
setattr(instance, 'OS-EXT-SRV-ATTR:host', self.source_hypervisor)
|
||||
addresses = mock.MagicMock()
|
||||
type = mock.MagicMock()
|
||||
networks = []
|
||||
networks.append(("lan", type))
|
||||
addresses.items.return_value = networks
|
||||
attached_volumes = mock.MagicMock()
|
||||
setattr(instance, 'addresses', addresses)
|
||||
setattr(instance, "os-extended-volumes:volumes_attached",
|
||||
attached_volumes)
|
||||
nova_util.nova.servers.list.return_value = [instance]
|
||||
nova_util.nova.servers.find.return_value = instance
|
||||
instance = nova_util.watcher_non_live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor, keep_original_image_name=False)
|
||||
self.assertIsNotNone(instance)
|
||||
|
||||
@mock.patch.object(time, 'sleep', mock.Mock())
|
||||
def test_create_image_from_instance(self, mock_glance, mock_cinder,
|
||||
mock_neutron, mock_nova):
|
||||
nova_util = nova_helper.NovaHelper()
|
||||
instance = mock.MagicMock()
|
||||
image = mock.MagicMock()
|
||||
setattr(instance, 'OS-EXT-SRV-ATTR:host', self.source_hypervisor)
|
||||
nova_util.nova.servers.list.return_value = [instance]
|
||||
nova_util.nova.servers.find.return_value = instance
|
||||
image_uuid = 'fake-image-uuid'
|
||||
nova_util.nova.servers.create_image.return_value = image
|
||||
|
||||
glance_client = mock.MagicMock()
|
||||
mock_glance.return_value = glance_client
|
||||
|
||||
glance_client.images = {image_uuid: image}
|
||||
instance = nova_util.create_image_from_instance(
|
||||
self.instance_uuid, "Cirros"
|
||||
)
|
||||
self.assertIsNone(instance)
|
||||
@@ -36,8 +36,8 @@ class TestStrategySelector(TestCase):
|
||||
enforce_type=True)
|
||||
expected_goal = 'DUMMY'
|
||||
expected_strategy = CONF.watcher_goals.goals[expected_goal]
|
||||
self.strategy_selector.define_from_goal(expected_goal)
|
||||
mock_call.assert_called_once_with(expected_strategy)
|
||||
self.strategy_selector.define_from_goal(expected_goal, osc=None)
|
||||
mock_call.assert_called_once_with(expected_strategy, osc=None)
|
||||
|
||||
@patch.object(DefaultStrategyLoader, 'load')
|
||||
def test_define_from_goal_with_incorrect_mapping(self, mock_call):
|
||||
|
||||
Reference in New Issue
Block a user