Switch compute flavors from novaclient/direct to SDK
Let's switch flavors from novaclient or direct API requests onto using SDK. Microversion agreement comes out of the box. SDK normalizes property names, while OSC uses server side names. In order not to break OSC users continue using server-side names. Depends-On: https://review.opendev.org/#/c/762989/ Change-Id: I62b2ed8488ee4ac9c42051311bcfb455506ddd90
This commit is contained in:
parent
ea358057f8
commit
0f4f42b652
@ -45,7 +45,7 @@ msgpack-python==0.4.0
|
|||||||
munch==2.1.0
|
munch==2.1.0
|
||||||
netaddr==0.7.18
|
netaddr==0.7.18
|
||||||
netifaces==0.10.4
|
netifaces==0.10.4
|
||||||
openstacksdk==0.51.0
|
openstacksdk==0.52.0
|
||||||
os-service-types==1.7.0
|
os-service-types==1.7.0
|
||||||
os-testr==1.0.0
|
os-testr==1.0.0
|
||||||
osc-lib==2.2.0
|
osc-lib==2.2.0
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from novaclient import api_versions
|
from openstack import exceptions as sdk_exceptions
|
||||||
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib.cli import parseractions
|
from osc_lib.cli import parseractions
|
||||||
from osc_lib.command import command
|
from osc_lib.command import command
|
||||||
@ -33,10 +34,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
_formatters = {
|
_formatters = {
|
||||||
'extra_specs': format_columns.DictColumn,
|
'extra_specs': format_columns.DictColumn,
|
||||||
# Unless we finish switch to use SDK resources this need to be doubled this
|
'properties': format_columns.DictColumn
|
||||||
# way
|
|
||||||
'properties': format_columns.DictColumn,
|
|
||||||
'Properties': format_columns.DictColumn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -51,29 +49,10 @@ def _get_flavor_columns(item):
|
|||||||
|
|
||||||
}
|
}
|
||||||
hidden_columns = ['links', 'location']
|
hidden_columns = ['links', 'location']
|
||||||
|
|
||||||
return utils.get_osc_show_columns_for_sdk_resource(
|
return utils.get_osc_show_columns_for_sdk_resource(
|
||||||
item, column_map, hidden_columns)
|
item, column_map, hidden_columns)
|
||||||
|
|
||||||
|
|
||||||
def _find_flavor(compute_client, flavor):
|
|
||||||
try:
|
|
||||||
return compute_client.flavors.get(flavor)
|
|
||||||
except Exception as ex:
|
|
||||||
if type(ex).__name__ == 'NotFound':
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
try:
|
|
||||||
return compute_client.flavors.find(name=flavor, is_public=None)
|
|
||||||
except Exception as ex:
|
|
||||||
if type(ex).__name__ == 'NotFound':
|
|
||||||
msg = _("No flavor with a name or ID of '%s' exists.") % flavor
|
|
||||||
raise exceptions.CommandError(msg)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class CreateFlavor(command.ShowOne):
|
class CreateFlavor(command.ShowOne):
|
||||||
_description = _("Create new flavor")
|
_description = _("Create new flavor")
|
||||||
|
|
||||||
@ -87,9 +66,7 @@ class CreateFlavor(command.ShowOne):
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--id",
|
"--id",
|
||||||
metavar="<id>",
|
metavar="<id>",
|
||||||
default='auto',
|
help=_("Unique flavor ID")
|
||||||
help=_("Unique flavor ID; 'auto' creates a UUID "
|
|
||||||
"(default: auto)")
|
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--ram",
|
"--ram",
|
||||||
@ -170,32 +147,36 @@ class CreateFlavor(command.ShowOne):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
if parsed_args.project and parsed_args.public:
|
if parsed_args.project and parsed_args.public:
|
||||||
msg = _("--project is only allowed with --private")
|
msg = _("--project is only allowed with --private")
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
args = {
|
||||||
|
'name': parsed_args.name,
|
||||||
|
'ram': parsed_args.ram,
|
||||||
|
'vcpus': parsed_args.vcpus,
|
||||||
|
'disk': parsed_args.disk,
|
||||||
|
'id': parsed_args.id,
|
||||||
|
'ephemeral': parsed_args.ephemeral,
|
||||||
|
'swap': parsed_args.swap,
|
||||||
|
'rxtx_factor': parsed_args.rxtx_factor,
|
||||||
|
'is_public': parsed_args.public,
|
||||||
|
}
|
||||||
|
|
||||||
if parsed_args.description:
|
if parsed_args.description:
|
||||||
if compute_client.api_version < api_versions.APIVersion("2.55"):
|
if not sdk_utils.supports_microversion(compute_client, '2.55'):
|
||||||
msg = _("--os-compute-api-version 2.55 or later is required")
|
msg = _(
|
||||||
|
'The --description parameter requires server support for '
|
||||||
|
'API microversion 2.55'
|
||||||
|
)
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
args = (
|
args['description'] = parsed_args.description
|
||||||
parsed_args.name,
|
|
||||||
parsed_args.ram,
|
|
||||||
parsed_args.vcpus,
|
|
||||||
parsed_args.disk,
|
|
||||||
parsed_args.id,
|
|
||||||
parsed_args.ephemeral,
|
|
||||||
parsed_args.swap,
|
|
||||||
parsed_args.rxtx_factor,
|
|
||||||
parsed_args.public,
|
|
||||||
parsed_args.description
|
|
||||||
)
|
|
||||||
|
|
||||||
flavor = compute_client.flavors.create(*args)
|
flavor = compute_client.create_flavor(**args)
|
||||||
|
|
||||||
if parsed_args.project:
|
if parsed_args.project:
|
||||||
try:
|
try:
|
||||||
@ -204,7 +185,7 @@ class CreateFlavor(command.ShowOne):
|
|||||||
parsed_args.project,
|
parsed_args.project,
|
||||||
parsed_args.project_domain,
|
parsed_args.project_domain,
|
||||||
).id
|
).id
|
||||||
compute_client.flavor_access.add_tenant_access(
|
compute_client.flavor_add_tenant_access(
|
||||||
flavor.id, project_id)
|
flavor.id, project_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = _("Failed to add project %(project)s access to "
|
msg = _("Failed to add project %(project)s access to "
|
||||||
@ -212,19 +193,14 @@ class CreateFlavor(command.ShowOne):
|
|||||||
LOG.error(msg, {'project': parsed_args.project, 'e': e})
|
LOG.error(msg, {'project': parsed_args.project, 'e': e})
|
||||||
if parsed_args.property:
|
if parsed_args.property:
|
||||||
try:
|
try:
|
||||||
flavor.set_keys(parsed_args.property)
|
flavor = compute_client.create_flavor_extra_specs(
|
||||||
|
flavor, parsed_args.property)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_("Failed to set flavor property: %s"), e)
|
LOG.error(_("Failed to set flavor property: %s"), e)
|
||||||
|
|
||||||
flavor_info = flavor._info.copy()
|
display_columns, columns = _get_flavor_columns(flavor)
|
||||||
flavor_info['properties'] = flavor.get_keys()
|
data = utils.get_dict_properties(flavor, columns,
|
||||||
|
formatters=_formatters)
|
||||||
display_columns, columns = _get_flavor_columns(flavor_info)
|
|
||||||
data = utils.get_dict_properties(
|
|
||||||
flavor_info, columns,
|
|
||||||
formatters=_formatters,
|
|
||||||
mixed_case_fields=['OS-FLV-DISABLED:disabled',
|
|
||||||
'OS-FLV-EXT-DATA:ephemeral'])
|
|
||||||
|
|
||||||
return (display_columns, data)
|
return (display_columns, data)
|
||||||
|
|
||||||
@ -243,12 +219,12 @@ class DeleteFlavor(command.Command):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
result = 0
|
result = 0
|
||||||
for f in parsed_args.flavor:
|
for f in parsed_args.flavor:
|
||||||
try:
|
try:
|
||||||
flavor = _find_flavor(compute_client, f)
|
flavor = compute_client.find_flavor(f, ignore_missing=False)
|
||||||
compute_client.flavors.delete(flavor.id)
|
compute_client.delete_flavor(flavor.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
result += 1
|
result += 1
|
||||||
LOG.error(_("Failed to delete flavor with name or "
|
LOG.error(_("Failed to delete flavor with name or "
|
||||||
@ -307,37 +283,63 @@ class ListFlavor(command.Lister):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
columns = (
|
|
||||||
"ID",
|
|
||||||
"Name",
|
|
||||||
"RAM",
|
|
||||||
"Disk",
|
|
||||||
"Ephemeral",
|
|
||||||
"VCPUs",
|
|
||||||
"Is Public",
|
|
||||||
)
|
|
||||||
|
|
||||||
# is_public is ternary - None means give all flavors,
|
# is_public is ternary - None means give all flavors,
|
||||||
# True is public only and False is private only
|
# True is public only and False is private only
|
||||||
# By default Nova assumes True and gives admins public flavors
|
# By default Nova assumes True and gives admins public flavors
|
||||||
# and flavors from their own projects only.
|
# and flavors from their own projects only.
|
||||||
is_public = None if parsed_args.all else parsed_args.public
|
is_public = None if parsed_args.all else parsed_args.public
|
||||||
|
|
||||||
data = compute_client.flavors.list(is_public=is_public,
|
query_attrs = {
|
||||||
marker=parsed_args.marker,
|
'is_public': is_public
|
||||||
limit=parsed_args.limit)
|
}
|
||||||
|
if parsed_args.marker:
|
||||||
|
query_attrs['marker'] = parsed_args.marker
|
||||||
|
if parsed_args.limit:
|
||||||
|
query_attrs['limit'] = parsed_args.limit
|
||||||
|
if parsed_args.limit or parsed_args.marker:
|
||||||
|
# User passed explicit pagination request, switch off SDK
|
||||||
|
# pagination
|
||||||
|
query_attrs['paginated'] = False
|
||||||
|
|
||||||
|
data = list(compute_client.flavors(**query_attrs))
|
||||||
|
# Even if server supports 2.61 some policy might stop it sending us
|
||||||
|
# extra_specs. So try to fetch them if they are absent
|
||||||
|
for f in data:
|
||||||
|
if not f.extra_specs:
|
||||||
|
compute_client.fetch_flavor_extra_specs(f)
|
||||||
|
|
||||||
|
columns = (
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"ram",
|
||||||
|
"disk",
|
||||||
|
"ephemeral",
|
||||||
|
"vcpus",
|
||||||
|
"is_public"
|
||||||
|
)
|
||||||
if parsed_args.long:
|
if parsed_args.long:
|
||||||
columns = columns + (
|
columns += (
|
||||||
|
"swap",
|
||||||
|
"rxtx_factor",
|
||||||
|
"extra_specs",
|
||||||
|
)
|
||||||
|
|
||||||
|
column_headers = (
|
||||||
|
"ID",
|
||||||
|
"Name",
|
||||||
|
"RAM",
|
||||||
|
"Disk",
|
||||||
|
"Ephemeral",
|
||||||
|
"VCPUs",
|
||||||
|
"Is Public"
|
||||||
|
)
|
||||||
|
if parsed_args.long:
|
||||||
|
column_headers += (
|
||||||
"Swap",
|
"Swap",
|
||||||
"RXTX Factor",
|
"RXTX Factor",
|
||||||
"Properties",
|
"Properties",
|
||||||
)
|
)
|
||||||
for f in data:
|
|
||||||
f.properties = f.get_keys()
|
|
||||||
|
|
||||||
column_headers = columns
|
|
||||||
|
|
||||||
return (column_headers,
|
return (column_headers,
|
||||||
(utils.get_item_properties(
|
(utils.get_item_properties(
|
||||||
@ -387,24 +389,42 @@ class SetFlavor(command.Command):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
flavor = _find_flavor(compute_client, parsed_args.flavor)
|
try:
|
||||||
|
flavor = compute_client.find_flavor(
|
||||||
|
parsed_args.flavor,
|
||||||
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False)
|
||||||
|
except sdk_exceptions.ResourceNotFound as e:
|
||||||
|
raise exceptions.CommandError(e.message)
|
||||||
|
|
||||||
|
if parsed_args.description:
|
||||||
|
if not sdk_utils.supports_microversion(compute_client, '2.55'):
|
||||||
|
msg = _(
|
||||||
|
'The --description parameter requires server support for '
|
||||||
|
'API microversion 2.55'
|
||||||
|
)
|
||||||
|
raise exceptions.CommandError(msg)
|
||||||
|
|
||||||
|
compute_client.update_flavor(
|
||||||
|
flavor=flavor.id, description=parsed_args.description)
|
||||||
|
|
||||||
result = 0
|
result = 0
|
||||||
key_list = []
|
|
||||||
if parsed_args.no_property:
|
if parsed_args.no_property:
|
||||||
try:
|
try:
|
||||||
for key in flavor.get_keys().keys():
|
for key in flavor.extra_specs.keys():
|
||||||
key_list.append(key)
|
compute_client.delete_flavor_extra_specs_property(
|
||||||
flavor.unset_keys(key_list)
|
flavor.id, key)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_("Failed to clear flavor property: %s"), e)
|
LOG.error(_("Failed to clear flavor property: %s"), e)
|
||||||
result += 1
|
result += 1
|
||||||
|
|
||||||
if parsed_args.property:
|
if parsed_args.property:
|
||||||
try:
|
try:
|
||||||
flavor.set_keys(parsed_args.property)
|
compute_client.create_flavor_extra_specs(
|
||||||
|
flavor.id, parsed_args.property)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_("Failed to set flavor property: %s"), e)
|
LOG.error(_("Failed to set flavor property: %s"), e)
|
||||||
result += 1
|
result += 1
|
||||||
@ -420,7 +440,7 @@ class SetFlavor(command.Command):
|
|||||||
parsed_args.project,
|
parsed_args.project,
|
||||||
parsed_args.project_domain,
|
parsed_args.project_domain,
|
||||||
).id
|
).id
|
||||||
compute_client.flavor_access.add_tenant_access(
|
compute_client.flavor_add_tenant_access(
|
||||||
flavor.id, project_id)
|
flavor.id, project_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_("Failed to set flavor access to project: %s"), e)
|
LOG.error(_("Failed to set flavor access to project: %s"), e)
|
||||||
@ -430,13 +450,6 @@ class SetFlavor(command.Command):
|
|||||||
raise exceptions.CommandError(_("Command Failed: One or more of"
|
raise exceptions.CommandError(_("Command Failed: One or more of"
|
||||||
" the operations failed"))
|
" the operations failed"))
|
||||||
|
|
||||||
if parsed_args.description:
|
|
||||||
if compute_client.api_version < api_versions.APIVersion("2.55"):
|
|
||||||
msg = _("--os-compute-api-version 2.55 or later is required")
|
|
||||||
raise exceptions.CommandError(msg)
|
|
||||||
compute_client.flavors.update(flavor=flavor.id,
|
|
||||||
description=parsed_args.description)
|
|
||||||
|
|
||||||
|
|
||||||
class ShowFlavor(command.ShowOne):
|
class ShowFlavor(command.ShowOne):
|
||||||
_description = _("Display flavor details")
|
_description = _("Display flavor details")
|
||||||
@ -451,35 +464,32 @@ class ShowFlavor(command.ShowOne):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
resource_flavor = _find_flavor(compute_client, parsed_args.flavor)
|
flavor = compute_client.find_flavor(
|
||||||
|
parsed_args.flavor, get_extra_specs=True, ignore_missing=False)
|
||||||
|
|
||||||
access_projects = None
|
access_projects = None
|
||||||
# get access projects list of this flavor
|
# get access projects list of this flavor
|
||||||
if not resource_flavor.is_public:
|
if not flavor.is_public:
|
||||||
try:
|
try:
|
||||||
flavor_access = compute_client.flavor_access.list(
|
flavor_access = compute_client.get_flavor_access(
|
||||||
flavor=resource_flavor.id)
|
flavor=flavor.id)
|
||||||
access_projects = [utils.get_field(access, 'tenant_id')
|
access_projects = [
|
||||||
|
utils.get_field(access, 'tenant_id')
|
||||||
for access in flavor_access]
|
for access in flavor_access]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = _("Failed to get access projects list "
|
msg = _("Failed to get access projects list "
|
||||||
"for flavor '%(flavor)s': %(e)s")
|
"for flavor '%(flavor)s': %(e)s")
|
||||||
LOG.error(msg, {'flavor': parsed_args.flavor, 'e': e})
|
LOG.error(msg, {'flavor': parsed_args.flavor, 'e': e})
|
||||||
|
|
||||||
flavor = resource_flavor._info.copy()
|
# Since we need to inject "access_project_id" into resource - convert
|
||||||
flavor.update({
|
# it to dict and treat it respectively
|
||||||
'access_project_ids': access_projects
|
flavor = flavor.to_dict()
|
||||||
})
|
flavor['access_project_ids'] = access_projects
|
||||||
|
|
||||||
flavor['properties'] = resource_flavor.get_keys()
|
|
||||||
|
|
||||||
display_columns, columns = _get_flavor_columns(flavor)
|
display_columns, columns = _get_flavor_columns(flavor)
|
||||||
data = utils.get_dict_properties(
|
data = utils.get_dict_properties(
|
||||||
flavor, columns,
|
flavor, columns, formatters=_formatters)
|
||||||
formatters=_formatters,
|
|
||||||
mixed_case_fields=['OS-FLV-DISABLED:disabled',
|
|
||||||
'OS-FLV-EXT-DATA:ephemeral'])
|
|
||||||
|
|
||||||
return (display_columns, data)
|
return (display_columns, data)
|
||||||
|
|
||||||
@ -512,16 +522,24 @@ class UnsetFlavor(command.Command):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
compute_client = self.app.client_manager.compute
|
compute_client = self.app.client_manager.sdk_connection.compute
|
||||||
identity_client = self.app.client_manager.identity
|
identity_client = self.app.client_manager.identity
|
||||||
|
|
||||||
flavor = _find_flavor(compute_client, parsed_args.flavor)
|
try:
|
||||||
|
flavor = compute_client.find_flavor(
|
||||||
|
parsed_args.flavor,
|
||||||
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False)
|
||||||
|
except sdk_exceptions.ResourceNotFound as e:
|
||||||
|
raise exceptions.CommandError(_(e.message))
|
||||||
|
|
||||||
result = 0
|
result = 0
|
||||||
if parsed_args.property:
|
if parsed_args.property:
|
||||||
|
for key in parsed_args.property:
|
||||||
try:
|
try:
|
||||||
flavor.unset_keys(parsed_args.property)
|
compute_client.delete_flavor_extra_specs_property(
|
||||||
except Exception as e:
|
flavor.id, key)
|
||||||
|
except sdk_exceptions.SDKException as e:
|
||||||
LOG.error(_("Failed to unset flavor property: %s"), e)
|
LOG.error(_("Failed to unset flavor property: %s"), e)
|
||||||
result += 1
|
result += 1
|
||||||
|
|
||||||
@ -530,13 +548,13 @@ class UnsetFlavor(command.Command):
|
|||||||
if flavor.is_public:
|
if flavor.is_public:
|
||||||
msg = _("Cannot remove access for a public flavor")
|
msg = _("Cannot remove access for a public flavor")
|
||||||
raise exceptions.CommandError(msg)
|
raise exceptions.CommandError(msg)
|
||||||
else:
|
|
||||||
project_id = identity_common.find_project(
|
project_id = identity_common.find_project(
|
||||||
identity_client,
|
identity_client,
|
||||||
parsed_args.project,
|
parsed_args.project,
|
||||||
parsed_args.project_domain,
|
parsed_args.project_domain,
|
||||||
).id
|
).id
|
||||||
compute_client.flavor_access.remove_tenant_access(
|
compute_client.flavor_remove_tenant_access(
|
||||||
flavor.id, project_id)
|
flavor.id, project_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(_("Failed to remove flavor access from project: %s"),
|
LOG.error(_("Failed to remove flavor access from project: %s"),
|
||||||
|
@ -19,6 +19,7 @@ from unittest import mock
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from novaclient import api_versions
|
from novaclient import api_versions
|
||||||
|
from openstack.compute.v2 import flavor as _flavor
|
||||||
|
|
||||||
from openstackclient.api import compute_v2
|
from openstackclient.api import compute_v2
|
||||||
from openstackclient.tests.unit import fakes
|
from openstackclient.tests.unit import fakes
|
||||||
@ -164,7 +165,6 @@ class FakeComputev2Client(object):
|
|||||||
self.extensions.resource_class = fakes.FakeResource(None, {})
|
self.extensions.resource_class = fakes.FakeResource(None, {})
|
||||||
|
|
||||||
self.flavors = mock.Mock()
|
self.flavors = mock.Mock()
|
||||||
self.flavors.resource_class = fakes.FakeResource(None, {})
|
|
||||||
|
|
||||||
self.flavor_access = mock.Mock()
|
self.flavor_access = mock.Mock()
|
||||||
self.flavor_access.resource_class = fakes.FakeResource(None, {})
|
self.flavor_access.resource_class = fakes.FakeResource(None, {})
|
||||||
@ -777,27 +777,13 @@ class FakeFlavor(object):
|
|||||||
'os-flavor-access:is_public': True,
|
'os-flavor-access:is_public': True,
|
||||||
'description': 'description',
|
'description': 'description',
|
||||||
'OS-FLV-EXT-DATA:ephemeral': 0,
|
'OS-FLV-EXT-DATA:ephemeral': 0,
|
||||||
'properties': {'property': 'value'},
|
'extra_specs': {'property': 'value'},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Overwrite default attributes.
|
# Overwrite default attributes.
|
||||||
flavor_info.update(attrs)
|
flavor_info.update(attrs)
|
||||||
|
|
||||||
# Set default methods.
|
flavor = _flavor.Flavor(**flavor_info)
|
||||||
flavor_methods = {
|
|
||||||
'set_keys': None,
|
|
||||||
'unset_keys': None,
|
|
||||||
'get_keys': {'property': 'value'},
|
|
||||||
}
|
|
||||||
|
|
||||||
flavor = fakes.FakeResource(info=copy.deepcopy(flavor_info),
|
|
||||||
methods=flavor_methods,
|
|
||||||
loaded=True)
|
|
||||||
|
|
||||||
# Set attributes with special mappings in nova client.
|
|
||||||
flavor.disabled = flavor_info['OS-FLV-DISABLED:disabled']
|
|
||||||
flavor.is_public = flavor_info['os-flavor-access:is_public']
|
|
||||||
flavor.ephemeral = flavor_info['OS-FLV-EXT-DATA:ephemeral']
|
|
||||||
|
|
||||||
return flavor
|
return flavor
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@
|
|||||||
# 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 unittest import mock
|
from unittest import mock
|
||||||
from unittest.mock import call
|
|
||||||
|
|
||||||
import novaclient
|
from openstack.compute.v2 import flavor as _flavor
|
||||||
|
from openstack import exceptions as sdk_exceptions
|
||||||
|
from openstack import utils as sdk_utils
|
||||||
from osc_lib.cli import format_columns
|
from osc_lib.cli import format_columns
|
||||||
from osc_lib import exceptions
|
from osc_lib import exceptions
|
||||||
|
|
||||||
@ -31,13 +31,19 @@ class TestFlavor(compute_fakes.TestComputev2):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestFlavor, self).setUp()
|
super(TestFlavor, self).setUp()
|
||||||
|
|
||||||
# Get a shortcut to the FlavorManager Mock
|
# SDK mock
|
||||||
self.flavors_mock = self.app.client_manager.compute.flavors
|
self.app.client_manager.sdk_connection = mock.Mock()
|
||||||
self.flavors_mock.reset_mock()
|
self.app.client_manager.sdk_connection.compute = mock.Mock()
|
||||||
|
self.sdk_client = self.app.client_manager.sdk_connection.compute
|
||||||
# Get a shortcut to the FlavorAccessManager Mock
|
self.sdk_client.flavors = mock.Mock()
|
||||||
self.flavor_access_mock = self.app.client_manager.compute.flavor_access
|
self.sdk_client.find_flavor = mock.Mock()
|
||||||
self.flavor_access_mock.reset_mock()
|
self.sdk_client.delete_flavor = mock.Mock()
|
||||||
|
self.sdk_client.update_flavor = mock.Mock()
|
||||||
|
self.sdk_client.flavor_add_tenant_access = mock.Mock()
|
||||||
|
self.sdk_client.flavor_remove_tenant_access = mock.Mock()
|
||||||
|
self.sdk_client.create_flavor_extra_specs = mock.Mock()
|
||||||
|
self.sdk_client.update_flavor_extra_specs_property = mock.Mock()
|
||||||
|
self.sdk_client.delete_flavor_extra_specs_property = mock.Mock()
|
||||||
|
|
||||||
self.projects_mock = self.app.client_manager.identity.projects
|
self.projects_mock = self.app.client_manager.identity.projects
|
||||||
self.projects_mock.reset_mock()
|
self.projects_mock.reset_mock()
|
||||||
@ -48,6 +54,7 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
flavor = compute_fakes.FakeFlavor.create_one_flavor(
|
flavor = compute_fakes.FakeFlavor.create_one_flavor(
|
||||||
attrs={'links': 'flavor-links'})
|
attrs={'links': 'flavor-links'})
|
||||||
project = identity_fakes.FakeProject.create_one_project()
|
project = identity_fakes.FakeProject.create_one_project()
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
'OS-FLV-DISABLED:disabled',
|
'OS-FLV-DISABLED:disabled',
|
||||||
'OS-FLV-EXT-DATA:ephemeral',
|
'OS-FLV-EXT-DATA:ephemeral',
|
||||||
@ -60,17 +67,32 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
'ram',
|
'ram',
|
||||||
'rxtx_factor',
|
'rxtx_factor',
|
||||||
'swap',
|
'swap',
|
||||||
'vcpus',
|
'vcpus'
|
||||||
)
|
)
|
||||||
|
|
||||||
data = (
|
data = (
|
||||||
flavor.disabled,
|
flavor.is_disabled,
|
||||||
flavor.ephemeral,
|
flavor.ephemeral,
|
||||||
flavor.description,
|
flavor.description,
|
||||||
flavor.disk,
|
flavor.disk,
|
||||||
flavor.id,
|
flavor.id,
|
||||||
flavor.name,
|
flavor.name,
|
||||||
flavor.is_public,
|
flavor.is_public,
|
||||||
format_columns.DictColumn(flavor.properties),
|
format_columns.DictColumn(flavor.extra_specs),
|
||||||
|
flavor.ram,
|
||||||
|
flavor.rxtx_factor,
|
||||||
|
flavor.swap,
|
||||||
|
flavor.vcpus,
|
||||||
|
)
|
||||||
|
data_private = (
|
||||||
|
flavor.is_disabled,
|
||||||
|
flavor.ephemeral,
|
||||||
|
flavor.description,
|
||||||
|
flavor.disk,
|
||||||
|
flavor.id,
|
||||||
|
flavor.name,
|
||||||
|
False,
|
||||||
|
format_columns.DictColumn(flavor.extra_specs),
|
||||||
flavor.ram,
|
flavor.ram,
|
||||||
flavor.rxtx_factor,
|
flavor.rxtx_factor,
|
||||||
flavor.swap,
|
flavor.swap,
|
||||||
@ -82,7 +104,7 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
|
|
||||||
# Return a project
|
# Return a project
|
||||||
self.projects_mock.get.return_value = self.project
|
self.projects_mock.get.return_value = self.project
|
||||||
self.flavors_mock.create.return_value = self.flavor
|
self.sdk_client.create_flavor.return_value = self.flavor
|
||||||
self.cmd = flavor.CreateFlavor(self.app, None)
|
self.cmd = flavor.CreateFlavor(self.app, None)
|
||||||
|
|
||||||
def test_flavor_create_default_options(self):
|
def test_flavor_create_default_options(self):
|
||||||
@ -95,20 +117,20 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
default_args = (
|
default_args = {
|
||||||
self.flavor.name,
|
'name': self.flavor.name,
|
||||||
256,
|
'ram': 256,
|
||||||
1,
|
'vcpus': 1,
|
||||||
0,
|
'disk': 0,
|
||||||
'auto',
|
'id': None,
|
||||||
0,
|
'ephemeral': 0,
|
||||||
0,
|
'swap': 0,
|
||||||
1.0,
|
'rxtx_factor': 1.0,
|
||||||
True,
|
'is_public': True,
|
||||||
None,
|
}
|
||||||
)
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.create.assert_called_once_with(*default_args)
|
self.sdk_client.create_flavor.assert_called_once_with(**default_args)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(self.data, data)
|
||||||
@ -143,29 +165,44 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
args = (
|
args = {
|
||||||
self.flavor.name,
|
'name': self.flavor.name,
|
||||||
self.flavor.ram,
|
'ram': self.flavor.ram,
|
||||||
self.flavor.vcpus,
|
'vcpus': self.flavor.vcpus,
|
||||||
self.flavor.disk,
|
'disk': self.flavor.disk,
|
||||||
self.flavor.id,
|
'id': self.flavor.id,
|
||||||
self.flavor.ephemeral,
|
'ephemeral': self.flavor.ephemeral,
|
||||||
self.flavor.swap,
|
'swap': self.flavor.swap,
|
||||||
self.flavor.rxtx_factor,
|
'rxtx_factor': self.flavor.rxtx_factor,
|
||||||
self.flavor.is_public,
|
'is_public': self.flavor.is_public,
|
||||||
self.flavor.description,
|
'description': self.flavor.description
|
||||||
)
|
}
|
||||||
self.app.client_manager.compute.api_version = 2.55
|
|
||||||
with mock.patch.object(novaclient.api_versions,
|
props = {'property': 'value'}
|
||||||
'APIVersion',
|
|
||||||
return_value=2.55):
|
# SDK updates the flavor object instance. In order to make the
|
||||||
|
# verification clear and preciese let's create new flavor and change
|
||||||
|
# expected props this way
|
||||||
|
create_flavor = _flavor.Flavor(**self.flavor)
|
||||||
|
expected_flavor = _flavor.Flavor(**self.flavor)
|
||||||
|
expected_flavor.extra_specs = props
|
||||||
|
# convert expected data tuple to list to be able to modify it
|
||||||
|
cmp_data = list(self.data)
|
||||||
|
cmp_data[7] = format_columns.DictColumn(props)
|
||||||
|
self.sdk_client.create_flavor.return_value = create_flavor
|
||||||
|
self.sdk_client.create_flavor_extra_specs.return_value = \
|
||||||
|
expected_flavor
|
||||||
|
|
||||||
|
with mock.patch.object(sdk_utils, 'supports_microversion',
|
||||||
|
return_value=True):
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.create.assert_called_once_with(*args)
|
self.sdk_client.create_flavor.assert_called_once_with(**args)
|
||||||
self.flavor.set_keys.assert_called_once_with({'property': 'value'})
|
self.sdk_client.create_flavor_extra_specs.assert_called_once_with(
|
||||||
self.flavor.get_keys.assert_called_once_with()
|
create_flavor, props)
|
||||||
|
self.sdk_client.get_flavor_access.assert_not_called()
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(tuple(cmp_data), data)
|
||||||
|
|
||||||
def test_flavor_create_other_options(self):
|
def test_flavor_create_other_options(self):
|
||||||
|
|
||||||
@ -200,33 +237,47 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
args = (
|
args = {
|
||||||
self.flavor.name,
|
'name': self.flavor.name,
|
||||||
self.flavor.ram,
|
'ram': self.flavor.ram,
|
||||||
self.flavor.vcpus,
|
'vcpus': self.flavor.vcpus,
|
||||||
self.flavor.disk,
|
'disk': self.flavor.disk,
|
||||||
'auto',
|
'id': 'auto',
|
||||||
self.flavor.ephemeral,
|
'ephemeral': self.flavor.ephemeral,
|
||||||
self.flavor.swap,
|
'swap': self.flavor.swap,
|
||||||
self.flavor.rxtx_factor,
|
'rxtx_factor': self.flavor.rxtx_factor,
|
||||||
self.flavor.is_public,
|
'is_public': False,
|
||||||
self.flavor.description,
|
'description': self.flavor.description
|
||||||
)
|
}
|
||||||
self.app.client_manager.compute.api_version = 2.55
|
|
||||||
with mock.patch.object(novaclient.api_versions,
|
props = {'key1': 'value1', 'key2': 'value2'}
|
||||||
'APIVersion',
|
|
||||||
return_value=2.55):
|
# SDK updates the flavor object instance. In order to make the
|
||||||
|
# verification clear and preciese let's create new flavor and change
|
||||||
|
# expected props this way
|
||||||
|
create_flavor = _flavor.Flavor(**self.flavor)
|
||||||
|
expected_flavor = _flavor.Flavor(**self.flavor)
|
||||||
|
expected_flavor.extra_specs = props
|
||||||
|
expected_flavor.is_public = False
|
||||||
|
# convert expected data tuple to list to be able to modify it
|
||||||
|
cmp_data = list(self.data_private)
|
||||||
|
cmp_data[7] = format_columns.DictColumn(props)
|
||||||
|
self.sdk_client.create_flavor.return_value = create_flavor
|
||||||
|
self.sdk_client.create_flavor_extra_specs.return_value = \
|
||||||
|
expected_flavor
|
||||||
|
|
||||||
|
with mock.patch.object(sdk_utils, 'supports_microversion',
|
||||||
|
return_value=True):
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.create.assert_called_once_with(*args)
|
self.sdk_client.create_flavor.assert_called_once_with(**args)
|
||||||
self.flavor_access_mock.add_tenant_access.assert_called_with(
|
self.sdk_client.flavor_add_tenant_access.assert_called_with(
|
||||||
self.flavor.id,
|
self.flavor.id,
|
||||||
self.project.id,
|
self.project.id,
|
||||||
)
|
)
|
||||||
self.flavor.set_keys.assert_called_with(
|
self.sdk_client.create_flavor_extra_specs.assert_called_with(
|
||||||
{'key1': 'value1', 'key2': 'value2'})
|
create_flavor, props)
|
||||||
self.flavor.get_keys.assert_called_with()
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(cmp_data, data)
|
||||||
|
|
||||||
def test_public_flavor_create_with_project(self):
|
def test_public_flavor_create_with_project(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -278,29 +329,28 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
('name', self.flavor.name),
|
('name', self.flavor.name),
|
||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
self.app.client_manager.compute.api_version = 2.55
|
with mock.patch.object(sdk_utils, 'supports_microversion',
|
||||||
with mock.patch.object(novaclient.api_versions,
|
return_value=True):
|
||||||
'APIVersion',
|
|
||||||
return_value=2.55):
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
args = (
|
args = {
|
||||||
self.flavor.name,
|
'name': self.flavor.name,
|
||||||
self.flavor.ram,
|
'ram': self.flavor.ram,
|
||||||
self.flavor.vcpus,
|
'vcpus': self.flavor.vcpus,
|
||||||
self.flavor.disk,
|
'disk': self.flavor.disk,
|
||||||
self.flavor.id,
|
'id': self.flavor.id,
|
||||||
self.flavor.ephemeral,
|
'ephemeral': self.flavor.ephemeral,
|
||||||
self.flavor.swap,
|
'swap': self.flavor.swap,
|
||||||
self.flavor.rxtx_factor,
|
'rxtx_factor': self.flavor.rxtx_factor,
|
||||||
False,
|
'is_public': self.flavor.is_public,
|
||||||
'fake description',
|
'description': 'fake description'
|
||||||
)
|
}
|
||||||
|
|
||||||
self.flavors_mock.create.assert_called_once_with(*args)
|
self.sdk_client.create_flavor.assert_called_once_with(**args)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(self.data, data)
|
self.assertItemEqual(self.data_private, data)
|
||||||
|
|
||||||
def test_flavor_create_with_description_api_older(self):
|
def test_flavor_create_with_description_api_older(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -318,10 +368,8 @@ class TestFlavorCreate(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
self.app.client_manager.compute.api_version = 2.54
|
with mock.patch.object(sdk_utils, 'supports_microversion',
|
||||||
with mock.patch.object(novaclient.api_versions,
|
return_value=False):
|
||||||
'APIVersion',
|
|
||||||
return_value=2.55):
|
|
||||||
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
|
|
||||||
@ -333,9 +381,7 @@ class TestFlavorDelete(TestFlavor):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestFlavorDelete, self).setUp()
|
super(TestFlavorDelete, self).setUp()
|
||||||
|
|
||||||
self.flavors_mock.get = (
|
self.sdk_client.delete_flavor.return_value = None
|
||||||
compute_fakes.FakeFlavor.get_flavors(self.flavors))
|
|
||||||
self.flavors_mock.delete.return_value = None
|
|
||||||
|
|
||||||
self.cmd = flavor.DeleteFlavor(self.app, None)
|
self.cmd = flavor.DeleteFlavor(self.app, None)
|
||||||
|
|
||||||
@ -348,9 +394,13 @@ class TestFlavorDelete(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.sdk_client.find_flavor.return_value = self.flavors[0]
|
||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.flavors_mock.delete.assert_called_with(self.flavors[0].id)
|
self.sdk_client.find_flavor.assert_called_with(self.flavors[0].id,
|
||||||
|
ignore_missing=False)
|
||||||
|
self.sdk_client.delete_flavor.assert_called_with(self.flavors[0].id)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_delete_multiple_flavors(self):
|
def test_delete_multiple_flavors(self):
|
||||||
@ -362,12 +412,17 @@ class TestFlavorDelete(TestFlavor):
|
|||||||
]
|
]
|
||||||
|
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.sdk_client.find_flavor.side_effect = self.flavors
|
||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
calls = []
|
find_calls = [
|
||||||
for f in self.flavors:
|
mock.call(i.id, ignore_missing=False) for i in self.flavors
|
||||||
calls.append(call(f.id))
|
]
|
||||||
self.flavors_mock.delete.assert_has_calls(calls)
|
delete_calls = [mock.call(i.id) for i in self.flavors]
|
||||||
|
self.sdk_client.find_flavor.assert_has_calls(find_calls)
|
||||||
|
self.sdk_client.delete_flavor.assert_has_calls(delete_calls)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_multi_flavors_delete_with_exception(self):
|
def test_multi_flavors_delete_with_exception(self):
|
||||||
@ -380,11 +435,10 @@ class TestFlavorDelete(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
find_mock_result = [self.flavors[0], exceptions.CommandError]
|
self.sdk_client.find_flavor.side_effect = [
|
||||||
self.flavors_mock.get = (
|
self.flavors[0],
|
||||||
mock.Mock(side_effect=find_mock_result)
|
sdk_exceptions.ResourceNotFound
|
||||||
)
|
]
|
||||||
self.flavors_mock.find.side_effect = exceptions.NotFound(None)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.cmd.take_action(parsed_args)
|
self.cmd.take_action(parsed_args)
|
||||||
@ -392,15 +446,18 @@ class TestFlavorDelete(TestFlavor):
|
|||||||
except exceptions.CommandError as e:
|
except exceptions.CommandError as e:
|
||||||
self.assertEqual('1 of 2 flavors failed to delete.', str(e))
|
self.assertEqual('1 of 2 flavors failed to delete.', str(e))
|
||||||
|
|
||||||
self.flavors_mock.get.assert_any_call(self.flavors[0].id)
|
find_calls = [
|
||||||
self.flavors_mock.get.assert_any_call('unexist_flavor')
|
mock.call(self.flavors[0].id, ignore_missing=False),
|
||||||
self.flavors_mock.delete.assert_called_once_with(self.flavors[0].id)
|
mock.call('unexist_flavor', ignore_missing=False),
|
||||||
|
]
|
||||||
|
delete_calls = [mock.call(self.flavors[0].id)]
|
||||||
|
self.sdk_client.find_flavor.assert_has_calls(find_calls)
|
||||||
|
self.sdk_client.delete_flavor.assert_has_calls(delete_calls)
|
||||||
|
|
||||||
|
|
||||||
class TestFlavorList(TestFlavor):
|
class TestFlavorList(TestFlavor):
|
||||||
|
|
||||||
# Return value of self.flavors_mock.list().
|
_flavor = compute_fakes.FakeFlavor.create_one_flavor()
|
||||||
flavors = compute_fakes.FakeFlavor.create_flavors(count=1)
|
|
||||||
|
|
||||||
columns = (
|
columns = (
|
||||||
'ID',
|
'ID',
|
||||||
@ -418,24 +475,27 @@ class TestFlavorList(TestFlavor):
|
|||||||
)
|
)
|
||||||
|
|
||||||
data = ((
|
data = ((
|
||||||
flavors[0].id,
|
_flavor.id,
|
||||||
flavors[0].name,
|
_flavor.name,
|
||||||
flavors[0].ram,
|
_flavor.ram,
|
||||||
flavors[0].disk,
|
_flavor.disk,
|
||||||
flavors[0].ephemeral,
|
_flavor.ephemeral,
|
||||||
flavors[0].vcpus,
|
_flavor.vcpus,
|
||||||
flavors[0].is_public,
|
_flavor.is_public,
|
||||||
), )
|
),)
|
||||||
data_long = (data[0] + (
|
data_long = (data[0] + (
|
||||||
flavors[0].swap,
|
_flavor.swap,
|
||||||
flavors[0].rxtx_factor,
|
_flavor.rxtx_factor,
|
||||||
format_columns.DictColumn(flavors[0].properties)
|
format_columns.DictColumn(_flavor.extra_specs)
|
||||||
), )
|
), )
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestFlavorList, self).setUp()
|
super(TestFlavorList, self).setUp()
|
||||||
|
|
||||||
self.flavors_mock.list.return_value = self.flavors
|
self.api_mock = mock.Mock()
|
||||||
|
self.api_mock.side_effect = [[self._flavor], [], ]
|
||||||
|
|
||||||
|
self.sdk_client.flavors = self.api_mock
|
||||||
|
|
||||||
# Get the command object to test
|
# Get the command object to test
|
||||||
self.cmd = flavor.ListFlavor(self.app, None)
|
self.cmd = flavor.ListFlavor(self.app, None)
|
||||||
@ -458,16 +518,14 @@ class TestFlavorList(TestFlavor):
|
|||||||
# Set expected values
|
# Set expected values
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'is_public': True,
|
'is_public': True,
|
||||||
'limit': None,
|
|
||||||
'marker': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flavors_mock.list.assert_called_with(
|
self.sdk_client.flavors.assert_called_with(
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertEqual(self.data, tuple(data))
|
||||||
|
|
||||||
def test_flavor_list_all_flavors(self):
|
def test_flavor_list_all_flavors(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -487,16 +545,14 @@ class TestFlavorList(TestFlavor):
|
|||||||
# Set expected values
|
# Set expected values
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'is_public': None,
|
'is_public': None,
|
||||||
'limit': None,
|
|
||||||
'marker': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flavors_mock.list.assert_called_with(
|
self.sdk_client.flavors.assert_called_with(
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertEqual(self.data, tuple(data))
|
||||||
|
|
||||||
def test_flavor_list_private_flavors(self):
|
def test_flavor_list_private_flavors(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -516,16 +572,14 @@ class TestFlavorList(TestFlavor):
|
|||||||
# Set expected values
|
# Set expected values
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'is_public': False,
|
'is_public': False,
|
||||||
'limit': None,
|
|
||||||
'marker': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flavors_mock.list.assert_called_with(
|
self.sdk_client.flavors.assert_called_with(
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertEqual(self.data, tuple(data))
|
||||||
|
|
||||||
def test_flavor_list_public_flavors(self):
|
def test_flavor_list_public_flavors(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -545,16 +599,14 @@ class TestFlavorList(TestFlavor):
|
|||||||
# Set expected values
|
# Set expected values
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'is_public': True,
|
'is_public': True,
|
||||||
'limit': None,
|
|
||||||
'marker': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flavors_mock.list.assert_called_with(
|
self.sdk_client.flavors.assert_called_with(
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(tuple(self.data), tuple(data))
|
self.assertEqual(self.data, tuple(data))
|
||||||
|
|
||||||
def test_flavor_list_long(self):
|
def test_flavor_list_long(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
@ -574,11 +626,9 @@ class TestFlavorList(TestFlavor):
|
|||||||
# Set expected values
|
# Set expected values
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'is_public': True,
|
'is_public': True,
|
||||||
'limit': None,
|
|
||||||
'marker': None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flavors_mock.list.assert_called_with(
|
self.sdk_client.flavors.assert_called_with(
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -588,7 +638,7 @@ class TestFlavorList(TestFlavor):
|
|||||||
|
|
||||||
class TestFlavorSet(TestFlavor):
|
class TestFlavorSet(TestFlavor):
|
||||||
|
|
||||||
# Return value of self.flavors_mock.find().
|
# Return value of self.sdk_client.find_flavor().
|
||||||
flavor = compute_fakes.FakeFlavor.create_one_flavor(
|
flavor = compute_fakes.FakeFlavor.create_one_flavor(
|
||||||
attrs={'os-flavor-access:is_public': False})
|
attrs={'os-flavor-access:is_public': False})
|
||||||
project = identity_fakes.FakeProject.create_one_project()
|
project = identity_fakes.FakeProject.create_one_project()
|
||||||
@ -596,8 +646,7 @@ class TestFlavorSet(TestFlavor):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestFlavorSet, self).setUp()
|
super(TestFlavorSet, self).setUp()
|
||||||
|
|
||||||
self.flavors_mock.find.return_value = self.flavor
|
self.sdk_client.find_flavor.return_value = self.flavor
|
||||||
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
|
|
||||||
# Return a project
|
# Return a project
|
||||||
self.projects_mock.get.return_value = self.project
|
self.projects_mock.get.return_value = self.project
|
||||||
self.cmd = flavor.SetFlavor(self.app, None)
|
self.cmd = flavor.SetFlavor(self.app, None)
|
||||||
@ -614,9 +663,14 @@ class TestFlavorSet(TestFlavor):
|
|||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
is_public=None)
|
parsed_args.flavor,
|
||||||
self.flavor.set_keys.assert_called_with({'FOO': '"B A R"'})
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False
|
||||||
|
)
|
||||||
|
self.sdk_client.create_flavor_extra_specs.assert_called_with(
|
||||||
|
self.flavor.id,
|
||||||
|
{'FOO': '"B A R"'})
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_flavor_set_no_property(self):
|
def test_flavor_set_no_property(self):
|
||||||
@ -631,9 +685,13 @@ class TestFlavorSet(TestFlavor):
|
|||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
is_public=None)
|
parsed_args.flavor,
|
||||||
self.flavor.unset_keys.assert_called_with(['property'])
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False
|
||||||
|
)
|
||||||
|
self.sdk_client.delete_flavor_extra_specs_property.assert_called_with(
|
||||||
|
self.flavor.id, 'property')
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_flavor_set_project(self):
|
def test_flavor_set_project(self):
|
||||||
@ -649,13 +707,16 @@ class TestFlavorSet(TestFlavor):
|
|||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
is_public=None)
|
parsed_args.flavor,
|
||||||
self.flavor_access_mock.add_tenant_access.assert_called_with(
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False
|
||||||
|
)
|
||||||
|
self.sdk_client.flavor_add_tenant_access.assert_called_with(
|
||||||
self.flavor.id,
|
self.flavor.id,
|
||||||
self.project.id,
|
self.project.id,
|
||||||
)
|
)
|
||||||
self.flavor.set_keys.assert_not_called()
|
self.sdk_client.create_flavor_extra_specs.assert_not_called()
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_flavor_set_no_project(self):
|
def test_flavor_set_no_project(self):
|
||||||
@ -681,8 +742,9 @@ class TestFlavorSet(TestFlavor):
|
|||||||
self.cmd, arglist, verifylist)
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
def test_flavor_set_with_unexist_flavor(self):
|
def test_flavor_set_with_unexist_flavor(self):
|
||||||
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
|
self.sdk_client.find_flavor.side_effect = [
|
||||||
self.flavors_mock.find.side_effect = exceptions.NotFound(None)
|
sdk_exceptions.ResourceNotFound()
|
||||||
|
]
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
'--project', self.project.id,
|
'--project', self.project.id,
|
||||||
@ -708,9 +770,12 @@ class TestFlavorSet(TestFlavor):
|
|||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
is_public=None)
|
parsed_args.flavor,
|
||||||
self.flavor_access_mock.add_tenant_access.assert_not_called()
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False
|
||||||
|
)
|
||||||
|
self.sdk_client.flavor_add_tenant_access.assert_not_called()
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_flavor_set_description_api_newer(self):
|
def test_flavor_set_description_api_newer(self):
|
||||||
@ -724,11 +789,11 @@ class TestFlavorSet(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
self.app.client_manager.compute.api_version = 2.55
|
self.app.client_manager.compute.api_version = 2.55
|
||||||
with mock.patch.object(novaclient.api_versions,
|
with mock.patch.object(sdk_utils,
|
||||||
'APIVersion',
|
'supports_microversion',
|
||||||
return_value=2.55):
|
return_value=True):
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.update.assert_called_with(
|
self.sdk_client.update_flavor.assert_called_with(
|
||||||
flavor=self.flavor.id, description='description')
|
flavor=self.flavor.id, description='description')
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
@ -743,9 +808,9 @@ class TestFlavorSet(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
self.app.client_manager.compute.api_version = 2.54
|
self.app.client_manager.compute.api_version = 2.54
|
||||||
with mock.patch.object(novaclient.api_versions,
|
with mock.patch.object(sdk_utils,
|
||||||
'APIVersion',
|
'supports_microversion',
|
||||||
return_value=2.55):
|
return_value=False):
|
||||||
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
|
|
||||||
@ -760,11 +825,12 @@ class TestFlavorSet(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
self.app.client_manager.compute.api_version = 2.55
|
self.app.client_manager.compute.api_version = 2.55
|
||||||
with mock.patch.object(novaclient.api_versions,
|
|
||||||
'APIVersion',
|
with mock.patch.object(sdk_utils,
|
||||||
return_value=2.55):
|
'supports_microversion',
|
||||||
|
return_value=True):
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.update.assert_called_with(
|
self.sdk_client.update_flavor.assert_called_with(
|
||||||
flavor=self.flavor.id, description='description')
|
flavor=self.flavor.id, description='description')
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
@ -779,16 +845,17 @@ class TestFlavorSet(TestFlavor):
|
|||||||
]
|
]
|
||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
self.app.client_manager.compute.api_version = 2.54
|
self.app.client_manager.compute.api_version = 2.54
|
||||||
with mock.patch.object(novaclient.api_versions,
|
|
||||||
'APIVersion',
|
with mock.patch.object(sdk_utils,
|
||||||
return_value=2.55):
|
'supports_microversion',
|
||||||
|
return_value=False):
|
||||||
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
||||||
parsed_args)
|
parsed_args)
|
||||||
|
|
||||||
|
|
||||||
class TestFlavorShow(TestFlavor):
|
class TestFlavorShow(TestFlavor):
|
||||||
|
|
||||||
# Return value of self.flavors_mock.find().
|
# Return value of self.sdk_client.find_flavor().
|
||||||
flavor_access = compute_fakes.FakeFlavorAccess.create_one_flavor_access()
|
flavor_access = compute_fakes.FakeFlavorAccess.create_one_flavor_access()
|
||||||
flavor = compute_fakes.FakeFlavor.create_one_flavor()
|
flavor = compute_fakes.FakeFlavor.create_one_flavor()
|
||||||
|
|
||||||
@ -805,11 +872,11 @@ class TestFlavorShow(TestFlavor):
|
|||||||
'ram',
|
'ram',
|
||||||
'rxtx_factor',
|
'rxtx_factor',
|
||||||
'swap',
|
'swap',
|
||||||
'vcpus',
|
'vcpus'
|
||||||
)
|
)
|
||||||
|
|
||||||
data = (
|
data = (
|
||||||
flavor.disabled,
|
flavor.is_disabled,
|
||||||
flavor.ephemeral,
|
flavor.ephemeral,
|
||||||
None,
|
None,
|
||||||
flavor.description,
|
flavor.description,
|
||||||
@ -817,7 +884,7 @@ class TestFlavorShow(TestFlavor):
|
|||||||
flavor.id,
|
flavor.id,
|
||||||
flavor.name,
|
flavor.name,
|
||||||
flavor.is_public,
|
flavor.is_public,
|
||||||
format_columns.DictColumn(flavor.get_keys()),
|
format_columns.DictColumn(flavor.extra_specs),
|
||||||
flavor.ram,
|
flavor.ram,
|
||||||
flavor.rxtx_factor,
|
flavor.rxtx_factor,
|
||||||
flavor.swap,
|
flavor.swap,
|
||||||
@ -828,9 +895,8 @@ class TestFlavorShow(TestFlavor):
|
|||||||
super(TestFlavorShow, self).setUp()
|
super(TestFlavorShow, self).setUp()
|
||||||
|
|
||||||
# Return value of _find_resource()
|
# Return value of _find_resource()
|
||||||
self.flavors_mock.find.return_value = self.flavor
|
self.sdk_client.find_flavor.return_value = self.flavor
|
||||||
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
|
self.sdk_client.get_flavor_access.return_value = [self.flavor_access]
|
||||||
self.flavor_access_mock.list.return_value = [self.flavor_access]
|
|
||||||
self.cmd = flavor.ShowFlavor(self.app, None)
|
self.cmd = flavor.ShowFlavor(self.app, None)
|
||||||
|
|
||||||
def test_show_no_options(self):
|
def test_show_no_options(self):
|
||||||
@ -862,7 +928,7 @@ class TestFlavorShow(TestFlavor):
|
|||||||
'os-flavor-access:is_public': False,
|
'os-flavor-access:is_public': False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.flavors_mock.find.return_value = private_flavor
|
self.sdk_client.find_flavor.return_value = private_flavor
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
private_flavor.name,
|
private_flavor.name,
|
||||||
@ -872,7 +938,7 @@ class TestFlavorShow(TestFlavor):
|
|||||||
]
|
]
|
||||||
|
|
||||||
data_with_project = (
|
data_with_project = (
|
||||||
private_flavor.disabled,
|
private_flavor.is_disabled,
|
||||||
private_flavor.ephemeral,
|
private_flavor.ephemeral,
|
||||||
[self.flavor_access.tenant_id],
|
[self.flavor_access.tenant_id],
|
||||||
private_flavor.description,
|
private_flavor.description,
|
||||||
@ -880,7 +946,7 @@ class TestFlavorShow(TestFlavor):
|
|||||||
private_flavor.id,
|
private_flavor.id,
|
||||||
private_flavor.name,
|
private_flavor.name,
|
||||||
private_flavor.is_public,
|
private_flavor.is_public,
|
||||||
format_columns.DictColumn(private_flavor.get_keys()),
|
format_columns.DictColumn(private_flavor.extra_specs),
|
||||||
private_flavor.ram,
|
private_flavor.ram,
|
||||||
private_flavor.rxtx_factor,
|
private_flavor.rxtx_factor,
|
||||||
private_flavor.swap,
|
private_flavor.swap,
|
||||||
@ -891,7 +957,7 @@ class TestFlavorShow(TestFlavor):
|
|||||||
|
|
||||||
columns, data = self.cmd.take_action(parsed_args)
|
columns, data = self.cmd.take_action(parsed_args)
|
||||||
|
|
||||||
self.flavor_access_mock.list.assert_called_with(
|
self.sdk_client.get_flavor_access.assert_called_with(
|
||||||
flavor=private_flavor.id)
|
flavor=private_flavor.id)
|
||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertItemEqual(data_with_project, data)
|
self.assertItemEqual(data_with_project, data)
|
||||||
@ -899,7 +965,7 @@ class TestFlavorShow(TestFlavor):
|
|||||||
|
|
||||||
class TestFlavorUnset(TestFlavor):
|
class TestFlavorUnset(TestFlavor):
|
||||||
|
|
||||||
# Return value of self.flavors_mock.find().
|
# Return value of self.sdk_client.find_flavor().
|
||||||
flavor = compute_fakes.FakeFlavor.create_one_flavor(
|
flavor = compute_fakes.FakeFlavor.create_one_flavor(
|
||||||
attrs={'os-flavor-access:is_public': False})
|
attrs={'os-flavor-access:is_public': False})
|
||||||
project = identity_fakes.FakeProject.create_one_project()
|
project = identity_fakes.FakeProject.create_one_project()
|
||||||
@ -907,12 +973,13 @@ class TestFlavorUnset(TestFlavor):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestFlavorUnset, self).setUp()
|
super(TestFlavorUnset, self).setUp()
|
||||||
|
|
||||||
self.flavors_mock.find.return_value = self.flavor
|
self.sdk_client.find_flavor.return_value = self.flavor
|
||||||
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
|
|
||||||
# Return a project
|
# Return a project
|
||||||
self.projects_mock.get.return_value = self.project
|
self.projects_mock.get.return_value = self.project
|
||||||
self.cmd = flavor.UnsetFlavor(self.app, None)
|
self.cmd = flavor.UnsetFlavor(self.app, None)
|
||||||
|
|
||||||
|
self.mock_shortcut = self.sdk_client.delete_flavor_extra_specs_property
|
||||||
|
|
||||||
def test_flavor_unset_property(self):
|
def test_flavor_unset_property(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--property', 'property',
|
'--property', 'property',
|
||||||
@ -925,12 +992,49 @@ class TestFlavorUnset(TestFlavor):
|
|||||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
is_public=None)
|
parsed_args.flavor,
|
||||||
self.flavor.unset_keys.assert_called_with(['property'])
|
get_extra_specs=True,
|
||||||
self.flavor_access_mock.remove_tenant_access.assert_not_called()
|
ignore_missing=False)
|
||||||
|
self.mock_shortcut.assert_called_with(
|
||||||
|
self.flavor.id, 'property')
|
||||||
|
self.sdk_client.flavor_remove_tenant_access.assert_not_called()
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
def test_flavor_unset_properties(self):
|
||||||
|
arglist = [
|
||||||
|
'--property', 'property1',
|
||||||
|
'--property', 'property2',
|
||||||
|
'baremetal'
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('property', ['property1', 'property2']),
|
||||||
|
('flavor', 'baremetal'),
|
||||||
|
]
|
||||||
|
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
|
self.cmd.take_action(parsed_args)
|
||||||
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
|
parsed_args.flavor,
|
||||||
|
get_extra_specs=True,
|
||||||
|
ignore_missing=False)
|
||||||
|
calls = [
|
||||||
|
mock.call(self.flavor.id, 'property1'),
|
||||||
|
mock.call(self.flavor.id, 'property2')
|
||||||
|
]
|
||||||
|
self.mock_shortcut.assert_has_calls(
|
||||||
|
calls)
|
||||||
|
|
||||||
|
# A bit tricky way to ensure we do not unset other properties
|
||||||
|
calls.append(mock.call(self.flavor.id, 'property'))
|
||||||
|
self.assertRaises(
|
||||||
|
AssertionError,
|
||||||
|
self.mock_shortcut.assert_has_calls,
|
||||||
|
calls
|
||||||
|
)
|
||||||
|
|
||||||
|
self.sdk_client.flavor_remove_tenant_access.assert_not_called()
|
||||||
|
|
||||||
def test_flavor_unset_project(self):
|
def test_flavor_unset_project(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--project', self.project.id,
|
'--project', self.project.id,
|
||||||
@ -945,13 +1049,14 @@ class TestFlavorUnset(TestFlavor):
|
|||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
|
self.sdk_client.find_flavor.assert_called_with(
|
||||||
is_public=None)
|
parsed_args.flavor, get_extra_specs=True,
|
||||||
self.flavor_access_mock.remove_tenant_access.assert_called_with(
|
ignore_missing=False)
|
||||||
|
self.sdk_client.flavor_remove_tenant_access.assert_called_with(
|
||||||
self.flavor.id,
|
self.flavor.id,
|
||||||
self.project.id,
|
self.project.id,
|
||||||
)
|
)
|
||||||
self.flavor.unset_keys.assert_not_called()
|
self.sdk_client.delete_flavor_extra_specs_proerty.assert_not_called()
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
def test_flavor_unset_no_project(self):
|
def test_flavor_unset_no_project(self):
|
||||||
@ -977,8 +1082,9 @@ class TestFlavorUnset(TestFlavor):
|
|||||||
self.cmd, arglist, verifylist)
|
self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
def test_flavor_unset_with_unexist_flavor(self):
|
def test_flavor_unset_with_unexist_flavor(self):
|
||||||
self.flavors_mock.get.side_effect = exceptions.NotFound(None)
|
self.sdk_client.find_flavor.side_effect = [
|
||||||
self.flavors_mock.find.side_effect = exceptions.NotFound(None)
|
sdk_exceptions.ResourceNotFound
|
||||||
|
]
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
'--project', self.project.id,
|
'--project', self.project.id,
|
||||||
@ -1004,4 +1110,4 @@ class TestFlavorUnset(TestFlavor):
|
|||||||
result = self.cmd.take_action(parsed_args)
|
result = self.cmd.take_action(parsed_args)
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
self.flavor_access_mock.remove_tenant_access.assert_not_called()
|
self.sdk_client.flavor_remove_tenant_access.assert_not_called()
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Switch compute.flavor operations from direct API calls (novaclient) to
|
||||||
|
OpenStackSDK.
|
@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
|||||||
|
|
||||||
cliff>=3.4.0 # Apache-2.0
|
cliff>=3.4.0 # Apache-2.0
|
||||||
iso8601>=0.1.11 # MIT
|
iso8601>=0.1.11 # MIT
|
||||||
openstacksdk>=0.51.0 # Apache-2.0
|
openstacksdk>=0.52.0 # Apache-2.0
|
||||||
osc-lib>=2.2.0 # Apache-2.0
|
osc-lib>=2.2.0 # Apache-2.0
|
||||||
oslo.i18n>=3.15.3 # Apache-2.0
|
oslo.i18n>=3.15.3 # Apache-2.0
|
||||||
python-keystoneclient>=3.22.0 # Apache-2.0
|
python-keystoneclient>=3.22.0 # Apache-2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user