]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/migration/S3Stream.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / librbd / migration / S3Stream.cc
CommitLineData
f67539c2
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/migration/S3Stream.h"
5#include "common/armor.h"
6#include "common/ceph_crypto.h"
7#include "common/ceph_time.h"
8#include "common/dout.h"
9#include "common/errno.h"
10#include "librbd/AsioEngine.h"
11#include "librbd/ImageCtx.h"
12#include "librbd/Utils.h"
13#include "librbd/asio/Utils.h"
14#include "librbd/io/AioCompletion.h"
15#include "librbd/io/ReadResult.h"
16#include "librbd/migration/HttpClient.h"
17#include "librbd/migration/HttpProcessorInterface.h"
18#include <boost/beast/http.hpp>
19
20#undef FMT_HEADER_ONLY
21#define FMT_HEADER_ONLY 1
22#include <fmt/chrono.h>
23#include <fmt/format.h>
24
25#include <time.h>
26
27namespace librbd {
28namespace migration {
29
30using HttpRequest = boost::beast::http::request<boost::beast::http::empty_body>;
31
32namespace {
33
34const std::string URL_KEY {"url"};
35const std::string ACCESS_KEY {"access_key"};
36const std::string SECRET_KEY {"secret_key"};
37
38} // anonymous namespace
39
40template <typename I>
41struct S3Stream<I>::HttpProcessor : public HttpProcessorInterface {
42 S3Stream* s3stream;
43
44 HttpProcessor(S3Stream* s3stream) : s3stream(s3stream) {
45 }
46
47 void process_request(EmptyRequest& request) override {
48 s3stream->process_request(request);
49 }
50};
51
52#define dout_subsys ceph_subsys_rbd
53#undef dout_prefix
54#define dout_prefix *_dout << "librbd::migration::S3Stream: " << this \
55 << " " << __func__ << ": "
56
57template <typename I>
58S3Stream<I>::S3Stream(I* image_ctx, const json_spirit::mObject& json_object)
59 : m_image_ctx(image_ctx), m_cct(image_ctx->cct),
60 m_asio_engine(image_ctx->asio_engine), m_json_object(json_object),
61 m_http_processor(std::make_unique<HttpProcessor>(this)) {
62}
63
64template <typename I>
65S3Stream<I>::~S3Stream() {
66}
67
68template <typename I>
69void S3Stream<I>::open(Context* on_finish) {
70 auto& url_value = m_json_object[URL_KEY];
71 if (url_value.type() != json_spirit::str_type) {
72 lderr(m_cct) << "failed to locate '" << URL_KEY << "' key" << dendl;
73 on_finish->complete(-EINVAL);
74 return;
75 }
76
77 auto& access_key = m_json_object[ACCESS_KEY];
78 if (access_key.type() != json_spirit::str_type) {
79 lderr(m_cct) << "failed to locate '" << ACCESS_KEY << "' key" << dendl;
80 on_finish->complete(-EINVAL);
81 return;
82 }
83
84 auto& secret_key = m_json_object[SECRET_KEY];
85 if (secret_key.type() != json_spirit::str_type) {
86 lderr(m_cct) << "failed to locate '" << SECRET_KEY << "' key" << dendl;
87 on_finish->complete(-EINVAL);
88 return;
89 }
90
91 m_url = url_value.get_str();
92
93 librados::Rados rados(m_image_ctx->md_ctx);
94 int r = 0;
95 m_access_key = access_key.get_str();
96 if (util::is_config_key_uri(m_access_key)) {
97 r = util::get_config_key(rados, m_access_key, &m_access_key);
98 if (r < 0) {
99 lderr(m_cct) << "failed to retrieve access key from config: "
100 << cpp_strerror(r) << dendl;
101 on_finish->complete(r);
102 return;
103 }
104 }
105
106 m_secret_key = secret_key.get_str();
107 if (util::is_config_key_uri(m_secret_key)) {
108 r = util::get_config_key(rados, m_secret_key, &m_secret_key);
109 if (r < 0) {
110 lderr(m_cct) << "failed to retrieve secret key from config: "
111 << cpp_strerror(r) << dendl;
112 on_finish->complete(r);
113 return;
114 }
115 }
116
117 ldout(m_cct, 10) << "url=" << m_url << ", "
118 << "access_key=" << m_access_key << dendl;
119
120 m_http_client.reset(HttpClient<I>::create(m_image_ctx, m_url));
121 m_http_client->set_http_processor(m_http_processor.get());
122 m_http_client->open(on_finish);
123}
124
125template <typename I>
126void S3Stream<I>::close(Context* on_finish) {
127 ldout(m_cct, 10) << dendl;
128
129 if (!m_http_client) {
130 on_finish->complete(0);
131 return;
132 }
133
134 m_http_client->close(on_finish);
135}
136
137template <typename I>
138void S3Stream<I>::get_size(uint64_t* size, Context* on_finish) {
139 ldout(m_cct, 10) << dendl;
140
141 m_http_client->get_size(size, on_finish);
142}
143
144template <typename I>
145void S3Stream<I>::read(io::Extents&& byte_extents, bufferlist* data,
146 Context* on_finish) {
147 ldout(m_cct, 20) << "byte_extents=" << byte_extents << dendl;
148
149 m_http_client->read(std::move(byte_extents), data, on_finish);
150}
151
152template <typename I>
153void S3Stream<I>::process_request(HttpRequest& http_request) {
154 ldout(m_cct, 20) << dendl;
155
156 // format RFC 1123 date/time
157 auto time = ceph::real_clock::to_time_t(ceph::real_clock::now());
158 struct tm timeInfo;
159 gmtime_r(&time, &timeInfo);
160
161 std::string date = fmt::format("{:%a, %d %b %Y %H:%M:%S %z}", timeInfo);
162 http_request.set(boost::beast::http::field::date, date);
163
164 // note: we don't support S3 subresources
165 std::string canonicalized_resource = std::string(http_request.target());
166
167 std::string string_to_sign = fmt::format(
168 "{}\n\n\n{}\n{}",
169 std::string(boost::beast::http::to_string(http_request.method())),
170 date, canonicalized_resource);
171
172 // create HMAC-SHA1 signature from secret key + string-to-sign
173 sha1_digest_t digest;
174 ceph::crypto::HMACSHA1 hmac(
175 reinterpret_cast<const unsigned char*>(m_secret_key.data()),
176 m_secret_key.size());
177 hmac.Update(reinterpret_cast<const unsigned char*>(string_to_sign.data()),
178 string_to_sign.size());
179 hmac.Final(reinterpret_cast<unsigned char*>(digest.v));
180
181 // base64 encode the result
182 char buf[64];
183 int r = ceph_armor(std::begin(buf), std::begin(buf) + sizeof(buf),
184 reinterpret_cast<const char *>(digest.v),
185 reinterpret_cast<const char *>(digest.v + digest.SIZE));
186 if (r < 0) {
187 ceph_abort("ceph_armor failed");
188 }
189
190 // store the access-key + signature in the HTTP authorization header
191 std::string signature = std::string(std::begin(buf), std::begin(buf) + r);
192 std::string authorization = fmt::format("AWS {}:{}", m_access_key, signature);
193 http_request.set(boost::beast::http::field::authorization, authorization);
194
195 ldout(m_cct, 20) << "string_to_sign=" << string_to_sign << ", "
196 << "authorization=" << authorization << dendl;
197}
198
199} // namespace migration
200} // namespace librbd
201
202template class librbd::migration::S3Stream<librbd::ImageCtx>;