remove_image "${dest_image}"
}
-test_import_nbd_stream() {
+test_import_nbd_stream_qcow2() {
local base_image=$1
local dest_image=$2
"type": "raw",
"stream": {
"type": "nbd",
- "server": "localhost",
- "port": "10809"
+ "uri": "nbd://localhost"
}
}
EOF
compare_images ${base_image} ${dest_image}
remove_image "${dest_image}"
+ # shortest possible URI
+ rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd://"}}' \
+ ${dest_image}
+ rbd migration abort ${dest_image}
+
+ # non-existing export name
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd:///myexport"}}' \
+ ${dest_image}
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd://localhost/myexport"}}' \
+ ${dest_image}
+
+ kill_nbd_server
+ qemu-nbd --export-name myexport -f qcow2 --read-only --shared 10 --persistent --fork \
+ ${TEMPDIR}/${base_image}.qcow2
+
+ rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd:///myexport"}}' \
+ ${dest_image}
+ rbd migration abort ${dest_image}
+
+ rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd://localhost/myexport"}}' \
+ ${dest_image}
+ rbd migration abort ${dest_image}
+
+ kill_nbd_server
+
+ # server not running
+ expect_false rbd migration prepare --import-only \
+ --source-spec-path ${TEMPDIR}/spec.json ${dest_image}
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd://"}}' \
+ ${dest_image}
+
+ # no URI
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd"}}' \
+ ${dest_image}
+
+ # invalid URI
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": 123456}}' \
+ ${dest_image}
+
+ # libnbd - nbd_get_errno() returns an error
+ # nbd_connect_uri: unknown URI scheme: NULL: Invalid argument (errno = 22)
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": ""}}' \
+ ${dest_image}
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "foo.example.com"}}' \
+ ${dest_image}
+
+ # libnbd - nbd_get_errno() returns 0, EIO fallback
+ # nbd_connect_uri: getaddrinfo: foo.example.com:10809: Name or service not known (errno = 0)
+ expect_false rbd migration prepare --import-only \
+ --source-spec '{"type": "raw", "stream": {"type": "nbd", "uri": "nbd://foo.example.com"}}' \
+ ${dest_image}
+}
+
+test_import_nbd_stream_raw() {
+ local base_image=$1
+ local dest_image=$2
+
+ qemu-nbd -f raw --read-only --shared 10 --persistent --fork \
+ --socket ${TEMPDIR}/qemu-nbd-${base_image} ${TEMPDIR}/${base_image}
+ qemu-nbd -f raw --read-only --shared 10 --persistent --fork \
+ --socket ${TEMPDIR}/qemu-nbd-${base_image}@1 ${TEMPDIR}/${base_image}@1
+ qemu-nbd -f raw --read-only --shared 10 --persistent --fork \
+ --socket ${TEMPDIR}/qemu-nbd-${base_image}@2 ${TEMPDIR}/${base_image}@2
+
+ cat > ${TEMPDIR}/spec.json <<EOF
+{
+ "type": "raw",
+ "stream": {
+ "type": "nbd",
+ "uri": "nbd+unix:///?socket=${TEMPDIR}/qemu-nbd-${base_image}"
+ },
+ "snapshots": [{
+ "type": "raw",
+ "name": "snap1",
+ "stream": {
+ "type": "nbd",
+ "uri": "nbd+unix:///?socket=${TEMPDIR}/qemu-nbd-${base_image}@1"
+ }
+ }, {
+ "type": "raw",
+ "name": "snap2",
+ "stream": {
+ "type": "nbd",
+ "uri": "nbd+unix:///?socket=${TEMPDIR}/qemu-nbd-${base_image}@2"
+ }
+ }]
+}
+EOF
+ cat ${TEMPDIR}/spec.json
+
+ rbd migration prepare --import-only \
+ --source-spec-path ${TEMPDIR}/spec.json ${dest_image}
+
+ rbd snap create ${dest_image}@head
+ rbd bench --io-type write --io-pattern rand --io-size 32K --io-total 4M ${dest_image}
+
+ compare_images "${base_image}@1" "${dest_image}@snap1"
+ compare_images "${base_image}@2" "${dest_image}@snap2"
+ compare_images "${base_image}" "${dest_image}@head"
+
+ rbd migration abort ${dest_image}
+
+ cat ${TEMPDIR}/spec.json | rbd migration prepare --import-only \
+ --source-spec-path - ${dest_image}
+
+ rbd snap create ${dest_image}@head
+ rbd bench --io-type write --io-pattern rand --io-size 64K --io-total 8M ${dest_image}
+
+ compare_images "${base_image}@1" "${dest_image}@snap1"
+ compare_images "${base_image}@2" "${dest_image}@snap2"
+ compare_images "${base_image}" "${dest_image}@head"
+
+ rbd migration execute ${dest_image}
+
+ compare_images "${base_image}@1" "${dest_image}@snap1"
+ compare_images "${base_image}@2" "${dest_image}@snap2"
+ compare_images "${base_image}" "${dest_image}@head"
+
+ rbd migration commit ${dest_image}
+
+ compare_images "${base_image}@1" "${dest_image}@snap1"
+ compare_images "${base_image}@2" "${dest_image}@snap2"
+ compare_images "${base_image}" "${dest_image}@head"
+
+ remove_image "${dest_image}"
+
kill_nbd_server
}
test_import_qcow_format ${IMAGE1} ${IMAGE2}
test_import_qcow2_format ${IMAGE2} ${IMAGE3}
-test_import_nbd_stream ${IMAGE2} ${IMAGE3}
+test_import_nbd_stream_qcow2 ${IMAGE2} ${IMAGE3}
test_import_raw_format ${IMAGE1} ${IMAGE2}
+test_import_nbd_stream_raw ${IMAGE1} ${IMAGE2}
echo OK
namespace {
-const std::string SERVER_KEY {"server"};
-const std::string PORT_KEY {"port"};
+const std::string URI_KEY{"uri"};
int from_nbd_errno(int rc) {
// nbd_get_errno() needs a default/fallback error:
template <typename I>
void NBDStream<I>::open(Context* on_finish) {
+ std::string uri;
int rc;
- auto& server_value = m_json_object[SERVER_KEY];
- if (server_value.type() != json_spirit::str_type) {
- lderr(m_cct) << "failed to locate '" << SERVER_KEY << "' key" << dendl;
- on_finish->complete(-EINVAL);
- return;
- }
-
- auto& port_value = m_json_object[PORT_KEY];
- if (port_value.type() != json_spirit::str_type) {
- lderr(m_cct) << "failed to locate '" << PORT_KEY << "' key" << dendl;
+ if (auto it = m_json_object.find(URI_KEY);
+ it != m_json_object.end()) {
+ if (it->second.type() == json_spirit::str_type) {
+ uri = it->second.get_str();
+ } else {
+ lderr(m_cct) << "invalid URI" << dendl;
+ on_finish->complete(-EINVAL);
+ return;
+ }
+ } else {
+ lderr(m_cct) << "missing URI" << dendl;
on_finish->complete(-EINVAL);
return;
}
- const char *m_server = &(server_value.get_str())[0];
- const char *m_port = &(port_value.get_str())[0];
+ ldout(m_cct, 10) << "uri=" << uri << dendl;
m_nbd = nbd_create();
if (m_nbd == nullptr) {
return;
}
- rc = nbd_connect_tcp(m_nbd, m_server, m_port);
+ rc = nbd_connect_uri(m_nbd, uri.c_str());
if (rc == -1) {
rc = nbd_get_errno();
- lderr(m_cct) << "connect_tcp: " << nbd_get_error()
+ lderr(m_cct) << "connect_uri: " << nbd_get_error()
<< " (errno = " << rc << ")" << dendl;
on_finish->complete(from_nbd_errno(rc));
return;
}
- ldout(m_cct, 20) << "server=" << m_server << ", "
- << "port=" << m_port << dendl;
-
on_finish->complete(0);
}
TestMockFixture::SetUp();
ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx));
- json_object["url"] = "localhost";
- json_object["port"] = "10809";
+ m_json_object["uri"] = "nbd://foo.example";
}
librbd::ImageCtx *m_image_ctx;
- json_spirit::mObject json_object;
+ json_spirit::mObject m_json_object;
};
-TEST_F(TestMockMigrationNBDStream, OpenClose) {
+TEST_F(TestMockMigrationNBDStream, OpenInvalidURI) {
MockTestImageCtx mock_image_ctx(*m_image_ctx);
- MockNBDStream mock_nbd_stream(&mock_image_ctx, json_object);
+ m_json_object["uri"] = 123;
+ MockNBDStream mock_nbd_stream(&mock_image_ctx, m_json_object);
C_SaferCond ctx1;
mock_nbd_stream.open(&ctx1);
- // Since we don't have an nbd server running, we actually expect a failure.
- ASSERT_EQ(-22, ctx1.wait());
+ ASSERT_EQ(-EINVAL, ctx1.wait());
+
+ C_SaferCond ctx2;
+ mock_nbd_stream.close(&ctx2);
+ ASSERT_EQ(0, ctx2.wait());
+}
+
+TEST_F(TestMockMigrationNBDStream, OpenMissingURI) {
+ MockTestImageCtx mock_image_ctx(*m_image_ctx);
+
+ m_json_object.clear();
+ MockNBDStream mock_nbd_stream(&mock_image_ctx, m_json_object);
+
+ C_SaferCond ctx1;
+ mock_nbd_stream.open(&ctx1);
+ ASSERT_EQ(-EINVAL, ctx1.wait());
C_SaferCond ctx2;
mock_nbd_stream.close(&ctx2);