]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * This is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License version 2.1, as published by the Free Software | |
9 | * Foundation. See file COPYING. | |
10 | * Copyright 2013 Inktank | |
11 | */ | |
12 | ||
13 | // install the librados-dev package to get this | |
14 | #include <rados/librados.hpp> | |
15 | #include <iostream> | |
16 | #include <string> | |
17 | ||
18 | int main(int argc, const char **argv) | |
19 | { | |
20 | int ret = 0; | |
21 | ||
22 | // we will use all of these below | |
23 | const char *pool_name = "hello_world_pool"; | |
24 | std::string hello("hello world!"); | |
25 | std::string object_name("hello_object"); | |
26 | librados::IoCtx io_ctx; | |
27 | ||
28 | // first, we create a Rados object and initialize it | |
29 | librados::Rados rados; | |
30 | { | |
31 | ret = rados.init("admin"); // just use the client.admin keyring | |
32 | if (ret < 0) { // let's handle any error that might have come back | |
33 | std::cerr << "couldn't initialize rados! error " << ret << std::endl; | |
34 | ret = EXIT_FAILURE; | |
35 | goto out; | |
7c673cae | 36 | } |
11fdf7f2 | 37 | std::cout << "we just set up a rados cluster object" << std::endl; |
7c673cae FG |
38 | } |
39 | ||
40 | /* | |
41 | * Now we need to get the rados object its config info. It can | |
42 | * parse argv for us to find the id, monitors, etc, so let's just | |
43 | * use that. | |
44 | */ | |
45 | { | |
46 | ret = rados.conf_parse_argv(argc, argv); | |
47 | if (ret < 0) { | |
48 | // This really can't happen, but we need to check to be a good citizen. | |
49 | std::cerr << "failed to parse config options! error " << ret << std::endl; | |
50 | ret = EXIT_FAILURE; | |
51 | goto out; | |
11fdf7f2 TL |
52 | } |
53 | ||
54 | std::cout << "we just parsed our config options" << std::endl; | |
55 | // We also want to apply the config file if the user specified | |
56 | // one, and conf_parse_argv won't do that for us. | |
57 | for (int i = 0; i < argc; ++i) { | |
58 | if ((strcmp(argv[i], "-c") == 0) || (strcmp(argv[i], "--conf") == 0)) { | |
59 | ret = rados.conf_read_file(argv[i+1]); | |
60 | if (ret < 0) { | |
61 | // This could fail if the config file is malformed, but it'd be hard. | |
62 | std::cerr << "failed to parse config file " << argv[i+1] | |
63 | << "! error" << ret << std::endl; | |
64 | ret = EXIT_FAILURE; | |
65 | goto out; | |
7c673cae | 66 | } |
11fdf7f2 | 67 | break; |
7c673cae FG |
68 | } |
69 | } | |
70 | } | |
71 | ||
72 | /* | |
73 | * next, we actually connect to the cluster | |
74 | */ | |
75 | { | |
76 | ret = rados.connect(); | |
77 | if (ret < 0) { | |
78 | std::cerr << "couldn't connect to cluster! error " << ret << std::endl; | |
79 | ret = EXIT_FAILURE; | |
80 | goto out; | |
7c673cae | 81 | } |
11fdf7f2 | 82 | std::cout << "we just connected to the rados cluster" << std::endl; |
7c673cae FG |
83 | } |
84 | ||
85 | /* | |
86 | * let's create our own pool instead of scribbling over real data. | |
87 | * Note that this command creates pools with default PG counts specified | |
88 | * by the monitors, which may not be appropriate for real use -- it's fine | |
89 | * for testing, though. | |
90 | */ | |
91 | { | |
92 | ret = rados.pool_create(pool_name); | |
93 | if (ret < 0) { | |
94 | std::cerr << "couldn't create pool! error " << ret << std::endl; | |
95 | return EXIT_FAILURE; | |
7c673cae | 96 | } |
11fdf7f2 | 97 | std::cout << "we just created a new pool named " << pool_name << std::endl; |
7c673cae FG |
98 | } |
99 | ||
100 | /* | |
101 | * create an "IoCtx" which is used to do IO to a pool | |
102 | */ | |
103 | { | |
104 | ret = rados.ioctx_create(pool_name, io_ctx); | |
105 | if (ret < 0) { | |
106 | std::cerr << "couldn't set up ioctx! error " << ret << std::endl; | |
107 | ret = EXIT_FAILURE; | |
108 | goto out; | |
7c673cae | 109 | } |
11fdf7f2 | 110 | std::cout << "we just created an ioctx for our pool" << std::endl; |
7c673cae FG |
111 | } |
112 | ||
113 | /* | |
114 | * now let's do some IO to the pool! We'll write "hello world!" to a | |
115 | * new object. | |
116 | */ | |
117 | { | |
118 | /* | |
119 | * "bufferlist"s are Ceph's native transfer type, and are carefully | |
120 | * designed to be efficient about copying. You can fill them | |
121 | * up from a lot of different data types, but strings or c strings | |
122 | * are often convenient. Just make sure not to deallocate the memory | |
123 | * until the bufferlist goes out of scope and any requests using it | |
124 | * have been finished! | |
125 | */ | |
126 | librados::bufferlist bl; | |
127 | bl.append(hello); | |
128 | ||
129 | /* | |
130 | * now that we have the data to write, let's send it to an object. | |
131 | * We'll use the synchronous interface for simplicity. | |
132 | */ | |
133 | ret = io_ctx.write_full(object_name, bl); | |
134 | if (ret < 0) { | |
135 | std::cerr << "couldn't write object! error " << ret << std::endl; | |
136 | ret = EXIT_FAILURE; | |
137 | goto out; | |
7c673cae | 138 | } |
11fdf7f2 TL |
139 | std::cout << "we just wrote new object " << object_name |
140 | << ", with contents\n" << hello << std::endl; | |
7c673cae FG |
141 | } |
142 | ||
143 | /* | |
144 | * now let's read that object back! Just for fun, we'll do it using | |
145 | * async IO instead of synchronous. (This would be more useful if we | |
146 | * wanted to send off multiple reads at once; see | |
147 | * http://docs.ceph.com/docs/master/rados/api/librados/#asychronous-io ) | |
148 | */ | |
149 | { | |
150 | librados::bufferlist read_buf; | |
151 | int read_len = 4194304; // this is way more than we need | |
152 | // allocate the completion from librados | |
153 | librados::AioCompletion *read_completion = librados::Rados::aio_create_completion(); | |
154 | // send off the request. | |
155 | ret = io_ctx.aio_read(object_name, read_completion, &read_buf, read_len, 0); | |
156 | if (ret < 0) { | |
157 | std::cerr << "couldn't start read object! error " << ret << std::endl; | |
158 | ret = EXIT_FAILURE; | |
159 | goto out; | |
160 | } | |
161 | // wait for the request to complete, and check that it succeeded. | |
162 | read_completion->wait_for_complete(); | |
163 | ret = read_completion->get_return_value(); | |
164 | if (ret < 0) { | |
165 | std::cerr << "couldn't read object! error " << ret << std::endl; | |
166 | ret = EXIT_FAILURE; | |
167 | goto out; | |
7c673cae | 168 | } |
11fdf7f2 TL |
169 | std::cout << "we read our object " << object_name |
170 | << ", and got back " << ret << " bytes with contents\n"; | |
171 | std::string read_string; | |
9f95a23c | 172 | read_buf.begin().copy(ret, read_string); |
11fdf7f2 | 173 | std::cout << read_string << std::endl; |
7c673cae FG |
174 | } |
175 | ||
176 | /* | |
177 | * We can also use xattrs that go alongside the object. | |
178 | */ | |
179 | { | |
180 | librados::bufferlist version_bl; | |
181 | version_bl.append('1'); | |
182 | ret = io_ctx.setxattr(object_name, "version", version_bl); | |
183 | if (ret < 0) { | |
184 | std::cerr << "failed to set xattr version entry! error " | |
185 | << ret << std::endl; | |
186 | ret = EXIT_FAILURE; | |
187 | goto out; | |
7c673cae | 188 | } |
11fdf7f2 | 189 | std::cout << "we set the xattr 'version' on our object!" << std::endl; |
7c673cae FG |
190 | } |
191 | ||
192 | /* | |
193 | * And if we want to be really cool, we can do multiple things in a single | |
194 | * atomic operation. For instance, we can update the contents of our object | |
195 | * and set the version at the same time. | |
196 | */ | |
197 | { | |
198 | librados::bufferlist bl; | |
199 | bl.append(hello); | |
200 | bl.append("v2"); | |
201 | librados::ObjectWriteOperation write_op; | |
202 | write_op.write_full(bl); | |
203 | librados::bufferlist version_bl; | |
204 | version_bl.append('2'); | |
205 | write_op.setxattr("version", version_bl); | |
206 | ret = io_ctx.operate(object_name, &write_op); | |
207 | if (ret < 0) { | |
208 | std::cerr << "failed to do compound write! error " << ret << std::endl; | |
209 | ret = EXIT_FAILURE; | |
210 | goto out; | |
7c673cae | 211 | } |
11fdf7f2 TL |
212 | std::cout << "we overwrote our object " << object_name |
213 | << " with contents\n" << bl.c_str() << std::endl; | |
7c673cae FG |
214 | } |
215 | ||
216 | /* | |
217 | * And to be even cooler, we can make sure that the object looks the | |
218 | * way we expect before doing the write! Notice how this attempt fails | |
219 | * because the xattr differs. | |
220 | */ | |
221 | { | |
222 | librados::ObjectWriteOperation failed_write_op; | |
223 | librados::bufferlist bl; | |
224 | bl.append(hello); | |
225 | bl.append("v2"); | |
226 | librados::ObjectWriteOperation write_op; | |
227 | write_op.write_full(bl); | |
228 | librados::bufferlist version_bl; | |
229 | version_bl.append('2'); | |
230 | librados::bufferlist old_version_bl; | |
231 | old_version_bl.append('1'); | |
232 | failed_write_op.cmpxattr("version", LIBRADOS_CMPXATTR_OP_EQ, old_version_bl); | |
233 | failed_write_op.write_full(bl); | |
234 | failed_write_op.setxattr("version", version_bl); | |
235 | ret = io_ctx.operate(object_name, &failed_write_op); | |
236 | if (ret < 0) { | |
237 | std::cout << "we just failed a write because the xattr wasn't as specified" | |
238 | << std::endl; | |
239 | } else { | |
240 | std::cerr << "we succeeded on writing despite an xattr comparison mismatch!" | |
241 | << std::endl; | |
242 | ret = EXIT_FAILURE; | |
243 | goto out; | |
244 | } | |
245 | ||
246 | /* | |
247 | * Now let's do the update with the correct xattr values so it | |
248 | * actually goes through | |
249 | */ | |
250 | bl.clear(); | |
251 | bl.append(hello); | |
252 | bl.append("v3"); | |
253 | old_version_bl.clear(); | |
254 | old_version_bl.append('2'); | |
255 | version_bl.clear(); | |
256 | version_bl.append('3'); | |
257 | librados::ObjectWriteOperation update_op; | |
258 | update_op.cmpxattr("version", LIBRADOS_CMPXATTR_OP_EQ, old_version_bl); | |
259 | update_op.write_full(bl); | |
260 | update_op.setxattr("version", version_bl); | |
261 | ret = io_ctx.operate(object_name, &update_op); | |
262 | if (ret < 0) { | |
263 | std::cerr << "failed to do a compound write update! error " << ret | |
264 | << std::endl; | |
265 | ret = EXIT_FAILURE; | |
266 | goto out; | |
7c673cae | 267 | } |
11fdf7f2 TL |
268 | std::cout << "we overwrote our object " << object_name |
269 | << " following an xattr test with contents\n" << bl.c_str() | |
270 | << std::endl; | |
7c673cae FG |
271 | } |
272 | ||
273 | ret = EXIT_SUCCESS; | |
274 | out: | |
275 | /* | |
276 | * And now we're done, so let's remove our pool and then | |
277 | * shut down the connection gracefully. | |
278 | */ | |
279 | int delete_ret = rados.pool_delete(pool_name); | |
280 | if (delete_ret < 0) { | |
281 | // be careful not to | |
282 | std::cerr << "We failed to delete our test pool!" << std::endl; | |
283 | ret = EXIT_FAILURE; | |
284 | } | |
285 | ||
286 | rados.shutdown(); | |
287 | ||
288 | return ret; | |
289 | } |