Skip to content

Commit b77c28d

Browse files
committed
Add server migration list CLI
Add ``openstack server migration list`` to fetch server migrations. Part of blueprint add-user-id-field-to-the-migrations-table Change-Id: I15b4a5aca8d0dee59dd293e7b1c7272cdfbeea20
1 parent 51aee43 commit b77c28d

6 files changed

Lines changed: 753 additions & 0 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
================
2+
server migration
3+
================
4+
5+
A server migration provides a way to move an instance from one
6+
host to another. There are four types of migration operation
7+
supported: live migration, cold migration, resize and evacuation.
8+
9+
Compute v2
10+
11+
.. autoprogram-cliff:: openstack.compute.v2
12+
:command: server migration list

openstackclient/compute/v2/server.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1768,6 +1768,167 @@ def _show_progress(progress):
17681768
raise SystemExit
17691769

17701770

1771+
class ListMigration(command.Command):
1772+
_description = _("""List server migrations.""")
1773+
1774+
def get_parser(self, prog_name):
1775+
parser = super(ListMigration, self).get_parser(prog_name)
1776+
parser.add_argument(
1777+
"--server",
1778+
metavar="<server>",
1779+
dest='server',
1780+
default=None,
1781+
help=_('Server to show migration details (name or ID).')
1782+
)
1783+
parser.add_argument(
1784+
"--host",
1785+
metavar="<host>",
1786+
default=None,
1787+
help=_('Fetch migrations for the given host.')
1788+
)
1789+
parser.add_argument(
1790+
"--status",
1791+
metavar="<status>",
1792+
default=None,
1793+
help=_('Fetch migrations for the given status.')
1794+
)
1795+
parser.add_argument(
1796+
"--marker",
1797+
metavar="<marker>",
1798+
dest='marker',
1799+
default=None,
1800+
help=_("The last migration of the previous page; displays list "
1801+
"of migrations after 'marker'. Note that the marker is "
1802+
"the migration UUID. (Supported with "
1803+
"``--os-compute-api-version`` 2.59 or greater.)")
1804+
)
1805+
parser.add_argument(
1806+
"--limit",
1807+
metavar="<limit>",
1808+
dest='limit',
1809+
type=int,
1810+
default=None,
1811+
help=_("Maximum number of migrations to display. Note that there "
1812+
"is a configurable max limit on the server, and the limit "
1813+
"that is used will be the minimum of what is requested "
1814+
"here and what is configured in the server. "
1815+
"(Supported with ``--os-compute-api-version`` 2.59 "
1816+
"or greater.)")
1817+
)
1818+
parser.add_argument(
1819+
'--changes-since',
1820+
dest='changes_since',
1821+
metavar='<changes-since>',
1822+
default=None,
1823+
help=_("List only migrations changed later or equal to a certain "
1824+
"point of time. The provided time should be an ISO 8061 "
1825+
"formatted time, e.g. ``2016-03-04T06:27:59Z``. "
1826+
"(Supported with ``--os-compute-api-version`` 2.59 "
1827+
"or greater.)")
1828+
)
1829+
parser.add_argument(
1830+
'--changes-before',
1831+
dest='changes_before',
1832+
metavar='<changes-before>',
1833+
default=None,
1834+
help=_("List only migrations changed earlier or equal to a "
1835+
"certain point of time. The provided time should be an ISO "
1836+
"8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
1837+
"(Supported with ``--os-compute-api-version`` 2.66 or "
1838+
"greater.)")
1839+
)
1840+
parser.add_argument(
1841+
'--project',
1842+
metavar='<project>',
1843+
dest='project_id',
1844+
default=None,
1845+
help=_("Filter the migrations by the given project ID. "
1846+
"(Supported with ``--os-compute-api-version`` 2.80 "
1847+
"or greater.)"),
1848+
)
1849+
parser.add_argument(
1850+
'--user',
1851+
metavar='<user>',
1852+
dest='user_id',
1853+
default=None,
1854+
help=_("Filter the migrations by the given user ID. "
1855+
"(Supported with ``--os-compute-api-version`` 2.80 "
1856+
"or greater.)"),
1857+
)
1858+
return parser
1859+
1860+
def print_migrations(self, parsed_args, compute_client, migrations):
1861+
columns = ['Source Node', 'Dest Node', 'Source Compute',
1862+
'Dest Compute', 'Dest Host', 'Status',
1863+
'Server UUID', 'Old Flavor', 'New Flavor',
1864+
'Created At', 'Updated At']
1865+
1866+
# Insert migrations UUID after ID
1867+
if compute_client.api_version >= api_versions.APIVersion("2.59"):
1868+
columns.insert(0, "UUID")
1869+
1870+
# TODO(brinzhang): It also suppports filter migrations by type
1871+
# since 2.1. https://review.opendev.org/#/c/675117 supported
1872+
# filtering the migrations by 'migration_type' and 'source_compute'
1873+
# in novaclient, that will be added in OSC by follow-up.
1874+
if compute_client.api_version >= api_versions.APIVersion("2.23"):
1875+
columns.insert(0, "Id")
1876+
columns.insert(len(columns) - 2, "Type")
1877+
1878+
if compute_client.api_version >= api_versions.APIVersion("2.80"):
1879+
if parsed_args.project_id:
1880+
columns.insert(len(columns) - 2, "Project")
1881+
if parsed_args.user_id:
1882+
columns.insert(len(columns) - 2, "User")
1883+
1884+
columns_header = columns
1885+
return (columns_header, (utils.get_item_properties(
1886+
mig, columns) for mig in migrations))
1887+
1888+
def take_action(self, parsed_args):
1889+
compute_client = self.app.client_manager.compute
1890+
1891+
search_opts = {
1892+
"host": parsed_args.host,
1893+
"server": parsed_args.server,
1894+
"status": parsed_args.status,
1895+
}
1896+
1897+
if (parsed_args.marker or parsed_args.limit or
1898+
parsed_args.changes_since):
1899+
if compute_client.api_version < api_versions.APIVersion("2.59"):
1900+
msg = _("marker, limit and/or changes_since is not supported "
1901+
"for --os-compute-api-version less than 2.59")
1902+
raise exceptions.CommandError(msg)
1903+
if parsed_args.marker:
1904+
search_opts['marker'] = parsed_args.marker
1905+
if parsed_args.limit:
1906+
search_opts['limit'] = parsed_args.limit
1907+
if parsed_args.changes_since:
1908+
search_opts['changes_since'] = parsed_args.changes_since
1909+
1910+
if parsed_args.changes_before:
1911+
if compute_client.api_version < api_versions.APIVersion("2.66"):
1912+
msg = _("changes_before is not supported for "
1913+
"--os-compute-api-version less than 2.66")
1914+
raise exceptions.CommandError(msg)
1915+
search_opts['changes_before'] = parsed_args.changes_before
1916+
1917+
if parsed_args.project_id or parsed_args.user_id:
1918+
if compute_client.api_version < api_versions.APIVersion("2.80"):
1919+
msg = _("Project and/or user is not supported for "
1920+
"--os-compute-api-version less than 2.80")
1921+
raise exceptions.CommandError(msg)
1922+
if parsed_args.project_id:
1923+
search_opts['project_id'] = parsed_args.project_id
1924+
if parsed_args.user_id:
1925+
search_opts['user_id'] = parsed_args.user_id
1926+
1927+
migrations = compute_client.migrations.list(**search_opts)
1928+
1929+
return self.print_migrations(parsed_args, compute_client, migrations)
1930+
1931+
17711932
class PauseServer(command.Command):
17721933
_description = _("Pause server(s)")
17731934

