summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/compute/v2/server.py226
-rw-r--r--openstackclient/compute/v2/server_backup.py5
-rw-r--r--openstackclient/compute/v2/server_image.py4
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py90
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py647
-rw-r--r--openstackclient/tests/unit/test_shell.py9
6 files changed, 955 insertions, 26 deletions
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index e5a7a328..f8d6aad0 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -21,6 +21,7 @@ import io
import logging
import os
+import iso8601
from novaclient import api_versions
from novaclient.v2 import servers
from openstack import exceptions as sdk_exceptions
@@ -29,7 +30,6 @@ from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
-from oslo_utils import timeutils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
@@ -292,6 +292,10 @@ class AddFloatingIP(network_common.NetworkAndComputeCommand):
parsed_args.server,
)
ports = list(client.ports(device_id=server.id))
+ if not ports:
+ msg = _('No attached ports found to associate floating IP with')
+ raise exceptions.CommandError(msg)
+
# If the fixed IP address was specified, we need to find the
# corresponding port.
if parsed_args.fixed_ip_address:
@@ -354,6 +358,14 @@ class AddPort(command.Command):
metavar="<port>",
help=_("Port to add to the server (name or ID)"),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ help=_(
+ "Tag for the attached interface. "
+ "(Supported by API versions '2.49' - '2.latest')"
+ )
+ )
return parser
def take_action(self, parsed_args):
@@ -369,7 +381,22 @@ class AddPort(command.Command):
else:
port_id = parsed_args.port
- server.interface_attach(port_id=port_id, net_id=None, fixed_ip=None)
+ kwargs = {
+ 'port_id': port_id,
+ 'net_id': None,
+ 'fixed_ip': None,
+ }
+
+ if parsed_args.tag:
+ if compute_client.api_version < api_versions.APIVersion("2.49"):
+ msg = _(
+ '--os-compute-api-version 2.49 or greater is required to '
+ 'support the --tag option'
+ )
+ raise exceptions.CommandError(msg)
+ kwargs['tag'] = parsed_args.tag
+
+ server.interface_attach(**kwargs)
class AddNetwork(command.Command):
@@ -387,6 +414,14 @@ class AddNetwork(command.Command):
metavar="<network>",
help=_("Network to add to the server (name or ID)"),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ help=_(
+ 'Tag for the attached interface. '
+ '(supported by --os-compute-api-version 2.49 or above)'
+ ),
+ )
return parser
def take_action(self, parsed_args):
@@ -402,7 +437,23 @@ class AddNetwork(command.Command):
else:
net_id = parsed_args.network
- server.interface_attach(port_id=None, net_id=net_id, fixed_ip=None)
+ kwargs = {
+ 'port_id': None,
+ 'net_id': net_id,
+ 'fixed_ip': None,
+ }
+
+ if parsed_args.tag:
+ if compute_client.api_version < api_versions.APIVersion('2.49'):
+ msg = _(
+ '--os-compute-api-version 2.49 or greater is required to '
+ 'support the --tag option'
+ )
+ raise exceptions.CommandError(msg)
+
+ kwargs['tag'] = parsed_args.tag
+
+ server.interface_attach(**kwargs)
class AddServerSecurityGroup(command.Command):
@@ -1404,8 +1455,8 @@ class ListServer(command.Lister):
raise exceptions.CommandError(msg)
try:
- timeutils.parse_isotime(search_opts['changes-before'])
- except ValueError:
+ iso8601.parse_date(search_opts['changes-before'])
+ except (TypeError, iso8601.ParseError):
raise exceptions.CommandError(
_('Invalid changes-before value: %s') %
search_opts['changes-before']
@@ -1413,8 +1464,8 @@ class ListServer(command.Lister):
if search_opts['changes-since']:
try:
- timeutils.parse_isotime(search_opts['changes-since'])
- except ValueError:
+ iso8601.parse_date(search_opts['changes-since'])
+ except (TypeError, iso8601.ParseError):
raise exceptions.CommandError(
_('Invalid changes-since value: %s') %
search_opts['changes-since']
@@ -1814,6 +1865,167 @@ revert to release the new server and restart the old one.""")
raise SystemExit
+class ListMigration(command.Command):
+ _description = _("""List server migrations.""")
+
+ def get_parser(self, prog_name):
+ parser = super(ListMigration, self).get_parser(prog_name)
+ parser.add_argument(
+ "--server",
+ metavar="<server>",
+ dest='server',
+ default=None,
+ help=_('Server to show migration details (name or ID).')
+ )
+ parser.add_argument(
+ "--host",
+ metavar="<host>",
+ default=None,
+ help=_('Fetch migrations for the given host.')
+ )
+ parser.add_argument(
+ "--status",
+ metavar="<status>",
+ default=None,
+ help=_('Fetch migrations for the given status.')
+ )
+ parser.add_argument(
+ "--marker",
+ metavar="<marker>",
+ dest='marker',
+ default=None,
+ help=_("The last migration of the previous page; displays list "
+ "of migrations after 'marker'. Note that the marker is "
+ "the migration UUID. (Supported with "
+ "``--os-compute-api-version`` 2.59 or greater.)")
+ )
+ parser.add_argument(
+ "--limit",
+ metavar="<limit>",
+ dest='limit',
+ type=int,
+ default=None,
+ help=_("Maximum number of migrations to display. Note that there "
+ "is a configurable max limit on the server, and the limit "
+ "that is used will be the minimum of what is requested "
+ "here and what is configured in the server. "
+ "(Supported with ``--os-compute-api-version`` 2.59 "
+ "or greater.)")
+ )
+ parser.add_argument(
+ '--changes-since',
+ dest='changes_since',
+ metavar='<changes-since>',
+ default=None,
+ help=_("List only migrations changed later or equal to a certain "
+ "point of time. The provided time should be an ISO 8061 "
+ "formatted time, e.g. ``2016-03-04T06:27:59Z``. "
+ "(Supported with ``--os-compute-api-version`` 2.59 "
+ "or greater.)")
+ )
+ parser.add_argument(
+ '--changes-before',
+ dest='changes_before',
+ metavar='<changes-before>',
+ default=None,
+ help=_("List only migrations changed earlier or equal to a "
+ "certain point of time. The provided time should be an ISO "
+ "8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
+ "(Supported with ``--os-compute-api-version`` 2.66 or "
+ "greater.)")
+ )
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ dest='project_id',
+ default=None,
+ help=_("Filter the migrations by the given project ID. "
+ "(Supported with ``--os-compute-api-version`` 2.80 "
+ "or greater.)"),
+ )
+ parser.add_argument(
+ '--user',
+ metavar='<user>',
+ dest='user_id',
+ default=None,
+ help=_("Filter the migrations by the given user ID. "
+ "(Supported with ``--os-compute-api-version`` 2.80 "
+ "or greater.)"),
+ )
+ return parser
+
+ def print_migrations(self, parsed_args, compute_client, migrations):
+ columns = ['Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status',
+ 'Server UUID', 'Old Flavor', 'New Flavor',
+ 'Created At', 'Updated At']
+
+ # Insert migrations UUID after ID
+ if compute_client.api_version >= api_versions.APIVersion("2.59"):
+ columns.insert(0, "UUID")
+
+ # TODO(brinzhang): It also suppports filter migrations by type
+ # since 2.1. https://review.opendev.org/#/c/675117 supported
+ # filtering the migrations by 'migration_type' and 'source_compute'
+ # in novaclient, that will be added in OSC by follow-up.
+ if compute_client.api_version >= api_versions.APIVersion("2.23"):
+ columns.insert(0, "Id")
+ columns.insert(len(columns) - 2, "Type")
+
+ if compute_client.api_version >= api_versions.APIVersion("2.80"):
+ if parsed_args.project_id:
+ columns.insert(len(columns) - 2, "Project")
+ if parsed_args.user_id:
+ columns.insert(len(columns) - 2, "User")
+
+ columns_header = columns
+ return (columns_header, (utils.get_item_properties(
+ mig, columns) for mig in migrations))
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+
+ search_opts = {
+ "host": parsed_args.host,
+ "server": parsed_args.server,
+ "status": parsed_args.status,
+ }
+
+ if (parsed_args.marker or parsed_args.limit or
+ parsed_args.changes_since):
+ if compute_client.api_version < api_versions.APIVersion("2.59"):
+ msg = _("marker, limit and/or changes_since is not supported "
+ "for --os-compute-api-version less than 2.59")
+ raise exceptions.CommandError(msg)
+ if parsed_args.marker:
+ search_opts['marker'] = parsed_args.marker
+ if parsed_args.limit:
+ search_opts['limit'] = parsed_args.limit
+ if parsed_args.changes_since:
+ search_opts['changes_since'] = parsed_args.changes_since
+
+ if parsed_args.changes_before:
+ if compute_client.api_version < api_versions.APIVersion("2.66"):
+ msg = _("changes_before is not supported for "
+ "--os-compute-api-version less than 2.66")
+ raise exceptions.CommandError(msg)
+ search_opts['changes_before'] = parsed_args.changes_before
+
+ if parsed_args.project_id or parsed_args.user_id:
+ if compute_client.api_version < api_versions.APIVersion("2.80"):
+ msg = _("Project and/or user is not supported for "
+ "--os-compute-api-version less than 2.80")
+ raise exceptions.CommandError(msg)
+ if parsed_args.project_id:
+ search_opts['project_id'] = parsed_args.project_id
+ if parsed_args.user_id:
+ search_opts['user_id'] = parsed_args.user_id
+
+ migrations = compute_client.migrations.list(**search_opts)
+
+ return self.print_migrations(parsed_args, compute_client, migrations)
+
+
class PauseServer(command.Command):
_description = _("Pause server(s)")
diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py
index a5d43fc6..b1b821b2 100644
--- a/openstackclient/compute/v2/server_backup.py
+++ b/openstackclient/compute/v2/server_backup.py
@@ -15,10 +15,11 @@
"""Compute v2 Server action implementations"""
+import importlib
+
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
-from oslo_utils import importutils
from openstackclient.i18n import _
@@ -119,7 +120,7 @@ class CreateServerBackup(command.ShowOne):
info['properties'] = utils.format_dict(info.get('properties', {}))
else:
# Get the right image module to format the output
- image_module = importutils.import_module(
+ image_module = importlib.import_module(
self.IMAGE_API_VERSIONS[
self.app.client_manager._api_version['image']
]
diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py
index fea87af8..c12bc2b3 100644
--- a/openstackclient/compute/v2/server_image.py
+++ b/openstackclient/compute/v2/server_image.py
@@ -15,12 +15,12 @@
"""Compute v2 Server action implementations"""
+import importlib
import logging
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
-from oslo_utils import importutils
from openstackclient.i18n import _
@@ -99,7 +99,7 @@ class CreateServerImage(command.ShowOne):
info['properties'] = utils.format_dict(info.get('properties', {}))
else:
# Get the right image module to format the output
- image_module = importutils.import_module(
+ image_module = importlib.import_module(
self.IMAGE_API_VERSIONS[
self.app.client_manager._api_version['image']
]
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 31430984..6aeb5da7 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -14,6 +14,7 @@
#
import copy
+import random
from unittest import mock
import uuid
@@ -198,6 +199,9 @@ class FakeComputev2Client(object):
self.instance_action = mock.Mock()
self.instance_action.resource_class = fakes.FakeResource(None, {})
+ self.migrations = mock.Mock()
+ self.migrations.resource_class = fakes.FakeResource(None, {})
+
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@@ -1570,3 +1574,89 @@ class FakeRateLimit(object):
self.remain = remain
self.unit = unit
self.next_available = next_available
+
+
+class FakeServerMigration(object):
+ """Fake one or more server migrations."""
+
+ @staticmethod
+ def create_one_server_migration(attrs=None, methods=None):
+ """Create a fake server migration.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param Dictionary methods:
+ A dictionary with all methods
+ :return:
+ A FakeResource object, with id, type, and so on
+ """
+ attrs = attrs or {}
+ methods = methods or {}
+
+ # Set default attributes.
+ migration_info = {
+ "dest_host": "10.0.2.15",
+ "status": "migrating",
+ "type": "migration",
+ "updated_at": "2017-01-31T08:03:25.000000",
+ "created_at": "2017-01-31T08:03:21.000000",
+ "dest_compute": "compute-" + uuid.uuid4().hex,
+ "id": random.randint(1, 999),
+ "source_node": "node-" + uuid.uuid4().hex,
+ "server": uuid.uuid4().hex,
+ "dest_node": "node-" + uuid.uuid4().hex,
+ "source_compute": "compute-" + uuid.uuid4().hex,
+ "uuid": uuid.uuid4().hex,
+ "old_instance_type_id": uuid.uuid4().hex,
+ "new_instance_type_id": uuid.uuid4().hex,
+ "project": uuid.uuid4().hex,
+ "user": uuid.uuid4().hex
+ }
+
+ # Overwrite default attributes.
+ migration_info.update(attrs)
+
+ migration = fakes.FakeResource(info=copy.deepcopy(migration_info),
+ methods=methods,
+ loaded=True)
+ return migration
+
+ @staticmethod
+ def create_server_migrations(attrs=None, methods=None, count=2):
+ """Create multiple fake server migrations.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param Dictionary methods:
+ A dictionary with all methods
+ :param int count:
+ The number of server migrations to fake
+ :return:
+ A list of FakeResource objects faking the server migrations
+ """
+ migrations = []
+ for i in range(0, count):
+ migrations.append(
+ FakeServerMigration.create_one_server_migration(
+ attrs, methods))
+
+ return migrations
+
+ @staticmethod
+ def get_server_migrations(migrations=None, count=2):
+ """Get an iterable MagicMock object with a list of faked migrations.
+
+ If server migrations list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List migrations:
+ A list of FakeResource objects faking server migrations
+ :param int count:
+ The number of server migrations to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ server migrations
+ """
+ if migrations is None:
+ migrations = FakeServerMigration.create_server_migrations(count)
+ return mock.Mock(side_effect=migrations)
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 59282b4a..a9f72cf6 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -19,11 +19,11 @@ import getpass
from unittest import mock
from unittest.mock import call
+import iso8601
from novaclient import api_versions
from openstack import exceptions as sdk_exceptions
from osc_lib import exceptions
from osc_lib import utils as common_utils
-from oslo_utils import timeutils
from openstackclient.compute.v2 import server
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -249,7 +249,7 @@ class TestServerAddFloatingIPNetwork(
# Get the command object to test
self.cmd = server.AddFloatingIP(self.app, self.namespace)
- def test_server_add_floating_ip_default(self):
+ def test_server_add_floating_ip(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
_port = network_fakes.FakePort.create_one_port()
@@ -284,8 +284,41 @@ class TestServerAddFloatingIPNetwork(
**attrs
)
- def test_server_add_floating_ip_default_no_external_gateway(self,
- success=False):
+ def test_server_add_floating_ip_no_ports(self):
+ server = compute_fakes.FakeServer.create_one_server()
+ floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+
+ self.servers_mock.get.return_value = server
+ self.network.find_ip = mock.Mock(return_value=floating_ip)
+ self.network.ports = mock.Mock(return_value=[])
+
+ arglist = [
+ server.id,
+ floating_ip['floating_ip_address'],
+ ]
+ verifylist = [
+ ('server', server.id),
+ ('ip_address', floating_ip['floating_ip_address']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ 'No attached ports found to associate floating IP with',
+ str(ex))
+
+ self.network.find_ip.assert_called_once_with(
+ floating_ip['floating_ip_address'],
+ ignore_missing=False,
+ )
+ self.network.ports.assert_called_once_with(
+ device_id=server.id,
+ )
+
+ def test_server_add_floating_ip_no_external_gateway(self, success=False):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
_port = network_fakes.FakePort.create_one_port()
@@ -338,11 +371,10 @@ class TestServerAddFloatingIPNetwork(
**attrs
)
- def test_server_add_floating_ip_default_one_external_gateway(self):
- self.test_server_add_floating_ip_default_no_external_gateway(
- success=True)
+ def test_server_add_floating_ip_one_external_gateway(self):
+ self.test_server_add_floating_ip_no_external_gateway(success=True)
- def test_server_add_floating_ip_fixed(self):
+ def test_server_add_floating_ip_with_fixed_ip(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
_port = network_fakes.FakePort.create_one_port()
@@ -384,7 +416,7 @@ class TestServerAddFloatingIPNetwork(
**attrs
)
- def test_server_add_floating_ip_fixed_no_port_found(self):
+ def test_server_add_floating_ip_with_fixed_ip_no_port_found(self):
_server = compute_fakes.FakeServer.create_one_server()
self.servers_mock.get.return_value = _server
_port = network_fakes.FakePort.create_one_port()
@@ -465,6 +497,59 @@ class TestServerAddPort(TestServer):
self._test_server_add_port('fake-port')
self.find_port.assert_not_called()
+ def test_server_add_port_with_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.49')
+
+ servers = self.setup_servers_mock(count=1)
+ self.find_port.return_value.id = 'fake-port'
+ arglist = [
+ servers[0].id,
+ 'fake-port',
+ '--tag', 'tag1',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('port', 'fake-port'),
+ ('tag', 'tag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.assertIsNone(result)
+
+ servers[0].interface_attach.assert_called_once_with(
+ port_id='fake-port',
+ net_id=None,
+ fixed_ip=None,
+ tag='tag1')
+
+ def test_server_add_port_with_tag_pre_v249(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.48')
+
+ servers = self.setup_servers_mock(count=1)
+ self.find_port.return_value.id = 'fake-port'
+ arglist = [
+ servers[0].id,
+ 'fake-port',
+ '--tag', 'tag1',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('port', 'fake-port'),
+ ('tag', 'tag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.49 or greater is required',
+ str(ex))
+
class TestServerVolume(TestServer):
@@ -681,6 +766,62 @@ class TestServerAddNetwork(TestServer):
self._test_server_add_network('fake-network')
self.find_network.assert_not_called()
+ def test_server_add_network_with_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.49')
+
+ servers = self.setup_servers_mock(count=1)
+ self.find_network.return_value.id = 'fake-network'
+
+ arglist = [
+ servers[0].id,
+ 'fake-network',
+ '--tag', 'tag1',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', 'fake-network'),
+ ('tag', 'tag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.assertIsNone(result)
+
+ servers[0].interface_attach.assert_called_once_with(
+ port_id=None,
+ net_id='fake-network',
+ fixed_ip=None,
+ tag='tag1'
+ )
+
+ def test_server_add_network_with_tag_pre_v249(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.48')
+
+ servers = self.setup_servers_mock(count=1)
+ self.find_network.return_value.id = 'fake-network'
+
+ arglist = [
+ servers[0].id,
+ 'fake-network',
+ '--tag', 'tag1',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', 'fake-network'),
+ ('tag', 'tag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.49 or greater is required',
+ str(ex))
+
@mock.patch(
'openstackclient.api.compute_v2.APIv2.security_group_find'
@@ -2945,7 +3086,7 @@ class TestServerList(TestServer):
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
- @mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
+ @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError)
def test_server_list_with_invalid_changes_since(self, mock_parse_isotime):
arglist = [
@@ -2988,7 +3129,7 @@ class TestServerList(TestServer):
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
- @mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
+ @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError)
def test_server_list_v266_with_invalid_changes_before(
self, mock_parse_isotime):
self.app.client_manager.compute.api_version = (
@@ -3582,6 +3723,490 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
+class TestServerMigration(TestServer):
+
+ def setUp(self):
+ super(TestServerMigration, self).setUp()
+
+ # Get a shortcut to the compute client ServerManager Mock
+ self.servers_mock = self.app.client_manager.compute.servers
+ self.servers_mock.reset_mock()
+
+ self.migrations_mock = (
+ self.app.client_manager.compute.migrations)
+ self.migrations_mock.reset_mock()
+
+ self.server = self.setup_servers_mock(1)[0]
+
+ def setup_servers_mock(self, count):
+ servers = compute_fakes.FakeServer.create_servers(count=count)
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get = compute_fakes.FakeServer.get_servers(servers)
+ return servers
+
+ def setup_server_migrations_mock(self, count):
+ return compute_fakes.FakeServerMigration.create_server_migrations(
+ count=count)
+
+
+class TestListMigration(TestServerMigration):
+ """Test fetch all migrations."""
+
+ MIGRATION_COLUMNS = [
+ 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Created At', 'Updated At'
+ ]
+
+ def setUp(self):
+ super(TestListMigration, self).setUp()
+
+ self.cmd = server.ListMigration(self.app, None)
+ self.migrations = self.setup_server_migrations_mock(3)
+ self.migrations_mock.list.return_value = self.migrations
+ self.setup_server_migrations_data(self.migrations)
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.20')
+
+ def setup_server_migrations_data(self, migrations):
+ self.data = (common_utils.get_item_properties(
+ s, self.MIGRATION_COLUMNS) for s in migrations)
+
+ def test_server_migraton_list(self):
+ arglist = [
+ '--status', 'migrating'
+ ]
+ verifylist = [
+ ('status', 'migrating')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'host': None,
+ 'server': None,
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+
+class TestListMigrationV223(TestListMigration):
+ """Test fetch all migrations. """
+
+ MIGRATION_COLUMNS = [
+ 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Created At', 'Updated At'
+ ]
+
+ def setUp(self):
+ super(TestListMigrationV223, self).setUp()
+ self.cmd = server.ListMigration(self.app, None)
+ self.migrations = self.setup_server_migrations_mock(3)
+ self.migrations_mock.list.return_value = self.migrations
+ self.setup_server_migrations_data(self.migrations)
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.23')
+
+ def test_server_migraton_list(self):
+ arglist = [
+ '--status', 'migrating'
+ ]
+ verifylist = [
+ ('status', 'migrating')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'host': None,
+ 'server': None,
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(0, "Id")
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, 'Type')
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+
+class TestListMigrationV259(TestListMigration):
+ """Test fetch all migrations. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
+ ]
+
+ def setUp(self):
+ super(TestListMigrationV259, self).setUp()
+ self.cmd = server.ListMigration(self.app, None)
+ self.migrations = self.setup_server_migrations_mock(3)
+ self.migrations_mock.list.return_value = self.migrations
+ self.setup_server_migrations_data(self.migrations)
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.59')
+
+ def test_server_migraton_list(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-09T08:03:25Z')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'server': None,
+ 'changes_since': '2019-08-09T08:03:25Z',
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_migraton_list_with_limit_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_server_migraton_list_with_marker_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+ arglist = [
+ '--status', 'migrating',
+ '--marker', 'test_kp'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('marker', 'test_kp')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_server_migraton_list_with_changes_since_pre_v259(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.58')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-since', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_since', '2019-08-09T08:03:25Z')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+
+class TestListMigrationV266(TestListMigration):
+ """Test fetch all migrations by changes-before. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
+ ]
+
+ def setUp(self):
+ super(TestListMigrationV266, self).setUp()
+ self.cmd = server.ListMigration(self.app, None)
+ self.migrations = self.setup_server_migrations_mock(3)
+ self.migrations_mock.list.return_value = self.migrations
+ self.setup_server_migrations_data(self.migrations)
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.66')
+
+ def test_server_migraton_list_with_changes_before(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'server': None,
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': '2019-08-09T08:03:25Z',
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_migraton_list_with_changes_before_pre_v266(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.65')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+
+class TestListMigrationV280(TestListMigration):
+ """Test fetch all migrations by user-id and/or project-id. """
+
+ MIGRATION_COLUMNS = [
+ 'Id', 'UUID', 'Source Node', 'Dest Node', 'Source Compute',
+ 'Dest Compute', 'Dest Host', 'Status', 'Server UUID',
+ 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At'
+ ]
+
+ def setUp(self):
+ super(TestListMigrationV280, self).setUp()
+ self.cmd = server.ListMigration(self.app, None)
+ self.migrations = self.setup_server_migrations_mock(3)
+ self.migrations_mock.list.return_value = self.migrations
+ self.setup_server_migrations_data(self.migrations)
+
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.80')
+
+ def test_server_migraton_list_with_project(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', '0c2accde-644a-45fa-8c10-e76debc7fbc3'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project_id', '0c2accde-644a-45fa-8c10-e76debc7fbc3')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'server': None,
+ 'project_id': '0c2accde-644a-45fa-8c10-e76debc7fbc3',
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': "2019-08-09T08:03:25Z",
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "Project")
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+ # Clean up global variables MIGRATION_COLUMNS
+ self.MIGRATION_COLUMNS.remove('Project')
+
+ def test_get_migrations_with_project_pre_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.79')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', '0c2accde-644a-45fa-8c10-e76debc7fbc3'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project_id', '0c2accde-644a-45fa-8c10-e76debc7fbc3')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_server_migraton_list_with_user(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--marker', 'test_kp',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--user', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('marker', 'test_kp'),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('user_id', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'marker': 'test_kp',
+ 'host': None,
+ 'server': None,
+ 'user_id': 'dd214878-ca12-40fb-b035-fa7d2c1e86d6',
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': "2019-08-09T08:03:25Z",
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "User")
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+ # Clean up global variables MIGRATION_COLUMNS
+ self.MIGRATION_COLUMNS.remove('User')
+
+ def test_get_migrations_with_user_pre_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.79')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--user', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('user_id', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+ def test_server_migraton_list_with_project_and_user(self):
+ arglist = [
+ '--status', 'migrating',
+ '--limit', '1',
+ '--changes-since', '2019-08-07T08:03:25Z',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', '0c2accde-644a-45fa-8c10-e76debc7fbc3',
+ '--user', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('limit', 1),
+ ('changes_since', '2019-08-07T08:03:25Z'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project_id', '0c2accde-644a-45fa-8c10-e76debc7fbc3'),
+ ('user_id', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': 'migrating',
+ 'limit': 1,
+ 'host': None,
+ 'server': None,
+ 'project_id': '0c2accde-644a-45fa-8c10-e76debc7fbc3',
+ 'user_id': 'dd214878-ca12-40fb-b035-fa7d2c1e86d6',
+ 'changes_since': '2019-08-07T08:03:25Z',
+ 'changes_before': "2019-08-09T08:03:25Z",
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "Project")
+ self.MIGRATION_COLUMNS.insert(
+ len(self.MIGRATION_COLUMNS) - 2, "User")
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+ # Clean up global variables MIGRATION_COLUMNS
+ self.MIGRATION_COLUMNS.remove('Project')
+ self.MIGRATION_COLUMNS.remove('User')
+
+ def test_get_migrations_with_project_and_user_pre_v280(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.79')
+ arglist = [
+ '--status', 'migrating',
+ '--changes-before', '2019-08-09T08:03:25Z',
+ '--project', '0c2accde-644a-45fa-8c10-e76debc7fbc3',
+ '--user', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6'
+ ]
+ verifylist = [
+ ('status', 'migrating'),
+ ('changes_before', '2019-08-09T08:03:25Z'),
+ ('project_id', '0c2accde-644a-45fa-8c10-e76debc7fbc3'),
+ ('user_id', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError, self.cmd.take_action,
+ parsed_args)
+
+
class TestServerPause(TestServer):
def setUp(self):
diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py
index 94f4f44d..366c364e 100644
--- a/openstackclient/tests/unit/test_shell.py
+++ b/openstackclient/tests/unit/test_shell.py
@@ -13,12 +13,12 @@
# under the License.
#
+import importlib
import os
import sys
from unittest import mock
from osc_lib.tests import utils as osc_lib_test_utils
-from oslo_utils import importutils
import wrapt
from openstackclient import shell
@@ -151,12 +151,13 @@ class TestShell(osc_lib_test_utils.TestShell):
super(TestShell, self).setUp()
# TODO(dtroyer): remove this once the shell_class_patch patch is
# released in osc-lib
- self.shell_class = importutils.import_class(self.shell_class_name)
+ mod_str, _sep, class_str = self.shell_class_name.rpartition('.')
+ self.shell_class = getattr(importlib.import_module(mod_str), class_str)
def _assert_admin_token_auth(self, cmd_options, default_args):
with mock.patch(
- self.shell_class_name + ".initialize_app",
- self.app,
+ self.shell_class_name + ".initialize_app",
+ self.app,
):
_shell = osc_lib_test_utils.make_shell(
shell_class=self.shell_class,