[lxc-devel] [pylxd/master] Add live parameter for migrations
ajkavanagh on Github
lxc-bot at linuxcontainers.org
Thu May 2 10:19:06 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 550 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190502/ff423bbe/attachment.bin>
-------------- next part --------------
From 5c4663deef35c3041b2981580cf0903a5f3c7051 Mon Sep 17 00:00:00 2001
From: Alex Kavanagh <alex.kavanagh at canonical.com>
Date: Thu, 2 May 2019 11:15:43 +0100
Subject: [PATCH] Add live parameter for migrations
The LXD api provides a parameter to kick of live migrations. See
https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-optional-targetmember-1
for further details.
Closes: #338
Signed-off-by: Alex Kavanagh <alex.kavanagh at canonical.com>
---
doc/source/containers.rst | 10 ++++++-
pylxd/models/container.py | 42 ++++++++++++++++++++++------
pylxd/tests/models/test_container.py | 7 +++--
3 files changed, 47 insertions(+), 12 deletions(-)
diff --git a/doc/source/containers.rst b/doc/source/containers.rst
index 48d5ff92..00ec7070 100644
--- a/doc/source/containers.rst
+++ b/doc/source/containers.rst
@@ -68,7 +68,9 @@ Container methods
an url to an interactive websocket and the execution only starts after a client connected to the websocket.
- `migrate` - Migrate the container. The first argument is a client
connection to the destination server. This call is asynchronous, so
- `wait=True` is optional. The container on the new client is returned.
+ ``wait=True`` is optional. The container on the new client is returned. If
+ ``live=True`` is passed to the function call, then the container is live
+ migrated (see the LXD documentation for further details).
- `publish` - Publish the container as an image. Note the container must be stopped
in order to use this method. If `wait=True` is passed, then the image is returned.
@@ -165,6 +167,12 @@ the source server has to be reachable by the destination server otherwise the mi
This will migrate the container from source server to destination server
+To migrate a live container, user the ``live=True`` parameter:
+
+..code-block:: python
+
+ cont.migrate(client__destination, live=True, wait=True)
+
If you want an interactive shell in the container, you can attach to it via a websocket.
.. code-block:: python
diff --git a/pylxd/models/container.py b/pylxd/models/container.py
index d6dc4694..856ec291 100644
--- a/pylxd/models/container.py
+++ b/pylxd/models/container.py
@@ -475,15 +475,31 @@ def raw_interactive_execute(self, commands, environment=None):
return {'ws': '{}?secret={}'.format(parsed.path, fds['0']),
'control': '{}?secret={}'.format(parsed.path, fds['control'])}
- def migrate(self, new_client, wait=False):
+ def migrate(self, new_client, live=False, wait=False):
"""Migrate a container.
Destination host information is contained in the client
connection passed in.
- If the container is running, it either must be shut down
- first or criu must be installed on the source and destination
- machines.
+ If the `live` param is True, then a live migration is attempted,
+ otherwise a non live one is running.
+
+ If the container is running for live migration, it either must be shut
+ down first or criu must be installed on the source and destination
+ machines and the `live` param should be True.
+
+ :param new_client: the pylxd client connection to migrate the container
+ to.
+ :type new_client: :class:`pylxd.client.Client`
+ :param live: whether to perform a live migration
+ :type live: bool
+ :param wait: if True, wait for the migration to complete
+ :type wait: bool
+ :raises: LXDAPIException if any of the API calls fail.
+ :raises: ValueError if source of target are local connections
+ :returns: the response from LXD of the new container (the target of the
+ migration and not the operation if waited on.)
+ :rtype: :class:`requests.Response`
"""
if self.api.scheme in ('http+unix',):
raise ValueError('Cannot migrate from a local client connection')
@@ -491,7 +507,7 @@ def migrate(self, new_client, wait=False):
if self.status_code == 103:
try:
res = new_client.containers.create(
- self.generate_migration_data(), wait=wait)
+ self.generate_migration_data(live), wait=wait)
except LXDAPIException as e:
if e.response.status_code == 103:
self.delete()
@@ -500,19 +516,29 @@ def migrate(self, new_client, wait=False):
raise e
else:
res = new_client.containers.create(
- self.generate_migration_data(), wait=wait)
+ self.generate_migration_data(live), wait=wait)
self.delete()
return res
- def generate_migration_data(self):
+ def generate_migration_data(self, live=False):
"""Generate the migration data.
This method can be used to handle migrations where the client
connection uses the local unix socket. For more information on
migration, see `Container.migrate`.
+
+ :param live: Whether to include "live": "true" in the migration
+ :type live: bool
+ :raises: LXDAPIException if the request to migrate fails
+ :returns: dictionary of migration data suitable to send to an new
+ client to complete a migration.
+ :rtype: Dict[str, ANY]
"""
self.sync() # Make sure the object isn't stale
- response = self.api.post(json={'migration': True})
+ _json = {'migration': True}
+ if live:
+ _json['live'] = True
+ response = self.api.post(json=_json)
operation = self.client.operations.get(response.json()['operation'])
operation_url = self.client.api.operations[operation.id]._api_endpoint
secrets = response.json()['metadata']['metadata']
diff --git a/pylxd/tests/models/test_container.py b/pylxd/tests/models/test_container.py
index 795bebdc..f8447f7c 100644
--- a/pylxd/tests/models/test_container.py
+++ b/pylxd/tests/models/test_container.py
@@ -285,7 +285,7 @@ def test_migrate_exception_error(self, generate_migration_data):
from pylxd.client import Client
from pylxd.exceptions import LXDAPIException
- def generate_exception():
+ def generate_exception(*args, **kwargs):
response = mock.Mock()
response.status_code = 400
raise LXDAPIException(response)
@@ -309,17 +309,18 @@ def test_migrate_exception_running(self, generate_migration_data):
self.client, name='an-container')
an_container.status_code = 103
- def generate_exception():
+ def generate_exception(*args, **kwargs):
response = mock.Mock()
response.status_code = 103
raise LXDAPIException(response)
generate_migration_data.side_effect = generate_exception
- an_migrated_container = an_container.migrate(client2)
+ an_migrated_container = an_container.migrate(client2, live=True)
self.assertEqual('an-container', an_migrated_container.name)
self.assertEqual(client2, an_migrated_container.client)
+ generate_migration_data.assert_called_once_with(True)
def test_migrate_started(self):
"""A container is migrated."""
More information about the lxc-devel
mailing list