]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "common/errno.h" | |
5 | #include "cls/rbd/cls_rbd_client.h" | |
9f95a23c | 6 | #include "librbd/api/Mirror.h" |
11fdf7f2 TL |
7 | #include "librbd/api/Namespace.h" |
8 | #include "librbd/ImageCtx.h" | |
9 | ||
10 | #define dout_subsys ceph_subsys_rbd | |
11 | #undef dout_prefix | |
12 | #define dout_prefix *_dout << "librbd::api::Namespace: " << __func__ << ": " | |
13 | ||
14 | namespace librbd { | |
15 | namespace api { | |
16 | ||
92f5a8d4 TL |
17 | namespace { |
18 | ||
19 | const std::list<std::string> POOL_OBJECTS { | |
20 | RBD_CHILDREN, | |
21 | RBD_GROUP_DIRECTORY, | |
22 | RBD_INFO, | |
23 | RBD_MIRRORING, | |
24 | RBD_TASK, | |
25 | RBD_TRASH, | |
26 | RBD_DIRECTORY | |
27 | }; | |
28 | ||
29 | } // anonymous namespace | |
30 | ||
11fdf7f2 TL |
31 | template <typename I> |
32 | int Namespace<I>::create(librados::IoCtx& io_ctx, const std::string& name) | |
33 | { | |
34 | CephContext *cct = (CephContext *)io_ctx.cct(); | |
35 | ldout(cct, 5) << "name=" << name << dendl; | |
36 | ||
37 | if (name.empty()) { | |
38 | return -EINVAL; | |
39 | } | |
40 | ||
41 | librados::Rados rados(io_ctx); | |
42 | int8_t require_osd_release; | |
43 | int r = rados.get_min_compatible_osd(&require_osd_release); | |
44 | if (r < 0) { | |
45 | lderr(cct) << "failed to retrieve min OSD release: " << cpp_strerror(r) | |
46 | << dendl; | |
47 | return r; | |
48 | } | |
49 | ||
50 | if (require_osd_release < CEPH_RELEASE_NAUTILUS) { | |
51 | ldout(cct, 1) << "namespace support requires nautilus or later OSD" | |
52 | << dendl; | |
53 | return -ENOSYS; | |
54 | } | |
55 | ||
56 | ||
57 | librados::IoCtx default_ns_ctx; | |
58 | default_ns_ctx.dup(io_ctx); | |
59 | default_ns_ctx.set_namespace(""); | |
60 | ||
61 | r = cls_client::namespace_add(&default_ns_ctx, name); | |
62 | if (r < 0) { | |
63 | lderr(cct) << "failed to add namespace: " << cpp_strerror(r) << dendl; | |
64 | return r; | |
65 | } | |
66 | ||
67 | librados::IoCtx ns_ctx; | |
68 | ns_ctx.dup(io_ctx); | |
69 | ns_ctx.set_namespace(name); | |
70 | ||
71 | r = cls_client::dir_state_set(&ns_ctx, RBD_DIRECTORY, | |
72 | cls::rbd::DIRECTORY_STATE_READY); | |
73 | if (r < 0) { | |
74 | lderr(cct) << "failed to initialize image directory: " << cpp_strerror(r) | |
75 | << dendl; | |
76 | goto rollback; | |
77 | } | |
78 | ||
79 | return 0; | |
80 | ||
81 | rollback: | |
82 | int ret_val = cls_client::namespace_remove(&default_ns_ctx, name); | |
83 | if (ret_val < 0) { | |
84 | lderr(cct) << "failed to remove namespace: " << cpp_strerror(ret_val) << dendl; | |
85 | } | |
86 | ||
87 | return r; | |
88 | } | |
89 | ||
90 | template <typename I> | |
91 | int Namespace<I>::remove(librados::IoCtx& io_ctx, const std::string& name) | |
92 | { | |
93 | CephContext *cct = (CephContext *)io_ctx.cct(); | |
94 | ldout(cct, 5) << "name=" << name << dendl; | |
95 | ||
96 | if (name.empty()) { | |
97 | return -EINVAL; | |
98 | } | |
99 | ||
100 | librados::IoCtx default_ns_ctx; | |
101 | default_ns_ctx.dup(io_ctx); | |
102 | default_ns_ctx.set_namespace(""); | |
103 | ||
104 | librados::IoCtx ns_ctx; | |
105 | ns_ctx.dup(io_ctx); | |
106 | ns_ctx.set_namespace(name); | |
107 | ||
108 | std::map<std::string, cls::rbd::TrashImageSpec> trash_entries; | |
109 | ||
110 | librados::ObjectWriteOperation dir_op; | |
111 | librbd::cls_client::dir_state_set( | |
112 | &dir_op, cls::rbd::DIRECTORY_STATE_ADD_DISABLED); | |
113 | dir_op.remove(); | |
114 | ||
115 | int r = ns_ctx.operate(RBD_DIRECTORY, &dir_op); | |
116 | if (r == -EBUSY) { | |
117 | ldout(cct, 5) << "image directory not empty" << dendl; | |
118 | goto rollback; | |
119 | } else if (r < 0 && r != -ENOENT) { | |
120 | lderr(cct) << "failed to disable the namespace: " << cpp_strerror(r) | |
121 | << dendl; | |
122 | return r; | |
123 | } | |
124 | ||
125 | r = cls_client::trash_list(&ns_ctx, "", 1, &trash_entries); | |
126 | if (r < 0 && r != -ENOENT) { | |
127 | lderr(cct) << "failed to list trash directory: " << cpp_strerror(r) | |
128 | << dendl; | |
129 | return r; | |
130 | } else if (!trash_entries.empty()) { | |
131 | ldout(cct, 5) << "image trash not empty" << dendl; | |
132 | goto rollback; | |
133 | } | |
134 | ||
9f95a23c TL |
135 | r = Mirror<I>::mode_set(ns_ctx, RBD_MIRROR_MODE_DISABLED); |
136 | if (r < 0) { | |
137 | lderr(cct) << "failed to disable mirroring: " << cpp_strerror(r) | |
138 | << dendl; | |
139 | return r; | |
140 | } | |
141 | ||
92f5a8d4 TL |
142 | for (auto& oid : POOL_OBJECTS) { |
143 | r = ns_ctx.remove(oid); | |
144 | if (r < 0 && r != -ENOENT) { | |
145 | lderr(cct) << "failed to remove object '" << oid << "': " | |
146 | << cpp_strerror(r) << dendl; | |
147 | return r; | |
148 | } | |
149 | } | |
150 | ||
11fdf7f2 TL |
151 | r = cls_client::namespace_remove(&default_ns_ctx, name); |
152 | if (r < 0) { | |
153 | lderr(cct) << "failed to remove namespace: " << cpp_strerror(r) << dendl; | |
154 | return r; | |
155 | } | |
156 | ||
157 | return 0; | |
158 | ||
159 | rollback: | |
160 | ||
161 | r = librbd::cls_client::dir_state_set( | |
162 | &ns_ctx, RBD_DIRECTORY, cls::rbd::DIRECTORY_STATE_READY); | |
163 | if (r < 0) { | |
164 | lderr(cct) << "failed to restore directory state: " << cpp_strerror(r) | |
165 | << dendl; | |
166 | } | |
167 | ||
168 | return -EBUSY; | |
169 | } | |
170 | ||
171 | template <typename I> | |
172 | int Namespace<I>::list(IoCtx& io_ctx, vector<string> *names) | |
173 | { | |
174 | CephContext *cct = (CephContext *)io_ctx.cct(); | |
175 | ldout(cct, 5) << dendl; | |
176 | ||
177 | librados::IoCtx default_ns_ctx; | |
178 | default_ns_ctx.dup(io_ctx); | |
179 | default_ns_ctx.set_namespace(""); | |
180 | ||
181 | int r; | |
182 | int max_read = 1024; | |
183 | std::string last_read = ""; | |
184 | do { | |
185 | std::list<std::string> name_list; | |
186 | r = cls_client::namespace_list(&default_ns_ctx, last_read, max_read, | |
187 | &name_list); | |
188 | if (r == -ENOENT) { | |
189 | return 0; | |
190 | } else if (r < 0) { | |
191 | lderr(cct) << "error listing namespaces: " << cpp_strerror(r) << dendl; | |
192 | return r; | |
193 | } | |
194 | ||
195 | names->insert(names->end(), name_list.begin(), name_list.end()); | |
196 | if (!name_list.empty()) { | |
197 | last_read = name_list.back(); | |
198 | } | |
199 | r = name_list.size(); | |
200 | } while (r == max_read); | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | template <typename I> | |
206 | int Namespace<I>::exists(librados::IoCtx& io_ctx, const std::string& name, bool *exists) | |
207 | { | |
208 | CephContext *cct = (CephContext *)io_ctx.cct(); | |
209 | ldout(cct, 5) << "name=" << name << dendl; | |
210 | ||
211 | *exists = false; | |
212 | if (name.empty()) { | |
213 | return -EINVAL; | |
214 | } | |
215 | ||
216 | librados::IoCtx ns_ctx; | |
217 | ns_ctx.dup(io_ctx); | |
218 | ns_ctx.set_namespace(name); | |
219 | ||
220 | int r = librbd::cls_client::dir_state_assert(&ns_ctx, RBD_DIRECTORY, | |
221 | cls::rbd::DIRECTORY_STATE_READY); | |
222 | if (r == 0) { | |
223 | *exists = true; | |
224 | } else if (r != -ENOENT) { | |
225 | lderr(cct) << "error asserting namespace: " << cpp_strerror(r) << dendl; | |
226 | return r; | |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | } // namespace api | |
233 | } // namespace librbd | |
234 | ||
235 | template class librbd::api::Namespace<librbd::ImageCtx>; |