Skip to content

Commit 53aca7d

Browse files
authored
Merge pull request ceph#62921 from idryomov/wip-71026
librbd: disallow "rbd trash mv" if image is in a group Reviewed-by: Ramana Raja <[email protected]>
2 parents f7c998c + 2ae4a57 commit 53aca7d

File tree

5 files changed

+45
-9
lines changed

5 files changed

+45
-9
lines changed

PendingReleaseNotes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@
133133
* RGW: Added support for the `RestrictPublicBuckets` property of the S3 `PublicAccessBlock`
134134
configuration.
135135

136+
* RBD: Moving an image that is a member of a group to trash is no longer
137+
allowed. `rbd trash mv` command now behaves the same way as `rbd rm` in this
138+
scenario.
139+
136140
>=19.2.1
137141

138142
* CephFS: Command `fs subvolume create` now allows tagging subvolumes through option

src/librbd/api/Trash.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
217217
ictx->state->close();
218218
return -EBUSY;
219219
}
220+
if (ictx->group_spec.is_valid() &&
221+
source != RBD_TRASH_IMAGE_SOURCE_MIGRATION) {
222+
lderr(cct) << "image is in a group - not moving to trash" << dendl;
223+
ictx->image_lock.unlock_shared();
224+
ictx->state->close();
225+
return -EMLINK;
226+
}
220227
ictx->image_lock.unlock_shared();
221228

222229
if (mirror_r >= 0 &&

src/pybind/rbd/rbd.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ class ImageHasSnapshots(OSError):
271271
"RBD image has snapshots (%s)" % message, errno)
272272

273273

274+
class ImageMemberOfGroup(OSError):
275+
def __init__(self, message, errno=None):
276+
super(ImageMemberOfGroup, self).__init__(
277+
"RBD image is member of group (%s)" % message, errno)
278+
279+
274280
class FunctionNotSupported(OSError):
275281
def __init__(self, message, errno=None):
276282
super(FunctionNotSupported, self).__init__(
@@ -320,6 +326,7 @@ cdef errno_to_exception = {
320326
errno.EROFS : ReadOnlyImage,
321327
errno.EBUSY : ImageBusy,
322328
errno.ENOTEMPTY : ImageHasSnapshots,
329+
errno.EMLINK : ImageMemberOfGroup,
323330
errno.ENOSYS : FunctionNotSupported,
324331
errno.EDOM : ArgumentOutOfRange,
325332
errno.ESHUTDOWN : ConnectionShutdown,
@@ -339,6 +346,7 @@ cdef group_errno_to_exception = {
339346
errno.EROFS : ReadOnlyImage,
340347
errno.EBUSY : ImageBusy,
341348
errno.ENOTEMPTY : ImageHasSnapshots,
349+
errno.EMLINK : ImageMemberOfGroup,
342350
errno.ENOSYS : FunctionNotSupported,
343351
errno.EDOM : ArgumentOutOfRange,
344352
errno.ESHUTDOWN : ConnectionShutdown,

src/test/pybind/test_rbd.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
LIBRADOS_OP_FLAG_FADVISE_RANDOM)
2222
from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
2323
ImageBusy, ImageHasSnapshots, ReadOnlyImage, ObjectNotFound,
24-
FunctionNotSupported, ArgumentOutOfRange,
24+
FunctionNotSupported, ArgumentOutOfRange, ImageMemberOfGroup,
2525
ECANCELED, OperationCanceled,
2626
DiskQuotaExceeded, ConnectionShutdown, PermissionError,
2727
RBD_FEATURE_LAYERING, RBD_FEATURE_STRIPINGV2,
@@ -3069,13 +3069,19 @@ def test_group_image_list(self):
30693069

30703070
def test_group_image_list_move_to_trash(self):
30713071
eq([], list(self.group.list_images()))
3072-
with Image(ioctx, image_name) as image:
3073-
image_id = image.id()
30743072
self.group.add_image(ioctx, image_name)
30753073
eq([image_name], [img['name'] for img in self.group.list_images()])
3076-
RBD().trash_move(ioctx, image_name, 0)
3074+
assert_raises(ImageMemberOfGroup, RBD().trash_move, ioctx, image_name, 0)
3075+
eq([image_name], [img['name'] for img in self.group.list_images()])
3076+
3077+
def test_group_image_list_remove(self):
3078+
# need a closed image to get ImageMemberOfGroup instead of ImageBusy
3079+
self.image_names.append(create_image())
30773080
eq([], list(self.group.list_images()))
3078-
RBD().trash_restore(ioctx, image_id, image_name)
3081+
self.group.add_image(ioctx, image_name)
3082+
eq([image_name], [img['name'] for img in self.group.list_images()])
3083+
assert_raises(ImageMemberOfGroup, RBD().remove, ioctx, image_name)
3084+
eq([image_name], [img['name'] for img in self.group.list_images()])
30793085

30803086
def test_group_get_id(self):
30813087
id = self.group.id()

src/tools/rbd/action/Trash.cc

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,22 @@ int execute_move(const po::variables_map &vm,
9797
librbd::RBD rbd;
9898
r = rbd.trash_move(io_ctx, image_name.c_str(), dt);
9999
if (r < 0) {
100-
std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
101-
<< std::endl;
100+
if (r == -EMLINK) {
101+
std::cerr << "rbd: error: image belongs to a group"
102+
<< std::endl
103+
<< "Remove the image from the group and try again."
104+
<< std::endl;
105+
} else {
106+
std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
107+
<< std::endl;
108+
}
109+
return r;
102110
}
103111

104112
if (expires_at != "now") {
105113
std::cout << "rbd: image " << image_name << " will expire at " << exp_time << std::endl;
106114
}
107-
return r;
115+
return 0;
108116
}
109117

110118
void get_remove_arguments(po::options_description *positional,
@@ -161,7 +169,10 @@ int execute_remove(const po::variables_map &vm,
161169
<< "waiting 30s for the crashed client to timeout."
162170
<< std::endl;
163171
} else if (r == -EMLINK) {
164-
std::cerr << std::endl
172+
// moving to trash an image that belongs to a group is no longer
173+
// allowed, this is to handle any image that was trashed earlier
174+
std::cerr << "rbd: error: image belongs to a group"
175+
<< std::endl
165176
<< "Remove the image from the group and try again."
166177
<< std::endl;
167178
} else if (r == -EPERM) {

0 commit comments

Comments
 (0)