Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@ stickysidebar
Stillaway
Stirnimann
Stolte
storageversion
Storbeck
Storesund
stou
Expand Down
33 changes: 32 additions & 1 deletion docs/http-api/swagger/authoritative-api-swagger.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
swagger: '2.0'
info:
version: "0.0.17"
version: "0.0.18"
title: PowerDNS Authoritative HTTP API
license:
name: MIT
Expand Down Expand Up @@ -79,6 +79,30 @@ paths:
$ref: '#/definitions/Server'
<<: *commonErrors

'/servers/{server_id}/backends/{backend_id}/storageversion':
get:
summary: 'Return the backend storage version used to store zone data.'
description: 'This returns a backend-dependent version information. Currently only supported by the LMDB backend.'
tags:
- zones
parameters:
- name: server_id
in: path
required: true
description: The id of the server to retrieve
type: string
- name: backend_id
type: string
in: path
required: true
description: The name of the backend to query
responses:
'200':
description: Storage version number
schema:
$ref: '#/definitions/StorageVersion'
<<: *commonErrors

'/servers/{server_id}/cache/flush':
put:
summary: Flush a cache-entry by name
Expand Down Expand Up @@ -1611,3 +1635,10 @@ definitions:
items:
$ref: '#/definitions/Network'

StorageVersion:
title: StorageVersion
description: 'The version number of the backend storage for a zone'
properties:
storageversion:
type: number

28 changes: 16 additions & 12 deletions modules/lmdbbackend/lmdbbackend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,8 @@ void copyIndexDBI(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)

}

