Skip to content

Commit ad2ac13

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add server migration list CLI"
2 parents a48c05b + b77c28d commit ad2ac13

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
@@ -1814,6 +1814,167 @@ def _show_progress(progress):
18141814
raise SystemExit
18151815

18161816

1817+
class ListMigration(command.Command):
1818+
_description = _("""List server migrations.""")
1819+
1820+
def get_parser(self, prog_name):
1821+
parser = super(ListMigration, self).get_parser(prog_name)
1822+
parser.add_argument(
1823+
"--server",
1824+
metavar="<server>",
1825+
dest='server',
1826+
default=None,
1827+
help=_('Server to show migration details (name or ID).')
1828+
)
1829+
parser.add_argument(
1830+
"--host",
1831+
metavar="<host>",
1832+
default=None,
1833+
help=_('Fetch migrations for the given host.')
1834+
)
1835+
parser.add_argument(
1836+
"--status",
1837+
metavar="<status>",
1838+
default=None,
1839+
help=_('Fetch migrations for the given status.')
1840+
)
1841+
parser.add_argument(
1842+
"--marker",
1843+
metavar="<marker>",
1844+
dest='marker',
1845+
default=None,
1846+
help=_("The last migration of the previous page; displays list "
1847+
"of migrations after 'marker'. Note that the marker is "
1848+
"the migration UUID. (Supported with "
1849+
"``--os-compute-api-version`` 2.59 or greater.)")
1850+
)
1851+
parser.add_argument(
1852+
"--limit",
1853+
metavar="<limit>",
1854+
dest='limit',
1855+
type=int,
1856+
default=None,
1857+
help=_("Maximum number of migrations to display. Note that there "
1858+
"is a configurable max limit on the server, and the limit "
1859+
"that is used will be the minimum of what is requested "
1860+
"here and what is configured in the server. "
1861+
"(Supported with ``--os-compute-api-version`` 2.59 "
1862+
"or greater.)")
1863+
)
1864+
parser.add_argument(
1865+
'--changes-since',
1866+
dest='changes_since',
1867+
metavar='<changes-since>',
1868+
default=None,
1869+
help=_("List only migrations changed later or equal to a certain "
1870+
"point of time. The provided time should be an ISO 8061 "
1871+
"formatted time, e.g. ``2016-03-04T06:27:59Z``. "
1872+
"(Supported with ``--os-compute-api-version`` 2.59 "
1873+
"or greater.)")
1874+
)
1875+
parser.add_argument(
1876+
'--changes-before',
1877+
dest='changes_before',
1878+
metavar='<changes-before>',
1879+
default=None,
1880+
help=_("List only migrations changed earlier or equal to a "
1881+
"certain point of time. The provided time should be an ISO "
1882+
"8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
1883+
"(Supported with ``--os-compute-api-version`` 2.66 or "
1884+
"greater.)")
1885+
)
1886+
parser.add_argument(
1887+
'--project',
1888+
metavar='<project>',
1889+
dest='project_id',
1890+
default=None,
1891+
help=_("Filter the migrations by the given project ID. "
1892+
"(Supported with ``--os-compute-api-version`` 2.80 "
1893+
"or greater.)"),
1894+
)
1895+
parser.add_argument(
1896+
'--user',
1897+
metavar='<user>',
1898+
dest='user_id',
1899+
default=None,
1900+
help=_("Filter the migrations by the given user ID. "
1901+
"(Supported with ``--os-compute-api-version`` 2.80 "
1902+
"or greater.)"),
1903+
)
1904+
return parser
1905+
1906+
def print_migrations(self, parsed_args, compute_client, migrations):
1907+
columns = ['Source Node', 'Dest Node', 'Source Compute',
1908+
'Dest Compute', 'Dest Host', 'Status',
1909+
'Server UUID', 'Old Flavor', 'New Flavor',
1910+
'Created At', 'Updated At']
1911+
1912+
# Insert migrations UUID after ID
1913+
if compute_client.api_version >= api_versions.APIVersion("2.59"):
1914+
columns.insert(0, "UUID")
1915+
1916+
# TODO(brinzhang): It also suppports filter migrations by type
1917+
# since 2.1. https://review.opendev.org/#/c/675117 supported
1918+
# filtering the migrations by 'migration_type' and 'source_compute'
1919+
# in novaclient, that will be added in OSC by follow-up.
1920+
if compute_client.api_version >= api_versions.APIVersion("2.23"):
1921+
columns.insert(0, "Id")
1922+
columns.insert(len(columns) - 2, "Type")
1923+
1924+
if compute_client.api_version >= api_versions.APIVersion("2.80"):
1925+
if parsed_args.project_id:
1926+
columns.insert(len(columns) - 2, "Project")
1927+
if parsed_args.user_id:
1928+
columns.insert(len(columns) - 2, "User")
1929+
1930+
columns_header = columns
1931+
return (columns_header, (utils.get_item_properties(
1932+
mig, columns) for mig in migrations))
1933+
1934+
def take_action(self, parsed_args):
1935+
compute_client = self.app.client_manager.compute
1936+
1937+
search_opts = {
1938+
"host": parsed_args.host,
1939+
"server": parsed_args.server,
1940+
"status": parsed_args.status,
1941+
}
1942+
1943+
if (parsed_args.marker or parsed_args.limit or
1944+
parsed_args.changes_since):
1945+
if compute_client.api_version < api_versions.APIVersion("2.59"):
1946+
msg = _("marker, limit and/or changes_since is not supported "
1947+
"for --os-compute-api-version less than 2.59")
1948+
raise exceptions.CommandError(msg)
1949+
if parsed_args.marker:
1950+
search_opts['marker'] = parsed_args.marker
1951+
if parsed_args.limit:
1952+
search_opts['limit'] = parsed_args.limit
1953+
if parsed_args.changes_since:
1954+
search_opts['changes_since'] = parsed_args.changes_since
1955+
1956+
if parsed_args.changes_before:
1957+
if compute_client.api_version < api_versions.APIVersion("2.66"):
1958+
msg = _("changes_before is not supported for "
1959+
"--os-compute-api-version less than 2.66")
1960+
raise exceptions.CommandError(msg)
1961+
search_opts['changes_before'] = parsed_args.changes_before
1962+
1963+
if parsed_args.project_id or parsed_args.user_id:
1964+
if compute_client.api_version < api_versions.APIVersion("2.80"):
1965+
msg = _("Project and/or user is not supported for "
1966+
"--os-compute-api-version less than 2.80")
1967+
raise exceptions.CommandError(msg)
1968+
if parsed_args.project_id:
1969+
search_opts['project_id'] = parsed_args.project_id
1970+
if parsed_args.user_id:
1971+
search_opts['user_id'] = parsed_args.user_id
1972+
1973+
migrations = compute_client.migrations.list(**search_opts)
1974+
1975+
return self.print_migrations(parsed_args, compute_client, migrations)
1976+
1977+
18171978
class PauseServer(command.Command):
18181979
_description = _("Pause server(s)")
18191980

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']
@@ -1570,3 +1574,89 @@ def __init__(self, verb, uri, value, remain,
15701574
self.remain = remain
15711575
self.unit = unit
15721576
self.next_available = next_available
1577+
1578+
1579+
class FakeServerMigration(object):
1580+
"""Fake one or more server migrations."""
1581+
1582+
@staticmethod
1583+
def create_one_server_migration(attrs=None, methods=None):
1584+
"""Create a fake server migration.
1585+
1586+
:param Dictionary attrs:
1587+
A dictionary with all attributes
1588+
:param Dictionary methods:
1589+
A dictionary with all methods
1590+
:return:
1591+
A FakeResource object, with id, type, and so on
1592+
"""
1593+
attrs = attrs or {}
1594+
methods = methods or {}
1595+
1596+
# Set default attributes.
1597+
migration_info = {
1598+
"dest_host": "10.0.2.15",
1599+
"status": "migrating",
1600+
"type": "migration",
1601+
"updated_at": "2017-01-31T08:03:25.000000",
1602+
"created_at": "2017-01-31T08:03:21.000000",
1603+
"dest_compute": "compute-" + uuid.uuid4().hex,
1604+
"id": random.randint(1, 999),
1605+
"source_node": "node-" + uuid.uuid4().hex,
1606+
"server": uuid.uuid4().hex,
1607+
"dest_node": "node-" + uuid.uuid4().hex,
1608+
"source_compute": "compute-" + uuid.uuid4().hex,
1609+
"uuid": uuid.uuid4().hex,
1610+
"old_instance_type_id": uuid.uuid4().hex,
1611+
"new_instance_type_id": uuid.uuid4().hex,
1612+
"project": uuid.uuid4().hex,
1613+
"user": uuid.uuid4().hex
1614+
}
1615+
1616+
# Overwrite default attributes.
1617+
migration_info.update(attrs)
1618+
1619+
migration = fakes.FakeResource(info=copy.deepcopy(migration_info),
1620+
methods=methods,
1621+
loaded=True)
1622+
return migration
1623+
1624+
@staticmethod
1625+
def create_server_migrations(attrs=None, methods=None, count=2):
1626+
"""Create multiple fake server migrations.
1627+
1628+
:param Dictionary attrs:
1629+
A dictionary with all attributes
1630+
:param Dictionary methods:
1631+
A dictionary with all methods
1632+
:param int count:
1633+
The number of server migrations to fake
1634+
:return:
1635+
A list of FakeResource objects faking the server migrations
1636+
"""
1637+
migrations = []
1638+
for i in range(0, count):
1639+
migrations.append(
1640+
FakeServerMigration.create_one_server_migration(
1641+
attrs, methods))
1642+
1643+
return migrations
1644+
1645+
@staticmethod
1646+
def get_server_migrations(migrations=None, count=2):
1647+
"""Get an iterable MagicMock object with a list of faked migrations.
1648+
1649+
If server migrations list is provided, then initialize the Mock object
1650+
with the list. Otherwise create one.
1651+
1652+
:param List migrations:
1653+
A list of FakeResource objects faking server migrations
1654+
:param int count:
1655+
The number of server migrations to fake
1656+
:return:
1657+
An iterable Mock object with side_effect set to a list of faked
1658+
server migrations
1659+
"""
1660+
if migrations is None:
1661+
migrations = FakeServerMigration.create_server_migrations(count)
1662+
return mock.Mock(side_effect=migrations)

0 commit comments

Comments
 (0)