From: Ondrej Kozina Date: Thu, 22 Aug 2013 14:35:59 +0000 (+0200) Subject: - simple cache for snapper managed logical volumes X-Git-Tag: v0.1.7~8^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef6c18ae944ac94ef8f5b83f76c46e1decf6c7f7;p=thirdparty%2Fsnapper.git - simple cache for snapper managed logical volumes --- diff --git a/snapper/Lvm.cc b/snapper/Lvm.cc index 6707b57d..f7e6a582 100644 --- a/snapper/Lvm.cc +++ b/snapper/Lvm.cc @@ -40,6 +40,7 @@ #include "snapper/SystemCmd.h" #include "snapper/SnapperDefines.h" #include "snapper/Regex.h" +#include "snapper/LvmCache.h" namespace snapper @@ -58,7 +59,8 @@ namespace snapper Lvm::Lvm(const string& subvolume, const string& mount_type) : Filesystem(subvolume), mount_type(mount_type), - caps(LvmCapabilities::get_lvm_capabilities()) + caps(LvmCapabilities::get_lvm_capabilities()), + cache(LvmCache::get_lvm_cache()) { if (access(LVCREATEBIN, X_OK) != 0) { @@ -198,6 +200,15 @@ namespace snapper y2err("mkdir failed errno:" << errno << " (" << strerror(errno) << ")"); throw CreateSnapshotFailedException(); } + + try + { + cache->add(vg_name, snapshotLvName(num)); + } + catch (const LvmCacheException& e) + { + y2war("lvm cache failed"); + } } @@ -208,6 +219,15 @@ namespace snapper if (cmd.retcode() != 0) throw DeleteSnapshotFailedException(); + try + { + cache->remove(vg_name, snapshotLvName(num)); + } + catch (const LvmCacheException& e) + { + y2war("lvm cache failed"); + } + SDir info_dir = openInfoDir(num); info_dir.unlink("snapshot", AT_REMOVEDIR); @@ -237,11 +257,10 @@ namespace snapper try { - activateSnapshot(vg_name, snapshotLvName(num)); + activateSnapshot(vg_name, snapshotLvName(num), true); } catch (const LvmActivationException& e) { - y2err("Couldn't mount snapshot " << num); throw MountSnapshotFailedException(); } @@ -255,21 +274,22 @@ namespace snapper void Lvm::umountSnapshot(unsigned int num) const { - if (!isSnapshotMounted(num)) - return; + if (isSnapshotMounted(num)) + { + SDir info_dir = openInfoDir(num); - SDir info_dir = openInfoDir(num); + if (!umount(info_dir, "snapshot")) + throw UmountSnapshotFailedException(); - if (!umount(info_dir, "snapshot")) - throw UmountSnapshotFailedException(); + } try { - deactivateSnapshot(vg_name, snapshotLvName(num)); + deactivateSnapshot(vg_name, snapshotLvName(num), true); } catch (const LvmDeactivatationException& e) { - y2war("Snapshot #" << num << "deactivation failed"); + y2war("Couldn't deactivate: " << vg_name << "/" << lv_name); } } @@ -277,10 +297,7 @@ namespace snapper bool Lvm::checkSnapshot(unsigned int num) const { - struct stat stat; - int r1 = ::stat(getDevice(num).c_str(), &stat); - - return (r1 == 0 && S_ISBLK(stat.st_mode)) || detectInactiveSnapshot(vg_name, snapshotLvName(num)); + return detectInactiveSnapshot(vg_name, snapshotLvName(num), true); } @@ -297,21 +314,17 @@ namespace snapper vg_name = boost::replace_all_copy(rx.cap(1), "--", "-"); lv_name = boost::replace_all_copy(rx.cap(2), "--", "-"); - SystemCmd cmd(LVSBIN " -o segtype --noheadings " + quote(vg_name + "/" + lv_name)); - - if (cmd.retcode() != 0) { - y2err("could not detect segment type infromation from: " << vg_name << "/" << lv_name); - return false; + try + { + cache->add_or_update(vg_name, lv_name); } - - string segtype = boost::trim_copy(cmd.getLine(0)); - - if (segtype.compare("thin")) { - y2err(vg_name << "/" << lv_name << " is not a LVM thin volume"); + catch(const LvmCacheException& e) + { + y2err("Lvm cache failure"); return false; } - return true; + return cache->contains_thin(vg_name, lv_name); } string @@ -323,31 +336,68 @@ namespace snapper void - Lvm::activateSnapshot(const string& vg_name, const string& lv_name) const + Lvm::activateSnapshot(const string& vg_name, const string& lv_name, bool use_cache) const { - SystemCmd cmd(LVCHANGEBIN + caps->get_ignoreactivationskip() + " -ay " + quote(vg_name + "/" + lv_name)); - if (cmd.retcode() != 0) + if (use_cache) { - y2err("Couldn't activate snapshot " << vg_name << "/" << lv_name); - throw LvmActivationException(); + try + { + cache->activate(vg_name, lv_name); + } + catch(const LvmCacheException& e) + { + y2err("Couldn't activate snapshot " << vg_name << "/" << lv_name); + throw LvmActivationException(); + } + } + else + { + SystemCmd cmd(LVCHANGEBIN + caps->get_ignoreactivationskip() + " -ay " + quote(vg_name + "/" + lv_name)); + if (cmd.retcode() != 0) + { + y2err("Couldn't activate snapshot " << vg_name << "/" << lv_name); + throw LvmActivationException(); + } } } void - Lvm::deactivateSnapshot(const string& vg_name, const string& lv_name) const + Lvm::deactivateSnapshot(const string& vg_name, const string& lv_name, bool use_cache) const { - SystemCmd cmd(LVCHANGEBIN " -an " + quote(vg_name + "/" + lv_name)); - if (cmd.retcode()) - throw LvmDeactivatationException(); + if (use_cache) + { + try + { + cache->deactivate(vg_name, lv_name); + } + catch(const LvmCacheException& e) + { + y2war("lvm cache failure"); + throw LvmDeactivatationException(); + } + } + else + { + SystemCmd cmd(LVCHANGEBIN " -an " + quote(vg_name + "/" + lv_name)); + if (cmd.retcode()) + throw LvmDeactivatationException(); + } } bool - Lvm::detectInactiveSnapshot(const string& vg_name, const string& lv_name) const + Lvm::detectInactiveSnapshot(const string& vg_name, const string& lv_name, bool use_cache) const { - SystemCmd cmd(LVSBIN " " + quote(vg_name + "/" + lv_name)); - return cmd.retcode() == 0; + if (use_cache) + { + return cache->contains(vg_name, lv_name); + } + else + { + SystemCmd cmd(LVSBIN " " + quote(vg_name + "/" + lv_name)); + return cmd.retcode() == 0; + } } diff --git a/snapper/Lvm.h b/snapper/Lvm.h index 462236cb..2e0936c1 100644 --- a/snapper/Lvm.h +++ b/snapper/Lvm.h @@ -54,6 +54,8 @@ namespace snapper bool operator>=(const lvm_version& a, const lvm_version& b); + class LvmCache; + class LvmCapabilities : public boost::noncopyable { public: @@ -104,11 +106,12 @@ namespace snapper const string mount_type; const LvmCapabilities* caps; + LvmCache* cache; bool detectThinVolumeNames(const MtabData& mtab_data); - void activateSnapshot(const string& vg_name, const string& lv_name) const; - void deactivateSnapshot(const string& vg_name, const string& lv_name) const; - bool detectInactiveSnapshot(const string& vg_name, const string& lv_name) const; + void activateSnapshot(const string& vg_name, const string& lv_name, bool use_cache) const; + void deactivateSnapshot(const string& vg_name, const string& lv_name, bool use_cache) const; + bool detectInactiveSnapshot(const string& vg_name, const string& lv_name, bool use_cache) const; string getDevice(unsigned int num) const; diff --git a/snapper/LvmCache.cc b/snapper/LvmCache.cc new file mode 100644 index 00000000..5ea8f0bc --- /dev/null +++ b/snapper/LvmCache.cc @@ -0,0 +1,509 @@ +/* + * Copyright (c) [2013] Red Hat, Inc. + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "config.h" + +#include + +#include +#include +#include + +#include "snapper/Log.h" +#include "snapper/LvmCache.h" +#include "snapper/Lvm.h" +#include "snapper/SystemCmd.h" + +namespace snapper +{ + using std::make_pair; + + bool + LvAttrs::extract_active(const string& raw) + { + return (raw.size() > 4 && raw[4] == 'a'); + } + + + bool + LvAttrs::extract_readonly(const string& raw) + { + return (raw.size() > 1 && raw[1] == 'r'); + } + + + LvAttrs::LvAttrs(const vector& raw) + : active(raw.size() > 0 && extract_active(raw.front())), + readonly(raw.size() > 0 && extract_readonly(raw.front())), + thin(raw.size() > 1 && raw[1] == "thin"), + pool(raw.size() > 2 ? raw[2] : "") + { + } + + + LvAttrs::LvAttrs(bool active, bool readonly, bool thin, string pool) + : active(active), readonly(readonly), thin(thin), pool(pool) + { + } + + + LogicalVolume::LogicalVolume(const VolumeGroup* vg, const string& lv_name) + : vg(vg), lv_name(lv_name), caps(LvmCapabilities::get_lvm_capabilities()), + attrs(caps->get_ignoreactivationskip().empty(), true, true, "") + { + } + + + LogicalVolume::LogicalVolume(const VolumeGroup* vg, const string& lv_name, const LvAttrs& attrs) + : vg(vg), lv_name(lv_name), caps(LvmCapabilities::get_lvm_capabilities()), attrs(attrs) + { + } + + + void + LogicalVolume::activate() + { + /* + * FIXME: There is bug in LVM causing lvs and lvchange commands + * may fail in certain situations. + * Concurrent lvs only commands are fine: + * https://bugzilla.redhat.com/show_bug.cgi?id=922568 + * + * Upgrade lock is used to protect concurrent lvs/lvchange + * in scope of logical volume. + */ + + boost::upgrade_lock upg_lock(lv_mutex); + + if (!attrs.active) + { + boost::upgrade_to_unique_lock unique_lock(upg_lock); + + SystemCmd cmd(LVCHANGEBIN + caps->get_ignoreactivationskip() + " -ay " + quote(vg->get_vg_name() + "/" + lv_name)); + if (cmd.retcode() != 0) + { + y2err("Couldn't activate snapshot " << vg->get_vg_name() << "/" << lv_name); + throw LvmActivationException(); + } + + attrs.active = true; + } + } + + + void + LogicalVolume::deactivate() + { + /* + * FIXME: There is bug in LVM causing lvs and lvchange commands + * may fail in certain situations. + * Concurrent lvs only commands are fine: + * https://bugzilla.redhat.com/show_bug.cgi?id=922568 + * + * Upgrade lock is used to protect concurrent lvs/lvchange + * in scope of logical volume. + */ + + boost::upgrade_lock upg_lock(lv_mutex); + + if (attrs.active) + { + boost::upgrade_to_unique_lock unique_lock(upg_lock); + + SystemCmd cmd(LVCHANGEBIN " -an " + quote(vg->get_vg_name() + "/" + lv_name)); + if (cmd.retcode() != 0) + { + y2err("Couldn't activate snapshot " << vg->get_vg_name() << "/" << lv_name); + throw LvmDeactivatationException(); + } + + attrs.active = false; + } + } + + // vg shared lock + void + LogicalVolume::update() + { + boost::shared_lock shared_lock(lv_mutex); + SystemCmd cmd(LVSBIN " --noheadings -o lv_attr,segtype,pool_lv " + quote(vg->get_vg_name() + "/" + lv_name)); + shared_lock.unlock(); + + if (cmd.retcode() != 0 || cmd.numLines() < 1) + { + y2err("lvm cache failed to get info about " << vg->get_vg_name() << "/" << lv_name); + throw LvmCacheException(); + } + + + vector args; + const string tmp = boost::trim_copy(cmd.getLine(0)); + boost::split(args, tmp, boost::is_any_of(" \t\n"), boost::token_compress_on); + if (args.size() < 1) + throw LvmCacheException(); + + LvAttrs new_attrs(args); + + boost::unique_lock unique_lock(lv_mutex); + + attrs = new_attrs; + } + + + bool + LogicalVolume::readonly() + { + boost::shared_lock shared_lock(lv_mutex); + + return attrs.readonly; + } + + + bool + LogicalVolume::thin() + { + boost::shared_lock shared_lock(lv_mutex); + + return attrs.thin; + } + + + VolumeGroup::VolumeGroup(vg_content_raw& input, const string& vg_name, const string& add_lv_name) + : vg_name(vg_name) + { + for (vg_content_raw::const_iterator cit = input.begin(); cit != input.end(); cit++) + if (cit->first == add_lv_name || cit->first.find("-snapshot") != string::npos) + lv_info_map.insert(make_pair(cit->first, new LogicalVolume(this, cit->first, LvAttrs(cit->second)))); + } + + + void + VolumeGroup::activate(const string& lv_name) + { + boost::shared_lock shared_lock(vg_mutex); + + iterator it = lv_info_map.find(lv_name); + if (it == lv_info_map.end()) + { + y2err(vg_name << "/" << lv_name << " is not in cache!"); + throw LvmCacheException(); + } + + it->second->activate(); + } + + + void + VolumeGroup::deactivate(const string& lv_name) + { + boost::shared_lock shared_lock(vg_mutex); + + iterator it = lv_info_map.find(lv_name); + if (it == lv_info_map.end()) + { + y2err(vg_name << "/" << lv_name << " is not in cache!"); + throw LvmCacheException(); + } + + it->second->deactivate(); + } + + + bool + VolumeGroup::contains(const std::string& lv_name) const + { + boost::shared_lock shared_lock(vg_mutex); + + return lv_info_map.find(lv_name) != lv_info_map.end(); + } + + + bool + VolumeGroup::contains_thin(const string& lv_name) const + { + boost::shared_lock shared_lock(vg_mutex); + + const_iterator cit = lv_info_map.find(lv_name); + + return cit != lv_info_map.end() && cit->second->thin(); + } + + + bool + VolumeGroup::read_only(const string& lv_name) const + { + boost::shared_lock shared_lock(vg_mutex); + + const_iterator cit = lv_info_map.find(lv_name); + + return cit != lv_info_map.end() && cit->second->readonly(); + } + + + void + VolumeGroup::add(const string& lv_name) + { + boost::unique_lock lock(vg_mutex); + + if (!lv_info_map.insert(make_pair(lv_name, new LogicalVolume(this, lv_name))).second) + throw LvmCacheException(); + } + + + void + VolumeGroup::add_or_update(const string& lv_name) + { + boost::upgrade_lock upg_lock(vg_mutex); + + iterator it = lv_info_map.find(lv_name); + if (it != lv_info_map.end()) + { + // FIXME: Is there a better way to downgrade upgrade lock? + upg_lock.release()->unlock_upgrade_and_lock_shared(); + boost::shared_lock_guard shared_guard(vg_mutex, boost::adopt_lock_t()); + + it->second->update(); + } + else + { + SystemCmd cmd(LVSBIN " --noheadings -o lv_attr,segtype,pool_lv " + quote(vg_name + "/" + lv_name)); + if (cmd.retcode() != 0 || cmd.numLines() < 1) + { + y2err("lvm cache failed to get info about " << vg_name << "/" << lv_name); + throw LvmCacheException(); + } + + vector args; + const string tmp = boost::trim_copy(cmd.getLine(0)); + boost::split(args, tmp, boost::is_any_of(" \t\n"), boost::token_compress_on); + if (args.size() < 1) + throw LvmCacheException(); + + LogicalVolume* p_lv = new LogicalVolume(this, lv_name, LvAttrs(args)); + + boost::upgrade_to_unique_lock unique_lock(upg_lock); + lv_info_map.insert(make_pair(lv_name, p_lv)); + } + } + + + void + VolumeGroup::remove(const string& lv_name) + { + boost::unique_lock unique_lock(vg_mutex); + + const_iterator cit = lv_info_map.find(lv_name); + + if (cit == lv_info_map.end()) + throw LvmCacheException(); + + delete cit->second; + lv_info_map.erase(cit); + } + + + void + VolumeGroup::rename(const string& old_name, const string& new_name) + { + boost::upgrade_lock upg_lock(vg_mutex); + + const_iterator cit = lv_info_map.find(old_name); + + if (cit == lv_info_map.end() || lv_info_map.find(new_name) != lv_info_map.end()) + throw LvmCacheException(); + + boost::upgrade_to_unique_lock unique_lock(upg_lock); + + lv_info_map.insert(make_pair(new_name, new LogicalVolume(this, new_name, cit->second->attrs))); + + delete cit->second; + lv_info_map.erase(cit); + } + + + LvmCache* + LvmCache::get_lvm_cache() + { + static LvmCache cache; + return &cache; + } + + + LvmCache::~LvmCache() + { + for (const_iterator cit = vgroups.begin(); cit != vgroups.end(); cit++) + delete cit->second; + } + + + void + LvmCache::activate(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + + if (cit == vgroups.end()) + { + y2err("VG " << vg_name << " is not in cache!"); + throw LvmCacheException(); + } + + cit->second->activate(lv_name); + } + + + void + LvmCache::deactivate(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + + if (cit == vgroups.end()) + { + y2err("VG " << vg_name << " is not in cache!"); + throw LvmCacheException(); + } + + cit->second->deactivate(lv_name); + } + + + bool + LvmCache::read_only(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + + return cit != vgroups.end() && cit->second->read_only(lv_name); + } + + + bool + LvmCache::contains(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + + return cit != vgroups.end() && cit->second->contains(lv_name); + } + + + bool + LvmCache::contains_thin(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + + return cit != vgroups.end() && cit->second->contains_thin(lv_name); + } + + + void + LvmCache::add_or_update(const string& vg_name, const string& lv_name) + { + const_iterator cit = vgroups.find(vg_name); + if (cit == vgroups.end()) + add_vg(vg_name, lv_name); + else + { + cit->second->add_or_update(lv_name); + } + } + + + void + LvmCache::add(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + if (cit == vgroups.end()) + { + y2err("VG " << vg_name << " is not in cache!"); + throw LvmCacheException(); + } + + try + { + cit->second->add(lv_name); + } + catch(const LvmCacheException& e) + { + y2err(vg_name << "/" << lv_name << " already in cache!"); + throw; + } + } + + + void + LvmCache::add_vg(const string& vg_name, const string& include_lv_name) + { + SystemCmd cmd(LVSBIN " --noheadings -o lv_name,lv_attr,segtype,pool_lv " + quote(vg_name)); + if (cmd.retcode() != 0) + { + y2err("lvm cache failed to get info about VG: " << vg_name); + throw LvmCacheException(); + } + + vg_content_raw new_content; + + for (vector::const_iterator cit = cmd.stdout().begin(); cit != cmd.stdout().end(); cit++) + { + vector args; + + const string tmp = boost::trim_copy(*cit); + boost::split(args, tmp, boost::is_any_of(" \t\n"), boost::token_compress_on); + if (args.size() < 1) + throw LvmCacheException(); + + new_content.insert(make_pair(args.front(), vector(args.begin() + 1, args.end()))); + } + + VolumeGroup *p_vg = new VolumeGroup(new_content, vg_name, include_lv_name); + + vgroups.insert(std::make_pair(vg_name, p_vg)); + } + + + void + LvmCache::remove(const string& vg_name, const string& lv_name) const + { + const_iterator cit = vgroups.find(vg_name); + + if (cit != vgroups.end()) + cit->second->remove(lv_name); + } + + + void + LvmCache::rename(const string& vg_name, const string& old_name, const string& new_name) const + { + const_iterator cit = vgroups.find(vg_name); + + if (cit == vgroups.end()) + { + y2err("VG " << vg_name << " is not in cache!"); + throw LvmCacheException(); + } + + try + { + cit->second->rename(old_name, new_name); + } + catch (const LvmCacheException& e) + { + y2err("lvm cache failed to rename " << vg_name << "/" << old_name << " -> " << vg_name << "/" << new_name); + throw; + } + } +} diff --git a/snapper/LvmCache.h b/snapper/LvmCache.h new file mode 100644 index 00000000..81e219a9 --- /dev/null +++ b/snapper/LvmCache.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) [2013] Red Hat, Inc. + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef SNAPPER_LVM_CACHE_H +#define SNAPPER_LVM_CACHE_H + +#include +#include +#include +#include + +#include +#include + + + +namespace snapper +{ + using std::map; + using std::set; + using std::string; + using std::vector; + + class LvmCapabilities; + class VolumeGroup; + + typedef map> vg_content_raw; + + struct LvmCacheException : public std::exception + { + explicit LvmCacheException() throw() {} + virtual const char* what() const throw() { return "lvm cache exception"; } + }; + + struct LvAttrs + { + static bool extract_active(const string& raw); + static bool extract_readonly(const string& raw); + + LvAttrs(const vector& raw); + LvAttrs(bool active, bool readonly, bool thin, string pool); + //LvAttrs() : active(false), readonly(false), thin(false), pool() {} + + bool active; + bool readonly; + bool thin; + string pool; + }; + + + class LogicalVolume : boost::noncopyable + { + public: + friend class VolumeGroup; + + // default constructor + LogicalVolume(const VolumeGroup* vg, const string& lv_name); + LogicalVolume(const VolumeGroup* vg, const string& lv_name, const LvAttrs& attrs); + + void activate(); // upg -> excl. lock + void deactivate(); // upg -> excl. lock + + void update(); // shared, unique_lock + + bool readonly(); // shared + bool thin(); // shared + + private: + const VolumeGroup* vg; + const string lv_name; + const LvmCapabilities* caps; + + LvAttrs attrs; + + mutable boost::upgrade_mutex lv_mutex; + }; + + + class VolumeGroup : boost::noncopyable + { + public: + + // store pointer: LvInfo can be modified + typedef map vg_content_t; + typedef vg_content_t::iterator iterator; + typedef vg_content_t::const_iterator const_iterator; + + VolumeGroup(vg_content_raw& input, const string& vg_name, const string& add_lv_name); + + string get_vg_name() const { return vg_name; } + + void activate(const string& lv_name); // shared lock + void deactivate(const string& lv_name); // shared lock + + bool contains(const string& lv_name) const; // shared lock + bool contains_thin(const string& lv_name) const; // shared lock + + bool read_only(const string& lv_name) const; // shared lock + + void add(const string& lv_name); // excl lock + void add_or_update(const string& lv_name); // upg lock -> excl + + void remove(const string& lv_name); // excl lock + void rename(const string& old_name, const string& new_name); // upg lock -> excl + + private: + const string vg_name; + + mutable boost::upgrade_mutex vg_mutex; + + vg_content_t lv_info_map; + }; + + + class LvmCache : public boost::noncopyable + { + public: + static LvmCache* get_lvm_cache(); + + ~LvmCache(); + + // storing pointers in case we will need locking (mutex is noncopyable) + typedef map::const_iterator const_iterator; + typedef map::iterator iterator; + + void activate(const string& vg_name, const string& lv_name) const; + void deactivate(const string& vg_name, const string& lv_name) const; + + bool contains(const string& vg_name, const string& lv_name) const; + bool contains_thin(const string& vg_name, const string& lv_name) const; + bool read_only(const string& vg_name, const string& lv_name) const; + + // add snapshot owned by snapper + void add(const string& vg_name, const string& lv_name) const; + // used to actualise info about origin volume + void add_or_update(const string& vg_name, const string& lv_name); + + // remove snapshot owned by snapper + void remove(const string& vg_name, const string& lv_name) const; + + // rename snapshots (used during import) + void rename(const string& vg_name, const string& old_name, const string& new_name) const; + + private: + LvmCache() {} + + // load all snapper's snapshots in vg_name VG and also add 'add_lv_name' LV + void add_vg(const string& vg_name, const string& include_lv_name); + + map vgroups; + }; +} +#endif // SNAPPER_LVM_CACHE_H diff --git a/snapper/Makefile.am b/snapper/Makefile.am index 335d885c..fba548da 100644 --- a/snapper/Makefile.am +++ b/snapper/Makefile.am @@ -46,7 +46,8 @@ endif if ENABLE_LVM libsnapper_la_SOURCES += \ - Lvm.cc Lvm.h + Lvm.cc Lvm.h \ + LvmCache.cc LvmCache.h endif