]>
Commit | Line | Data |
---|---|---|
9f95a23c 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 "librbd/mirror/snapshot/CreatePrimaryRequest.h" | |
5 | #include "common/dout.h" | |
6 | #include "common/errno.h" | |
7 | #include "cls/rbd/cls_rbd_client.h" | |
8 | #include "librbd/ImageCtx.h" | |
9 | #include "librbd/ImageState.h" | |
10 | #include "librbd/Operations.h" | |
11 | #include "librbd/Utils.h" | |
12 | #include "librbd/mirror/snapshot/UnlinkPeerRequest.h" | |
13 | #include "librbd/mirror/snapshot/Utils.h" | |
14 | ||
15 | #define dout_subsys ceph_subsys_rbd | |
16 | ||
17 | #undef dout_prefix | |
18 | #define dout_prefix *_dout << "librbd::mirror::snapshot::CreatePrimaryRequest: " \ | |
19 | << this << " " << __func__ << ": " | |
20 | ||
21 | namespace librbd { | |
22 | namespace mirror { | |
23 | namespace snapshot { | |
24 | ||
25 | using librbd::util::create_context_callback; | |
26 | using librbd::util::create_rados_callback; | |
27 | ||
28 | template <typename I> | |
29 | CreatePrimaryRequest<I>::CreatePrimaryRequest( | |
1911f103 | 30 | I *image_ctx, const std::string& global_image_id, |
f67539c2 TL |
31 | uint64_t clean_since_snap_id, uint64_t snap_create_flags, uint32_t flags, |
32 | uint64_t *snap_id, Context *on_finish) | |
9f95a23c | 33 | : m_image_ctx(image_ctx), m_global_image_id(global_image_id), |
f67539c2 TL |
34 | m_clean_since_snap_id(clean_since_snap_id), |
35 | m_snap_create_flags(snap_create_flags), m_flags(flags), m_snap_id(snap_id), | |
36 | m_on_finish(on_finish) { | |
9f95a23c TL |
37 | m_default_ns_ctx.dup(m_image_ctx->md_ctx); |
38 | m_default_ns_ctx.set_namespace(""); | |
39 | } | |
40 | ||
41 | template <typename I> | |
42 | void CreatePrimaryRequest<I>::send() { | |
43 | if (!util::can_create_primary_snapshot( | |
44 | m_image_ctx, | |
45 | ((m_flags & CREATE_PRIMARY_FLAG_DEMOTED) != 0), | |
46 | ((m_flags & CREATE_PRIMARY_FLAG_FORCE) != 0), nullptr, nullptr)) { | |
47 | finish(-EINVAL); | |
48 | return; | |
49 | } | |
50 | ||
51 | uuid_d uuid_gen; | |
52 | uuid_gen.generate_random(); | |
53 | m_snap_name = ".mirror.primary." + m_global_image_id + "." + | |
54 | uuid_gen.to_string(); | |
55 | ||
56 | get_mirror_peers(); | |
57 | } | |
58 | ||
59 | template <typename I> | |
60 | void CreatePrimaryRequest<I>::get_mirror_peers() { | |
61 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 62 | ldout(cct, 15) << dendl; |
9f95a23c TL |
63 | |
64 | librados::ObjectReadOperation op; | |
65 | cls_client::mirror_peer_list_start(&op); | |
66 | ||
67 | librados::AioCompletion *comp = create_rados_callback< | |
68 | CreatePrimaryRequest<I>, | |
69 | &CreatePrimaryRequest<I>::handle_get_mirror_peers>(this); | |
70 | m_out_bl.clear(); | |
71 | int r = m_default_ns_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl); | |
72 | ceph_assert(r == 0); | |
73 | comp->release(); | |
74 | } | |
75 | ||
76 | template <typename I> | |
77 | void CreatePrimaryRequest<I>::handle_get_mirror_peers(int r) { | |
78 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 79 | ldout(cct, 15) << "r=" << r << dendl; |
9f95a23c TL |
80 | |
81 | std::vector<cls::rbd::MirrorPeer> peers; | |
82 | if (r == 0) { | |
83 | auto iter = m_out_bl.cbegin(); | |
84 | r = cls_client::mirror_peer_list_finish(&iter, &peers); | |
85 | } | |
86 | ||
87 | if (r < 0) { | |
88 | lderr(cct) << "failed to retrieve mirror peers: " << cpp_strerror(r) | |
89 | << dendl; | |
90 | finish(r); | |
91 | return; | |
92 | } | |
93 | ||
94 | for (auto &peer : peers) { | |
95 | if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_RX) { | |
96 | continue; | |
97 | } | |
98 | m_mirror_peer_uuids.insert(peer.uuid); | |
99 | } | |
100 | ||
101 | if (m_mirror_peer_uuids.empty() && | |
102 | ((m_flags & CREATE_PRIMARY_FLAG_IGNORE_EMPTY_PEERS) == 0)) { | |
103 | lderr(cct) << "no mirror tx peers configured for the pool" << dendl; | |
104 | finish(-EINVAL); | |
105 | return; | |
106 | } | |
107 | ||
108 | create_snapshot(); | |
109 | } | |
110 | ||
111 | template <typename I> | |
112 | void CreatePrimaryRequest<I>::create_snapshot() { | |
113 | cls::rbd::MirrorSnapshotNamespace ns{ | |
114 | ((m_flags & CREATE_PRIMARY_FLAG_DEMOTED) != 0 ? | |
115 | cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY_DEMOTED : | |
116 | cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY), | |
1911f103 | 117 | m_mirror_peer_uuids, "", m_clean_since_snap_id}; |
9f95a23c TL |
118 | |
119 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 120 | ldout(cct, 15) << "name=" << m_snap_name << ", " |
9f95a23c TL |
121 | << "ns=" << ns << dendl; |
122 | auto ctx = create_context_callback< | |
123 | CreatePrimaryRequest<I>, | |
124 | &CreatePrimaryRequest<I>::handle_create_snapshot>(this); | |
f67539c2 TL |
125 | m_image_ctx->operations->snap_create(ns, m_snap_name, m_snap_create_flags, |
126 | m_prog_ctx, ctx); | |
9f95a23c TL |
127 | } |
128 | ||
129 | template <typename I> | |
130 | void CreatePrimaryRequest<I>::handle_create_snapshot(int r) { | |
131 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 132 | ldout(cct, 15) << "r=" << r << dendl; |
9f95a23c TL |
133 | |
134 | if (r < 0) { | |
135 | lderr(cct) << "failed to create mirror snapshot: " << cpp_strerror(r) | |
136 | << dendl; | |
137 | finish(r); | |
138 | return; | |
139 | } | |
140 | ||
141 | refresh_image(); | |
142 | } | |
143 | ||
144 | template <typename I> | |
145 | void CreatePrimaryRequest<I>::refresh_image() { | |
146 | // if snapshot created via remote RPC, refresh is required to retrieve | |
147 | // the snapshot id | |
148 | if (m_snap_id == nullptr) { | |
149 | unlink_peer(); | |
150 | return; | |
151 | } | |
152 | ||
153 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 154 | ldout(cct, 15) << dendl; |
9f95a23c TL |
155 | |
156 | auto ctx = create_context_callback< | |
157 | CreatePrimaryRequest<I>, | |
158 | &CreatePrimaryRequest<I>::handle_refresh_image>(this); | |
159 | m_image_ctx->state->refresh(ctx); | |
160 | } | |
161 | ||
162 | template <typename I> | |
163 | void CreatePrimaryRequest<I>::handle_refresh_image(int r) { | |
164 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 165 | ldout(cct, 15) << "r=" << r << dendl; |
9f95a23c TL |
166 | |
167 | if (r < 0) { | |
168 | lderr(cct) << "failed to refresh image: " << cpp_strerror(r) << dendl; | |
169 | finish(r); | |
170 | return; | |
171 | } | |
172 | ||
173 | { | |
174 | std::shared_lock image_locker{m_image_ctx->image_lock}; | |
175 | *m_snap_id = m_image_ctx->get_snap_id( | |
176 | cls::rbd::MirrorSnapshotNamespace{}, m_snap_name); | |
f67539c2 | 177 | ldout(cct, 15) << "snap_id=" << *m_snap_id << dendl; |
9f95a23c TL |
178 | } |
179 | ||
180 | unlink_peer(); | |
181 | } | |
182 | ||
183 | template <typename I> | |
184 | void CreatePrimaryRequest<I>::unlink_peer() { | |
185 | uint64_t max_snapshots = m_image_ctx->config.template get_val<uint64_t>( | |
186 | "rbd_mirroring_max_mirroring_snapshots"); | |
187 | ceph_assert(max_snapshots >= 3); | |
188 | ||
189 | std::string peer_uuid; | |
190 | uint64_t snap_id = CEPH_NOSNAP; | |
191 | ||
192 | for (auto &peer : m_mirror_peer_uuids) { | |
193 | std::shared_lock image_locker{m_image_ctx->image_lock}; | |
194 | size_t count = 0; | |
f67539c2 | 195 | uint64_t unlink_snap_id = 0; |
9f95a23c TL |
196 | for (auto &snap_it : m_image_ctx->snap_info) { |
197 | auto info = boost::get<cls::rbd::MirrorSnapshotNamespace>( | |
198 | &snap_it.second.snap_namespace); | |
199 | if (info == nullptr) { | |
200 | continue; | |
201 | } | |
202 | if (info->state != cls::rbd::MIRROR_SNAPSHOT_STATE_PRIMARY) { | |
203 | // reset counters -- we count primary snapshots after the last promotion | |
204 | count = 0; | |
f67539c2 | 205 | unlink_snap_id = 0; |
9f95a23c TL |
206 | continue; |
207 | } | |
b3b6e05e TL |
208 | // call UnlinkPeerRequest only if the snapshot is linked with this peer |
209 | // or if it's not linked with any peer (happens if mirroring is enabled | |
210 | // on a pool with no peers configured or if UnlinkPeerRequest gets | |
211 | // interrupted) | |
212 | if (info->mirror_peer_uuids.size() == 0) { | |
213 | peer_uuid = peer; | |
214 | snap_id = snap_it.first; | |
215 | break; | |
216 | } | |
217 | if (info->mirror_peer_uuids.count(peer) == 0) { | |
218 | continue; | |
219 | } | |
9f95a23c | 220 | count++; |
f67539c2 TL |
221 | if (count == 3) { |
222 | unlink_snap_id = snap_it.first; | |
223 | } | |
224 | if (count > max_snapshots) { | |
9f95a23c | 225 | peer_uuid = peer; |
f67539c2 | 226 | snap_id = unlink_snap_id; |
9f95a23c TL |
227 | break; |
228 | } | |
229 | } | |
230 | if (snap_id != CEPH_NOSNAP) { | |
231 | break; | |
232 | } | |
233 | } | |
234 | ||
235 | if (snap_id == CEPH_NOSNAP) { | |
236 | finish(0); | |
237 | return; | |
238 | } | |
239 | ||
240 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 241 | ldout(cct, 15) << "peer=" << peer_uuid << ", snap_id=" << snap_id << dendl; |
9f95a23c TL |
242 | |
243 | auto ctx = create_context_callback< | |
244 | CreatePrimaryRequest<I>, | |
245 | &CreatePrimaryRequest<I>::handle_unlink_peer>(this); | |
246 | auto req = UnlinkPeerRequest<I>::create(m_image_ctx, snap_id, peer_uuid, ctx); | |
247 | req->send(); | |
248 | } | |
249 | ||
250 | template <typename I> | |
251 | void CreatePrimaryRequest<I>::handle_unlink_peer(int r) { | |
252 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 253 | ldout(cct, 15) << "r=" << r << dendl; |
9f95a23c TL |
254 | |
255 | if (r < 0) { | |
256 | lderr(cct) << "failed to unlink peer: " << cpp_strerror(r) << dendl; | |
257 | finish(0); // not fatal | |
258 | return; | |
259 | } | |
260 | ||
261 | unlink_peer(); | |
262 | } | |
263 | ||
264 | template <typename I> | |
265 | void CreatePrimaryRequest<I>::finish(int r) { | |
266 | CephContext *cct = m_image_ctx->cct; | |
f67539c2 | 267 | ldout(cct, 15) << "r=" << r << dendl; |
9f95a23c TL |
268 | |
269 | m_on_finish->complete(r); | |
270 | delete this; | |
271 | } | |
272 | ||
273 | } // namespace snapshot | |
274 | } // namespace mirror | |
275 | } // namespace librbd | |
276 | ||
277 | template class librbd::mirror::snapshot::CreatePrimaryRequest<librbd::ImageCtx>; |