openstackclient/tests/unit/compute/v2/fakes.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#
1515

1616
import copy
17+
import random
1718
from unittest import mock
1819
import uuid
1920

@@ -198,6 +199,9 @@ def __init__(self, **kwargs):
198199
self.instance_action = mock.Mock()
199200
self.instance_action.resource_class = fakes.FakeResource(None, {})
200201

202+
self.migrations = mock.Mock()
203+
self.migrations.resource_class = fakes.FakeResource(None, {})
204+
201205
self.auth_token = kwargs['token']
202206

203207
self.management_url = kwargs['endpoint']
@@ -1539,3 +1543,89 @@ def __init__(self, verb, uri, value, remain,
15391543
self.remain = remain
15401544
self.unit = unit
15411545
self.next_available = next_available
1546+
1547+
1548+
class FakeServerMigration(object):
1549+
"""Fake one or more server migrations."""
1550+
1551+
@staticmethod
1552+
def create_one_server_migration(attrs=None, methods=None):
1553+
"""Create a fake server migration.
1554+
1555+
:param Dictionary attrs:
1556+
A dictionary with all attributes
1557+
:param Dictionary methods:
1558+
A dictionary with all methods
1559+
:return:
1560+
A FakeResource object, with id, type, and so on
1561+
"""
1562+
attrs = attrs or {}
1563+
methods = methods or {}
1564+
1565+
# Set default attributes.
1566+
migration_info = {
1567+
"dest_host": "10.0.2.15",
1568+
"status": "migrating",
1569+
"type": "migration",
1570+
"updated_at": "2017-01-31T08:03:25.000000",
1571+
"created_at": "2017-01-31T08:03:21.000000",
1572+
"dest_compute": "compute-" + uuid.uuid4().hex,
1573+
"id": random.randint(1, 999),
1574+
"source_node": "node-" + uuid.uuid4().hex,
1575+
"server": uuid.uuid4().hex,
1576+
"dest_node": "node-" + uuid.uuid4().hex,
1577+
"source_compute": "compute-" + uuid.uuid4().hex,
1578+
"uuid": uuid.uuid4().hex,
1579+
"old_instance_type_id": uuid.uuid4().hex,
1580+
"new_instance_type_id": uuid.uuid4().hex,
1581+
"project": uuid.uuid4().hex,
1582+
"user": uuid.uuid4().hex
1583+
}
1584+
1585+
# Overwrite default attributes.
1586+
migration_info.update(attrs)
1587+
1588+
migration = fakes.FakeResource(info=copy.deepcopy(migration_info),
1589+
methods=methods,
1590+
loaded=True)
1591+
return migration
1592+
1593+
@staticmethod
1594+
def create_server_migrations(attrs=None, methods=None, count=2):
1595+
"""Create multiple fake server migrations.
1596+
1597+
:param Dictionary attrs:
1598+
A dictionary with all attributes
1599+
:param Dictionary methods:
1600+
A dictionary with all methods
1601+
:param int count:
1602+
The number of server migrations to fake
1603+
:return:
1604+
A list of FakeResource objects faking the server migrations
1605+
"""
1606+
migrations = []
1607+
for i in range(0, count):
1608+
migrations.append(
1609+
FakeServerMigration.create_one_server_migration(
1610+
attrs, methods))
1611+
1612+
return migrations
1613+
1614+
@staticmethod
1615+
def get_server_migrations(migrations=None, count=2):
1616+
"""Get an iterable MagicMock object with a list of faked migrations.
1617+
1618+
If server migrations list is provided, then initialize the Mock object
1619+
with the list. Otherwise create one.
1620+
1621+
:param List migrations:
1622+
A list of FakeResource objects faking server migrations
1623+
:param int count:
1624+
The number of server migrations to fake
1625+
:return:
1626+
An iterable Mock object with side_effect set to a list of faked
1627+
server migrations
1628+
"""
1629+
if migrations is None:
1630+
migrations = FakeServerMigration.create_server_migrations(count)
1631+
return mock.Mock(side_effect=migrations)

0 commit comments

Comments
 (0)