blob: 010de85654a527d3c66cad7c38eed4f515306401 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "thinpool_migrator/lvm_metadata.h"
#include <cinttypes>
#include <string>
#include <base/rand_util.h>
#include <base/strings/stringprintf.h>
namespace thinpool_migrator {
namespace {
// Metadata for a volume group. This mimics the format reported by vgcfgbackup.
// The generated metadata can be setup onto a device via vgcfgrestore.
constexpr char kVolumeGroupMetadataTemplate[] = R"(
contents = "Text Format Volume Group"
version = 1
description = "Generated by thinpool_migrator"
creation_host = "localhost"
creation_time = %ld
%s {
id = "%s"
seqno = %)" PRIu64 R"(
format = "%s"
status = %s
flags = %s
extent_size = %)" PRIu64 R"(
max_lv = %)" PRIu64 R"(
max_pv = %)" PRIu64 R"(
metadata_copies = %)" PRIu64 R"(
physical_volumes {
%s
}
logical_volumes {
%s
}
})";
// Each volume group's metadata contains a list of physical volumes associated
// with it.
constexpr char kPhysicalVolumeMetadataTemplate[] = R"(
pv%u {
id = "%s"
device = %s
status = %s
flags = %s
dev_size = %)" PRIu64 R"(
pe_start = %)" PRIu64 R"(
pe_count = %)" PRIu64 R"(
})";
// Volume groups also contain a description of all associated logical volumes.
constexpr char kLogicalVolumeMetadataTemplate[] = R"(
%s {
id = "%s"
status = %s
flags = %s
creation_time = %ld
creation_host = "localhost"
segment_count = %zu
%s
})";
// Each logical volume comprises of segments, which may be of the following
// types:
//
// 1. Striped: 1:1 mapping to underlying device(s) via dm-linear
// A 'striped' logical volume can comprise of multiple
// stripes spanning different physical volumes.
// 2. Thin-pool: Thin provisioning layer via dm-thin-pool.
// Each thin-pool, in turn, references two logical volumes for
// data and metadata storage.
// 3. Thin: Thinly provisioned logical volume backed by a thinpool.
constexpr char kLvSegmentMetadataTemplate[] = R"(
segment%u {
start_extent = %)" PRIu64 R"(
extent_count = %)" PRIu64 R"(
type = "%s"
%s
})";
constexpr char kLvCompThinpoolMetadataTemplate[] = R"(
metadata = "%s"
pool = "%s"
transaction_id = %)" PRIu64 R"(
chunk_size = %)" PRIu64 R"(
discards = "%s"
zero_new_blocks = %)" PRIu64;
constexpr char kLvCompThinMetadataTemplate[] = R"(
thin_pool = "%s"
transaction_id = %)" PRIu64 R"(
device_id = %)" PRIu64;
// Thinpool mapping metadata templates describe each thinly provisioned device's
// logical-to-physical mapping. thin-provisioning-tools provides a
// human-readable XML format for modifying thinpool metadata.
//
//
// The 'thinpool metadata' metadata comprises of:
// 1. A superblock which describes the necessary thinpool metadata.
// 2. Device specific metadata, which is keyed using device ids.
// 3. Range/single block mappings per device that represent the l2p mapping.
constexpr char kThinpoolSuperblockTemplate[] =
R"(<superblock uuid="%s" time="%ld" transaction="%)" PRIu64
R"(" flags="%)" PRIu64 R"(" version="%)" PRIu64
R"(" data_block_size="%)" PRIu64 R"(" nr_data_blocks="%)" PRIu64 R"(">
%s
</superblock>)";
constexpr char kThinDeviceMappingTemplate[] =
R"(<device dev_id="%)" PRIu64 R"(" mapped_blocks="%)" PRIu64
R"(" transaction="%)" PRIu64
R"(" creation_time="%ld" snap_time="%ld">
%s
</device>)";
constexpr char kThinBlockRangeMappingTemplate[] =
R"( <range_mapping origin_begin="%)" PRIu64 R"(" data_begin="%)" PRIu64
R"(" length="%)" PRIu64 R"(" time="%ld"/>)";
constexpr char kThinBlockSingleMappingTemplate[] =
R"(<single_mapping origin_block="%)" PRIu64 R"(" data_block="%)" PRIu64
R"(" time="%ld"/>)";
std::string GenerateRandomAlphanumString(int size) {
constexpr const char kCharset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\0";
std::string random_string(size, '0');
for (int i = 0; i < size; ++i) {
random_string[i] = kCharset[base::RandInt(0, strlen(kCharset) - 1)];
}
return random_string;
}
} // namespace
std::string GenerateLvmDeviceId() {
return GenerateRandomAlphanumString(6) + '-' +
GenerateRandomAlphanumString(4) + '-' +
GenerateRandomAlphanumString(4) + '-' +
GenerateRandomAlphanumString(4) + '-' +
GenerateRandomAlphanumString(4) + '-' +
GenerateRandomAlphanumString(4) + '-' +
GenerateRandomAlphanumString(6);
}
std::string GenerateVolumeGroupName() {
return GenerateRandomAlphanumString(16);
}
// Physical volumes are unnamed in metadata and referred to as pv0, pv1...
std::string PhysicalVolumeMetadata::ToString(int num) const {
return base::StringPrintf(kPhysicalVolumeMetadataTemplate, num, id.c_str(),
device.c_str(), status.c_str(), flags.c_str(),
dev_size, pe_start, pe_count);
}
std::string LogicalVolumeSegment::ToString(int num) const {
std::string segment_specific_data;
if (type == "striped") {
segment_specific_data.append(
base::StringPrintf("stripe_count = %zu\n", stripe.stripes.size()));
segment_specific_data.append("stripes = [\n");
for (const auto& i : stripe.stripes) {
segment_specific_data.append(base::StringPrintf(
"\"%s\", %" PRIu64 "\n", i.first.c_str(), i.second));
}
segment_specific_data.append("]\n");
} else if (type == "thin-pool") {
segment_specific_data.append(base::StringPrintf(
kLvCompThinpoolMetadataTemplate, thinpool.metadata.c_str(),
thinpool.pool.c_str(), thinpool.transaction_id, thinpool.chunk_size,
thinpool.discards.c_str(), thinpool.zero_new_blocks));
} else if (type == "thin") {
segment_specific_data.append(
base::StringPrintf(kLvCompThinMetadataTemplate, thin.thin_pool.c_str(),
thin.transaction_id, thin.device_id));
}
return base::StringPrintf(kLvSegmentMetadataTemplate, num, start_extent,
extent_count, type.c_str(),
segment_specific_data.c_str());
}
std::string LogicalVolumeMetadata::GetCollatedSegments() const {
std::string lv_segment_metadata;
int lv_idx = 1;
for (const auto& segment : segments) {
lv_segment_metadata.append(segment.ToString(lv_idx++));
}
return lv_segment_metadata;
}
std::string LogicalVolumeMetadata::ToString() const {
return base::StringPrintf(kLogicalVolumeMetadataTemplate, name.c_str(),
id.c_str(), status.c_str(), flags.c_str(),
creation_time, segments.size(),
GetCollatedSegments().c_str());
}
std::string VolumeGroupMetadata::GetCollatedPvMetadata() const {
std::string physical_volume_metadata;
int pv_count = 0;
for (const auto& pv : pv_metadata) {
physical_volume_metadata.append(pv.ToString(pv_count++));
}
return physical_volume_metadata;
}
std::string VolumeGroupMetadata::GetCollatedLvMetadata() const {
std::string logical_volume_metadata;
for (const auto& lv : lv_metadata) {
logical_volume_metadata.append(lv.ToString());
}
return logical_volume_metadata;
}
std::string VolumeGroupMetadata::ToString() const {
return base::StringPrintf(
kVolumeGroupMetadataTemplate, creation_time, name.c_str(), id.c_str(),
seqno, format.c_str(), status.c_str(), flags.c_str(), extent_size, max_lv,
max_pv, metadata_copies, GetCollatedPvMetadata().c_str(),
GetCollatedLvMetadata().c_str());
}
std::string ThinBlockMapping::ToString() const {
if (type == "range") {
return base::StringPrintf(
kThinBlockRangeMappingTemplate, mapping.range.origin_begin,
mapping.range.data_begin, mapping.range.length, time);
} else {
return base::StringPrintf(kThinBlockSingleMappingTemplate,
mapping.single.origin_block,
mapping.single.data_block, time);
}
}
std::string ThinDeviceMapping::ToString() const {
std::string device_block_mappings;
for (auto& block : mappings) {
device_block_mappings.append(block.ToString());
}
return base::StringPrintf(kThinDeviceMappingTemplate, device_id,
mapped_blocks, transaction, creation_time,
snap_time, device_block_mappings.c_str());
}
std::string ThinpoolSuperblockMetadata::ToString() const {
std::string thin_device_mappings;
for (const auto& device : device_mappings) {
thin_device_mappings.append(device.ToString());
}
return base::StringPrintf(kThinpoolSuperblockTemplate, uuid.c_str(), time,
transaction, flags, version, data_block_size,
nr_data_blocks, thin_device_mappings.c_str());
}
} // namespace thinpool_migrator