Skip to content

Commit

Permalink
Merge pull request #357 from ajkavanagh/add-restore-snapshot-method
Browse files Browse the repository at this point in the history
Add restore snapshot methods
  • Loading branch information
ChrisMacNaughton authored May 3, 2019
2 parents b231c0a + 40dc4dd commit 7794c86
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 2 deletions.
2 changes: 2 additions & 0 deletions doc/source/containers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Container methods
`wait=True` is optional. The container on the new client is returned.
- `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.
- `restore_snapshot` - Restore a snapshot by name.


Examples
Expand Down Expand Up @@ -196,6 +197,7 @@ A container object (returned by `get` or `all`) has the following methods:
image from the snapshot is bigger than the logical volume that is allocated
by lxc. See https://github.com/lxc/lxd/issues/2201 for more details. The solution
is to increase the `storage.lvm_volume_size` parameter in lxc.
- `restore` - restore the container to this snapshot.

.. code-block:: python
Expand Down
38 changes: 38 additions & 0 deletions pylxd/models/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,28 @@ def publish(self, public=False, wait=False):

return self.client.images.get(operation.metadata['fingerprint'])

def restore_snapshot(self, snapshot_name, wait=False):
"""Restore a snapshot using its name.
Attempts to restore a container using a snapshot previously made. The
container should be stopped, but the method does not enforce this
constraint, so an LXDAPIException may be raised if this method fails.
:param snapshot_name: the name of the snapshot to restore from
:type snapshot_name: str
:param wait: wait until the operation is completed.
:type wait: boolean
:raises: LXDAPIException if the the operation fails.
:returns: the original response from the restore operation (not the
operation result)
:rtype: :class:`requests.Response`
"""
response = self.api.put(json={"restore": snapshot_name})
if wait:
self.client.operations.wait_for_operation(
response.json()['operation'])
return response


class _CommandWebsocketClient(WebSocketBaseClient): # pragma: no cover
"""Handle a websocket for container.execute(...) and manage decoding of the
Expand Down Expand Up @@ -710,3 +732,19 @@ def publish(self, public=False, wait=False):
operation = self.client.operations.wait_for_operation(
response.json()['operation'])
return self.client.images.get(operation.metadata['fingerprint'])

def restore(self, wait=False):
"""Restore this snapshot.
Attempts to restore a container using this snapshot. The container
should be stopped, but the method does not enforce this constraint, so
an LXDAPIException may be raised if this method fails.
:param wait: wait until the operation is completed.
:type wait: boolean
:raises: LXDAPIException if the the operation fails.
:returns: the original response from the restore operation (not the
operation result)
:rtype: :class:`requests.Response`
"""
return self.container.restore_snapshot(self.name, wait)
17 changes: 15 additions & 2 deletions pylxd/tests/mock_lxd.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ def container_POST(request, context):
}


def container_PUT(request, context):
context.status_code = 202
return {
'type': 'async',
'operation': '/1.0/operations/operation-abc?project=default'
}


def container_DELETE(request, context):
context.status_code = 202
return json.dumps({
Expand Down Expand Up @@ -260,7 +268,7 @@ def snapshot_DELETE(request, context):
'method': 'GET',
'url': r'^http://pylxd.test/1.0/containers$',
},
{
{
'text': json.dumps({
'type': 'sync',
'metadata': [
Expand Down Expand Up @@ -332,7 +340,7 @@ def snapshot_DELETE(request, context):
'method': 'GET',
'url': r'^http://pylxd2.test/1.0/containers/an-container$',
},
{
{
'json': {
'type': 'sync',
'metadata': {
Expand Down Expand Up @@ -480,6 +488,11 @@ def snapshot_DELETE(request, context):
'method': 'POST',
'url': r'^http://pylxd.test/1.0/containers/an-container/exec$', # NOQA
},
{
'json': container_PUT,
'method': 'PUT',
'url': r'^http://pylxd.test/1.0/containers/an-container$',
},

# Container Snapshots
{
Expand Down
13 changes: 13 additions & 0 deletions pylxd/tests/models/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,12 @@ def test_publish(self):
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
image.fingerprint)

def test_restore_snapshot(self):
"""Snapshots can be restored"""
an_container = models.Container(
self.client, name='an-container')
an_container.restore_snapshot('thing')


class TestContainerState(testing.PyLXDTestCase):
"""Tests for pylxd.models.ContainerState."""
Expand Down Expand Up @@ -540,6 +546,13 @@ def test_publish(self):
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
image.fingerprint)

def test_restore_snapshot(self):
"""Snapshots can be restored from the snapshot object"""
snapshot = models.Snapshot(
self.client, container=self.container,
name='an-snapshot')
snapshot.restore(wait=True)


class TestFiles(testing.PyLXDTestCase):
"""Tests for pylxd.models.Container.files."""
Expand Down

0 comments on commit 7794c86

Please sign in to comment.