Skip to content

Commit

Permalink
Merge pull request #1087 from mulkieran/issue_stratis-cli_1085
Browse files Browse the repository at this point in the history
Support snapshot revert operations
  • Loading branch information
mulkieran authored Oct 30, 2024
2 parents 288314f + 18ce835 commit 354ceb8
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 3 deletions.
9 changes: 8 additions & 1 deletion docs/stratis.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ filesystem set-size-limit <pool_name> <fs_name> <size_limit>::
as the filesystem's current size, use the keyword "current".
filesystem unset-size-limit <pool_name> <fs_name>::
Remove a filesystem size limit previously set.
filesystem schedule-revert <pool_name> <snapshot_name>::
Set a flag so that when the pool is next started, the specified snapshot
will overwrite its origin filesystem.
filesystem cancel-revert <pool_name> <snapshot_name>::
Cancel a scheduled revert.
filesystem debug get-object-path <(--uuid <uuid> |--name <name>)> ::
Look up the D-Bus object path for a filesystem given the UUID or name.
filesystem debug get-metadata <pool_name> [--pretty] [--written] [--fs-name <fs-name>] ::
Expand Down Expand Up @@ -381,7 +386,9 @@ Created::
The time the filesystem was created.

Snapshot origin::
If this filesystem is a snapshot, its origin.
If this filesystem is a snapshot, its origin. If the filesystem is
a snapshot, whether or not it is scheduled to replace its origin
next time the pool is started.

Sizes::
The logical size of the Stratis filesystem, the total space actually
Expand Down
1 change: 1 addition & 0 deletions src/stratis_cli/_actions/_introspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
<property name="Devnode" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates" />
</property>
<property name="MergeScheduled" type="b" access="readwrite" />
<property name="Name" type="s" access="read" />
<property name="Origin" type="(bs)" access="read" />
<property name="Pool" type="o" access="read">
Expand Down
7 changes: 5 additions & 2 deletions src/stratis_cli/_actions/_list_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def display(self):
date_parser.parse(fs.Created()).astimezone().strftime("%b %d %Y %H:%M")
)

origin = get_property(fs.Origin(), self.uuid_formatter, "None")
origin = get_property(fs.Origin(), self.uuid_formatter, None)

