Files
python-novaclient/novaclient/tests/unit/test_api_versions.py
Andrey Kurilin f9bdba2dd7 Extend microversion stuff to support resource classes
Current implementation of novaclient.api_versions.wraps allow to use
versioned methods for shell functions and resource managers. As resource
managers can have versioned methods, resource objects can have it too, but
it was not implemented yet. This patch fixes this omission.

Changes:
 - Add api_version property to base resource class. It is mapped to
   api_version property of manager class;
 - Move `novaclient.utils.generate_function_name` to
   `novaclient.api_versions._generate_function_name`, since this method is
   specific to microversion stuff and should not used outside api_versions
   module;
 - Rewrite _generate_function_name to handle class(owner) name. Previously,
   it was improssible to have two classes in one module with versioned
   methods with equal names.
 - Remove call of generate_function_name from novaclient.shell. Shell module
   should not take care about function identifiers. get_substitutions accepts
   object with __id__ property now.
 - Mark _add_substitution as private method, since it should not be used
   outside api_versions module
 - Split all versioned methods of Server resource from novaclient.v2.servers
   module.

Change-Id: Icfce16bfa6f919d7f8451d592f4a8e276b1f1709
2016-06-16 05:57:59 +00:00

441 lines
17 KiB
Python

# Copyright 2016 Mirantis
# All Rights Reserved.
#
# 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 novaclient
from novaclient import api_versions
from novaclient import exceptions
from novaclient.tests.unit import utils
from novaclient import utils as nutils
from novaclient.v2 import versions
class APIVersionTestCase(utils.TestCase):
def test_valid_version_strings(self):
def _test_string(version, exp_major, exp_minor):
v = api_versions.APIVersion(version)
self.assertEqual(v.ver_major, exp_major)
self.assertEqual(v.ver_minor, exp_minor)
_test_string("1.1", 1, 1)
_test_string("2.10", 2, 10)
_test_string("5.234", 5, 234)
_test_string("12.5", 12, 5)
_test_string("2.0", 2, 0)
_test_string("2.200", 2, 200)
def test_null_version(self):
v = api_versions.APIVersion()
self.assertTrue(v.is_null())
def test_invalid_version_strings(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "2")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "200")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "2.1.4")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "200.23.66.3")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "5 .3")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "5. 3")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "5.03")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "02.1")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "2.001")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, " 2.1")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.APIVersion, "2.1 ")
def test_version_comparisons(self):
v1 = api_versions.APIVersion("2.0")
v2 = api_versions.APIVersion("2.5")
v3 = api_versions.APIVersion("5.23")
v4 = api_versions.APIVersion("2.0")
v_null = api_versions.APIVersion()
self.assertTrue(v1 < v2)
self.assertTrue(v3 > v2)
self.assertTrue(v1 != v2)
self.assertTrue(v1 == v4)
self.assertTrue(v1 != v_null)
self.assertTrue(v_null == v_null)
self.assertRaises(TypeError, v1.__le__, "2.1")
def test_version_matches(self):
v1 = api_versions.APIVersion("2.0")
v2 = api_versions.APIVersion("2.5")
v3 = api_versions.APIVersion("2.45")
v4 = api_versions.APIVersion("3.3")
v5 = api_versions.APIVersion("3.23")
v6 = api_versions.APIVersion("2.0")
v7 = api_versions.APIVersion("3.3")
v8 = api_versions.APIVersion("4.0")
v_null = api_versions.APIVersion()
self.assertTrue(v2.matches(v1, v3))
self.assertTrue(v2.matches(v1, v_null))
self.assertTrue(v1.matches(v6, v2))
self.assertTrue(v4.matches(v2, v7))
self.assertTrue(v4.matches(v_null, v7))
self.assertTrue(v4.matches(v_null, v8))
self.assertFalse(v1.matches(v2, v3))
self.assertFalse(v5.matches(v2, v4))
self.assertFalse(v2.matches(v3, v1))
self.assertRaises(ValueError, v_null.matches, v1, v3)
def test_get_string(self):
v1_string = "3.23"
v1 = api_versions.APIVersion(v1_string)
self.assertEqual(v1_string, v1.get_string())
self.assertRaises(ValueError,
api_versions.APIVersion().get_string)
class UpdateHeadersTestCase(utils.TestCase):
def test_api_version_is_null(self):
headers = {}
api_versions.update_headers(headers, api_versions.APIVersion())
self.assertEqual({}, headers)
def test_api_version_is_major(self):
headers = {}
api_versions.update_headers(headers, api_versions.APIVersion("7.0"))
self.assertEqual({}, headers)
def test_api_version_is_not_null(self):
api_version = api_versions.APIVersion("2.3")
headers = {}
api_versions.update_headers(headers, api_version)
self.assertEqual(
{"X-OpenStack-Nova-API-Version": api_version.get_string()},
headers)
def test_api_version_is_gte_27(self):
api_version = api_versions.APIVersion("2.27")
headers = {}
api_versions.update_headers(headers, api_version)
self.assertIn('X-OpenStack-Nova-API-Version', headers)
self.assertIn('OpenStack-API-Version', headers)
self.assertEqual(api_version.get_string(),
headers['X-OpenStack-Nova-API-Version'])
self.assertEqual('%s %s' % (api_versions.SERVICE_TYPE,
api_version.get_string()),
headers['OpenStack-API-Version'])
class CheckHeadersTestCase(utils.TestCase):
def setUp(self):
super(CheckHeadersTestCase, self).setUp()
mock_log_patch = mock.patch("novaclient.api_versions.LOG")
self.mock_log = mock_log_patch.start()
self.addCleanup(mock_log_patch.stop)
def test_legacy_microversion_is_specified(self):
response = mock.MagicMock(
headers={api_versions.LEGACY_HEADER_NAME: ""})
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
self.assertFalse(self.mock_log.warning.called)
response = mock.MagicMock(headers={})
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
self.assertTrue(self.mock_log.warning.called)
def test_generic_microversion_is_specified(self):
response = mock.MagicMock(
headers={api_versions.HEADER_NAME: ""})
api_versions.check_headers(response, api_versions.APIVersion("2.27"))
self.assertFalse(self.mock_log.warning.called)
response = mock.MagicMock(headers={})
api_versions.check_headers(response, api_versions.APIVersion("2.27"))
self.assertTrue(self.mock_log.warning.called)
def test_microversion_is_not_specified(self):
response = mock.MagicMock(
headers={api_versions.LEGACY_HEADER_NAME: ""})
api_versions.check_headers(response, api_versions.APIVersion("2.2"))
self.assertFalse(self.mock_log.warning.called)
response = mock.MagicMock(headers={})
api_versions.check_headers(response, api_versions.APIVersion("2.0"))
self.assertFalse(self.mock_log.warning.called)
class GetAPIVersionTestCase(utils.TestCase):
def test_get_available_client_versions(self):
output = api_versions.get_available_major_versions()
self.assertNotEqual([], output)
def test_wrong_format(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.get_api_version, "something_wrong")
def test_wrong_major_version(self):
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.get_api_version, "1")
@mock.patch("novaclient.api_versions.APIVersion")
def test_only_major_part_is_presented(self, mock_apiversion):
version = 7
self.assertEqual(mock_apiversion.return_value,
api_versions.get_api_version(version))
mock_apiversion.assert_called_once_with("%s.0" % str(version))
@mock.patch("novaclient.api_versions.APIVersion")
def test_major_and_minor_parts_is_presented(self, mock_apiversion):
version = "2.7"
self.assertEqual(mock_apiversion.return_value,
api_versions.get_api_version(version))
mock_apiversion.assert_called_once_with(version)
class WrapsTestCase(utils.TestCase):
def _get_obj_with_vers(self, vers):
return mock.MagicMock(api_version=api_versions.APIVersion(vers))
def _side_effect_of_vers_method(self, *args, **kwargs):
m = mock.MagicMock(start_version=args[1], end_version=args[2])
m.name = args[0]
return m
@mock.patch("novaclient.api_versions._get_function_name")
@mock.patch("novaclient.api_versions.VersionedMethod")
def test_end_version_is_none(self, mock_versioned_method, mock_name):
mock_versioned_method.side_effect = self._side_effect_of_vers_method
@api_versions.wraps("2.2")
def foo(*args, **kwargs):
pass
foo(self._get_obj_with_vers("2.4"))
mock_versioned_method.assert_called_once_with(
mock_name.return_value, api_versions.APIVersion("2.2"),
api_versions.APIVersion("2.latest"), mock.ANY)
@mock.patch("novaclient.api_versions._get_function_name")
@mock.patch("novaclient.api_versions.VersionedMethod")
def test_start_and_end_version_are_presented(self, mock_versioned_method,
mock_name):
mock_versioned_method.side_effect = self._side_effect_of_vers_method
@api_versions.wraps("2.2", "2.6")
def foo(*args, **kwargs):
pass
foo(self._get_obj_with_vers("2.4"))
mock_versioned_method.assert_called_once_with(
mock_name.return_value, api_versions.APIVersion("2.2"),
api_versions.APIVersion("2.6"), mock.ANY)
@mock.patch("novaclient.api_versions._get_function_name")
@mock.patch("novaclient.api_versions.VersionedMethod")
def test_api_version_doesnt_match(self, mock_versioned_method, mock_name):
mock_versioned_method.side_effect = self._side_effect_of_vers_method
@api_versions.wraps("2.2", "2.6")
def foo(*args, **kwargs):
pass
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
foo, self._get_obj_with_vers("2.1"))
mock_versioned_method.assert_called_once_with(
mock_name.return_value, api_versions.APIVersion("2.2"),
api_versions.APIVersion("2.6"), mock.ANY)
def test_define_method_is_actually_called(self):
checker = mock.MagicMock()
@api_versions.wraps("2.2", "2.6")
def some_func(*args, **kwargs):
checker(*args, **kwargs)
obj = self._get_obj_with_vers("2.4")
some_args = ("arg_1", "arg_2")
some_kwargs = {"key1": "value1", "key2": "value2"}
some_func(obj, *some_args, **some_kwargs)
checker.assert_called_once_with(*((obj,) + some_args), **some_kwargs)
@mock.patch("novaclient.api_versions._get_function_name")
def test_arguments_property_is_copied(self, mock_name):
@nutils.arg("argument_1")
@api_versions.wraps("2.666", "2.777")
@nutils.arg("argument_2")
def some_func():
pass
versioned_method = api_versions.get_substitutions(
mock_name.return_value, api_versions.APIVersion("2.700"))[0]
self.assertEqual(some_func.arguments,
versioned_method.func.arguments)
self.assertIn((("argument_1",), {}), versioned_method.func.arguments)
self.assertIn((("argument_2",), {}), versioned_method.func.arguments)
def test_several_methods_with_same_name_in_one_module(self):
class A(object):
api_version = api_versions.APIVersion("777.777")
@api_versions.wraps("777.777")
def f(self):
return 1
class B(object):
api_version = api_versions.APIVersion("777.777")
@api_versions.wraps("777.777")
def f(self):
return 2
self.assertEqual(1, A().f())
self.assertEqual(2, B().f())
def test_generate_function_name(self):
expected_name = "novaclient.tests.unit.test_api_versions.fake_func"
self.assertNotIn(expected_name, api_versions._SUBSTITUTIONS)
@api_versions.wraps("7777777.7777777")
def fake_func():
pass
self.assertIn(expected_name, api_versions._SUBSTITUTIONS)
self.assertEqual(expected_name, fake_func.__id__)
class DiscoverVersionTestCase(utils.TestCase):
def setUp(self):
super(DiscoverVersionTestCase, self).setUp()
self.orig_max = novaclient.API_MAX_VERSION
self.orig_min = novaclient.API_MIN_VERSION
self.addCleanup(self._clear_fake_version)
def _clear_fake_version(self):
novaclient.API_MAX_VERSION = self.orig_max
novaclient.API_MIN_VERSION = self.orig_min
def test_server_is_too_new(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = mock.MagicMock(
version="2.7", min_version="2.4")
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.3")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.discover_version, fake_client,
api_versions.APIVersion('2.latest'))
def test_server_is_too_old(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = mock.MagicMock(
version="2.7", min_version="2.4")
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.10")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.9")
self.assertRaises(exceptions.UnsupportedVersion,
api_versions.discover_version, fake_client,
api_versions.APIVersion('2.latest'))
def test_server_end_version_is_the_latest_one(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = mock.MagicMock(
version="2.7", min_version="2.4")
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertEqual(
"2.7",
api_versions.discover_version(
fake_client,
api_versions.APIVersion('2.latest')).get_string())
def test_client_end_version_is_the_latest_one(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = mock.MagicMock(
version="2.16", min_version="2.4")
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertEqual(
"2.11",
api_versions.discover_version(
fake_client,
api_versions.APIVersion('2.latest')).get_string())
def test_server_without_microversion(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = mock.MagicMock(
version='', min_version='')
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertEqual(
"2.0",
api_versions.discover_version(
fake_client,
api_versions.APIVersion('2.latest')).get_string())
def test_server_without_microversion_and_no_version_field(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = versions.Version(
None, {})
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertEqual(
"2.0",
api_versions.discover_version(
fake_client,
api_versions.APIVersion('2.latest')).get_string())
def test_server_without_microversion_rax_workaround(self):
fake_client = mock.MagicMock()
fake_client.versions.get_current.return_value = None
novaclient.API_MAX_VERSION = api_versions.APIVersion("2.11")
novaclient.API_MIN_VERSION = api_versions.APIVersion("2.1")
self.assertEqual(
"2.0",
api_versions.discover_version(
fake_client,
api_versions.APIVersion('2.latest')).get_string())