bool LMDBBackend::upgradeToSchemav5(std::string& filename)
bool LMDBBackend::upgradeToSchemav5(std::string& filename, uint32_t currentSchemaVersion, uint32_t shardCount)
{
auto currentSchemaVersionAndShards = getSchemaVersionAndShards(filename);
uint32_t currentSchemaVersion = currentSchemaVersionAndShards.first;
uint32_t shards = currentSchemaVersionAndShards.second;

if (currentSchemaVersion != 3 && currentSchemaVersion != 4) {
throw std::runtime_error("upgrade to v5 requested but current schema is not v3 or v4, stopping");
}
Expand Down Expand Up @@ -397,7 +393,7 @@ bool LMDBBackend::upgradeToSchemav5(std::string& filename)
#endif

std::cerr << "migrating shards" << std::endl;
for (uint32_t i = 0; i < shards; i++) {
for (uint32_t i = 0; i < shardCount; i++) {
string shardfile = filename + "-" + std::to_string(i);
if (access(shardfile.c_str(), F_OK) < 0) {
if (errno == ENOENT) {
Expand Down Expand Up @@ -659,7 +655,7 @@ bool LMDBBackend::upgradeToSchemav5(std::string& filename)
return true;
}

bool LMDBBackend::upgradeToSchemav6(std::string& /* filename */)
bool LMDBBackend::upgradeToSchemav6(std::string& /* filename */, uint32_t /* currentSchemaVersion */, uint32_t /* shardCount */)
{
// a v6 reader can read v5 databases just fine
// so this function currently does nothing
Expand Down Expand Up @@ -766,16 +762,19 @@ LMDBBackend::LMDBBackend(const std::string& suffix)
LMDBLS::s_flag_deleted = mustDo("flag-deleted");
}

// The current state of this code only supports one schema version and
// requires users to update their schema if outdated.
d_currentschema = SCHEMAVERSION;

bool opened = false;

if (s_first) {
auto lock = std::scoped_lock(s_lmdbStartupLock);
if (s_first) {
auto filename = getArg("filename");

auto currentSchemaVersionAndShards = getSchemaVersionAndShards(filename);
uint32_t currentSchemaVersion = currentSchemaVersionAndShards.first;
// std::cerr<<"current schema version: "<<currentSchemaVersion<<", shards="<<currentSchemaVersionAndShards.second<<std::endl;
auto [currentSchemaVersion, shardCount] = getSchemaVersionAndShards(filename);
// std::cerr<<"current schema version: "<<currentSchemaVersion<<", shards="<<shardCount<<std::endl;

if (getArgAsNum("schema-version") != SCHEMAVERSION) {
throw std::runtime_error("This version of the lmdbbackend only supports schema version 6. Configuration demands a lower version. Not starting up.");
Expand All @@ -791,14 +790,14 @@ LMDBBackend::LMDBBackend(const std::string& suffix)
}

if (currentSchemaVersion == 3 || currentSchemaVersion == 4) {
if (!upgradeToSchemav5(filename)) {
if (!upgradeToSchemav5(filename, currentSchemaVersion, shardCount)) {
throw std::runtime_error("Failed to perform LMDB schema version upgrade from v4 to v5");
}
currentSchemaVersion = 5;
}

if (currentSchemaVersion == 5) {
if (!upgradeToSchemav6(filename)) {
if (!upgradeToSchemav6(filename, currentSchemaVersion, shardCount)) {
throw std::runtime_error("Failed to perform LMDB schema version upgrade from v5 to v6");
}
currentSchemaVersion = 6;
Expand Down Expand Up @@ -3485,6 +3484,11 @@ void LMDBBackend::flush()
}
}

int LMDBBackend::getStorageLayoutVersion()
{
return static_cast<int>(d_currentschema);
}

class LMDBFactory : public BackendFactory
{
public:
Expand Down
7 changes: 5 additions & 2 deletions modules/lmdbbackend/lmdbbackend.hh
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ public:

void flush() override;

int getStorageLayoutVersion() override;

// other
string directBackendCmd(const string& query) override;

Expand All @@ -177,8 +179,8 @@ public:

// functions to use without constructing a backend object
static std::pair<uint32_t, uint32_t> getSchemaVersionAndShards(std::string& filename);
static bool upgradeToSchemav5(std::string& filename);
static bool upgradeToSchemav6(std::string& filename);
static bool upgradeToSchemav5(std::string& filename, uint32_t currentSchemaVersion, uint32_t shardCount);
static bool upgradeToSchemav6(std::string& filename, uint32_t currentSchemaVersion, uint32_t shardCount);

private:
struct compoundOrdername
Expand Down Expand Up @@ -423,6 +425,7 @@ private:
bool d_handle_dups;
bool d_views;
bool d_write_notification_update;
uint32_t d_currentschema;
DTime d_dtime; // used only for logging
uint64_t d_mapsize_main;
uint64_t d_mapsize_shards;
Expand Down
4 changes: 4 additions & 0 deletions pdns/dnsbackend.hh
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,10 @@ public:
{
}

// Return the level/version of the storage layout used by this backend
// (values being obviously backend-specific).
virtual int getStorageLayoutVersion() { return -1; };

protected:
bool mustDo(const string& key);
const string& getArg(const string& key);
Expand Down
47 changes: 21 additions & 26 deletions pdns/pdnsutil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3629,11 +3629,27 @@ static int addOrSetMeta(const ZoneName& zone, const string& kind, const vector<s
return 0;
}

static std::unique_ptr<DNSBackend> getBackendByName(const std::string& name)
{
for (auto& backend : BackendMakers().all()) {
if (backend->getPrefix() == name) {
return std::move(backend);
}
}

return nullptr;
}

// Command handlers

static int lmdbGetBackendVersion([[maybe_unused]] vector<string>& cmds, [[maybe_unused]] const std::string_view synopsis)
{
cout << "6" << endl; // FIXME this should reuse the constant from lmdbbackend but that is currently a #define in a .cc
if (auto lmdbBackend = getBackendByName("lmdb"); lmdbBackend) {
cout << std::to_string(lmdbBackend->getStorageLayoutVersion()) << endl;
}
else {
cout << "LMDB backend not configured." << endl;
}
return 0;
}

Expand Down Expand Up @@ -5375,17 +5391,8 @@ static int B2BMigrate(vector<string>& cmds, const std::string_view synopsis)
return 1;
}

unique_ptr<DNSBackend> src{nullptr};
unique_ptr<DNSBackend> tgt{nullptr};

for (auto& backend : BackendMakers().all()) {
if (backend->getPrefix() == cmds.at(0)) {
src = std::move(backend);
}
else if (backend->getPrefix() == cmds.at(1)) {
tgt = std::move(backend);
}
}
unique_ptr<DNSBackend> src = getBackendByName(cmds.at(0));
unique_ptr<DNSBackend> tgt = getBackendByName(cmds.at(1));

if (src == nullptr) {
cerr << "Unknown source backend '" << cmds.at(0) << "'" << endl;
Expand Down Expand Up @@ -5446,13 +5453,7 @@ static int backendCmd(vector<string>& cmds, const std::string_view synopsis)
return usage(synopsis);
}

std::unique_ptr<DNSBackend> matchingBackend{nullptr};

for (auto& backend : BackendMakers().all()) {
if (backend->getPrefix() == cmds.at(0)) {
matchingBackend = std::move(backend);
}
}
std::unique_ptr<DNSBackend> matchingBackend = getBackendByName(cmds.at(0));

if (matchingBackend == nullptr) {
cerr << "Unknown backend '" << cmds.at(0) << "'" << endl;
Expand Down Expand Up @@ -5480,13 +5481,7 @@ static int backendLookup(vector<string>& cmds, const std::string_view synopsis)
return usage(synopsis);
}

std::unique_ptr<DNSBackend> matchingBackend{nullptr};

for (auto& backend : BackendMakers().all()) {
if (backend->getPrefix() == cmds.at(0)) {
matchingBackend = std::move(backend);
}
}
std::unique_ptr<DNSBackend> matchingBackend = getBackendByName(cmds.at(0));

if (matchingBackend == nullptr) {
cerr << "Unknown backend '" << cmds.at(0) << "'" << endl;
Expand Down
25 changes: 25 additions & 0 deletions pdns/ws-auth.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1926,6 +1926,30 @@ static void apiServerAutoprimariesPOST(HttpRequest* req, HttpResponse* resp)
resp->status = 201;
}

static void apiServerBackendStorageVersion(HttpRequest* req, HttpResponse* resp)
{
const std::string backendName = req->parameters["id"];
UeberBackend backend;
for (auto& onebackend : backend.backends) {
if (onebackend->getPrefix() == backendName) {
auto version = onebackend->getStorageLayoutVersion();
if (version < 0) {
throw ApiException("Storage layout version not available");
}

if (req->accept_json) {
resp->setJsonBody(Json::object{{"storageversion", version}});
}
else {
resp->headers["Content-Type"] = "text/plain; charset=us-ascii";
resp->body = std::to_string(version);
}
return;
}
}
throw ApiException("Unknown backend");
}

// create new zone
static void apiServerZonesPOST(HttpRequest* req, HttpResponse* resp)
{
Expand Down Expand Up @@ -3020,6 +3044,7 @@ void AuthWebServer::webThread()
d_ws->registerApiHandler("/api/v1/servers/localhost/autoprimaries/<ip>/<nameserver>", &apiServerAutoprimaryDetailDELETE, "DELETE");
d_ws->registerApiHandler("/api/v1/servers/localhost/autoprimaries", &apiServerAutoprimariesGET, "GET");
d_ws->registerApiHandler("/api/v1/servers/localhost/autoprimaries", &apiServerAutoprimariesPOST, "POST");
d_ws->registerApiHandler("/api/v1/servers/localhost/backends/<id>/storageversion", apiServerBackendStorageVersion, "GET");
d_ws->registerApiHandler("/api/v1/servers/localhost/networks", apiServerNetworksGET, "GET");
d_ws->registerApiHandler("/api/v1/servers/localhost/networks/<ip>/<prefixlen>", apiServerNetworksGET, "GET");
d_ws->registerApiHandler("/api/v1/servers/localhost/networks/<ip>/<prefixlen>", apiServerNetworksPUT, "PUT");
Expand Down
20 changes: 20 additions & 0 deletions regression-tests.api/test_Backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import print_function
import json
import operator
import unittest
import requests.exceptions
from test_helper import ApiTestCase, is_auth, is_auth_lmdb, BACKEND

@unittest.skipIf(not is_auth(), "Not applicable")
class AuthBackends(ApiTestCase):

def test_storage_version(self):
# Require a json answer to GET
self.session.headers['Content-Type'] = 'application/json'
r = self.session.get(self.url("/api/v1/servers/localhost/backends/" + BACKEND + "/storageversion"))
if is_auth_lmdb():
self.assertEqual(r.status_code, 200)
self.assertGreater(r.json()['storageversion'], 5)
else:
self.assertEqual(r.status_code, 422)

Loading