print(f"UUID: {self.uuid_formatter(fs.Uuid())}")
print(f"Name: {fs.Name()}")
Expand All @@ -186,7 +186,10 @@ def display(self):
print()
print(f"Created: {created}")
print()
print(f"Snapshot origin: {origin}")
print(f'Snapshot origin: {"None" if origin is None else origin}')
if origin is not None:
scheduled = "Yes" if fs.MergeScheduled() else "No"
print(f" Revert scheduled: {scheduled}")
print()
print("Sizes:")
print(f" Logical size of thin device: {total}")
Expand Down
62 changes: 62 additions & 0 deletions src/stratis_cli/_actions/_logical.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .._constants import Id, IdType
from .._errors import (
StratisCliEngineError,
StratisCliFsMergeScheduledChangeError,
StratisCliFsSizeLimitChangeError,
StratisCliIncoherenceError,
StratisCliNoChangeError,
Expand Down Expand Up @@ -336,3 +337,64 @@ def unset_size_limit(namespace):
raise StratisCliFsSizeLimitChangeError(None)

Filesystem.Properties.SizeLimit.Set(get_object(fs_object_path), (False, ""))

@staticmethod
def schedule_revert(namespace):
"""
Schedule reverting a snapshot into its origin.
"""
# pylint: disable=import-outside-toplevel
from ._data import Filesystem, MOFilesystem, ObjectManager, filesystems, pools

proxy = get_object(TOP_OBJECT)
managed_objects = ObjectManager.Methods.GetManagedObjects(proxy, {})
(pool_object_path, _) = next(
pools(props={"Name": namespace.pool_name})
.require_unique_match(True)
.search(managed_objects)
)
(fs_object_path, fs_info) = next(
filesystems(
props={"Name": namespace.snapshot_name, "Pool": pool_object_path}
)
.require_unique_match(True)
.search(managed_objects)
)

merge_requested = MOFilesystem(fs_info).MergeScheduled()

if bool(merge_requested):
raise StratisCliFsMergeScheduledChangeError(True)

Filesystem.Properties.MergeScheduled.Set(get_object(fs_object_path), True)

@staticmethod
def cancel_revert(namespace):
"""
Cancel reverting a snapshot into its origin.
"""
# pylint: disable=import-outside-toplevel
from ._data import Filesystem, MOFilesystem, ObjectManager, filesystems, pools

proxy = get_object(TOP_OBJECT)
managed_objects = ObjectManager.Methods.GetManagedObjects(proxy, {})
(pool_object_path, _) = next(
pools(props={"Name": namespace.pool_name})
.require_unique_match(True)
.search(managed_objects)
)
(fs_object_path, fs_info) = next(
filesystems(
props={"Name": namespace.snapshot_name, "Pool": pool_object_path}
)
.require_unique_match(True)
.search(managed_objects)
)

mofs = MOFilesystem(fs_info)
(merge_requested, (origin_set, _)) = (mofs.MergeScheduled(), mofs.Origin())

if origin_set and not bool(merge_requested):
raise StratisCliFsMergeScheduledChangeError(False)

Filesystem.Properties.MergeScheduled.Set(get_object(fs_object_path), False)
12 changes: 12 additions & 0 deletions src/stratis_cli/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ def __str__(self):
return f"Filesystem's size limit is exactly {self.value}"


class StratisCliFsMergeScheduledChangeError(StratisCliNoPropertyChangeError):
"""
Raised when the user requests the same filesystems MergeScheduled value
that the filesystem already has.
"""

def __str__(self):
if self.value:
return "Filesystem is already scheduled for a revert operation."
return "Filesystem is not currently scheduled for a revert operation."


class StratisCliHasCacheChangeError(StratisCliNoPropertyChangeError):
"""
Raised when the user request cache initialization, but a cache is already
Expand Down
45 changes: 45 additions & 0 deletions src/stratis_cli/_parser/_logical.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,51 @@ def verify(self, namespace, parser):
"func": LogicalActions.unset_size_limit,
},
),
(
"schedule-revert",
{
"help": (
"Schedule a revert operation for this snapshot filesystem "
"into its origin filesystem"
),
"args": [
(
"pool_name",
{
"help": (
"Name of the pool the snapshot and its origin belong to"
),
},
),
(
"snapshot_name",
{"help": "Name of the snapshot filesystem"},
),
],
"func": LogicalActions.schedule_revert,
},
),
(
"cancel-revert",
{
"help": "Cancel a previously scheduled revert operation",
"args": [
(
"pool_name",
{
"help": (
"Name of the pool the snapshot and its origin belong to"
),
},
),
(
"snapshot_name",
{"help": "Name of the snapshot filesystem"},
),
],
"func": LogicalActions.cancel_revert,
},
),
(
"debug",
{
Expand Down
94 changes: 94 additions & 0 deletions tests/whitebox/integration/logical/test_cancel_revert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Test 'cancel-revert'.
"""

# isort: FIRSTPARTY
from dbus_python_client_gen import DPClientInvocationError

# isort: LOCAL
from stratis_cli import StratisCliErrorCodes
from stratis_cli._errors import StratisCliFsMergeScheduledChangeError

from .._misc import RUNNER, SimTestCase, device_name_list

_DEVICE_STRATEGY = device_name_list(1)
_ERROR = StratisCliErrorCodes.ERROR


class FsCancelRevertTestCase(SimTestCase):
"""
Test canceling a filesystem revert.
"""

_MENU = ["--propagate", "filesystem", "cancel-revert"]
_POOLNAME = "pool"
_FSNAME = "nofs"
_SNAPNAME = "snofs"

def setUp(self):
"""
Start the stratisd daemon with the simulator.
"""
super().setUp()
command_line = ["pool", "create", self._POOLNAME] + _DEVICE_STRATEGY()
RUNNER(command_line)

command_line = [
"filesystem",
"create",
self._POOLNAME,
self._FSNAME,
]
RUNNER(command_line)

command_line = [
"filesystem",
"snapshot",
self._POOLNAME,
self._FSNAME,
self._SNAPNAME,
]
RUNNER(command_line)

def test_cancel_revert_when_unscheduled(self):
"""
Cancelling an unscheduled revert should fail.
"""
command_line = self._MENU + [self._POOLNAME, self._SNAPNAME]
self.check_error(StratisCliFsMergeScheduledChangeError, command_line, _ERROR)

def test_cancel_revert_when_no_origin(self):
"""
Cancelling a revert on a filesystem with no origin should fail.
"""
command_line = self._MENU + [self._POOLNAME, self._FSNAME]
self.check_error(DPClientInvocationError, command_line, _ERROR)

def test_cancel_a_revert_twice(self):
"""
Cancelling a revert twice should fail.
"""
command_line = [
"--propagate",
"filesystem",
"schedule-revert",
self._POOLNAME,
self._SNAPNAME,
]
RUNNER(command_line)
command_line = self._MENU + [self._POOLNAME, self._SNAPNAME]
RUNNER(command_line)
self.check_error(StratisCliFsMergeScheduledChangeError, command_line, _ERROR)
15 changes: 15 additions & 0 deletions tests/whitebox/integration/logical/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ def setUp(self):
RUNNER(command_line)
command_line = ["filesystem", "create", self._POOLNAMES[0], self._VOLUMES[1]]
RUNNER(command_line)
command_line = [
"filesystem",
"snapshot",
self._POOLNAMES[0],
self._VOLUMES[1],
self._VOLUMES[2],
]
RUNNER(command_line)
command_line = ["pool", "create", self._POOLNAMES[1]] + device_lists[1]
RUNNER(command_line)
command_line = ["filesystem", "create", self._POOLNAMES[1], self._VOLUMES[2]]
Expand Down Expand Up @@ -191,3 +199,10 @@ def test_list_fs_name_pool_name_not_match(self):
self.check_error(
DbusClientUniqueResultError, command_line, StratisCliErrorCodes.ERROR
)

def test_list_fs_name_snapshot(self):
"""
Test list detailed view of a snapshot to test printing of revert information.
"""
command_line = self._MENU + [self._POOLNAMES[0], f"--name={self._VOLUMES[2]}"]
TEST_RUNNER(command_line)
Loading

0 comments on commit 354ceb8

Please sign in to comment.