]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- simple cache for snapper managed logical volumes
authorOndrej Kozina <okozina@redhat.com>
Thu, 22 Aug 2013 14:35:59 +0000 (16:35 +0200)
committerOndrej Kozina <okozina@redhat.com>
Thu, 22 Aug 2013 14:35:59 +0000 (16:35 +0200)
snapper/Lvm.cc
snapper/Lvm.h
snapper/LvmCache.cc [new file with mode: 0644]
snapper/LvmCache.h [new file with mode: 0644]
snapper/Makefile.am

index 6707b57d05f9949271068c8f21453b440950f022..f7e6a58263174c4536081ea32702d1af59b8b791 100644 (file)
@@ -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;
+       }
     }
 
 
index 462236cb6b6c1acaacf879900ed3225568b1e714..2e0936c1e12c6141c41375a99dbaecca15d7ef24 100644 (file)
@@ -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 (file)
index 0000000..5ea8f0b
--- /dev/null
@@ -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 <vector>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/thread/lock_options.hpp>
+#include <boost/thread/shared_lock_guard.hpp>
+
+#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<string>& 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<boost::upgrade_mutex> upg_lock(lv_mutex);
+
+       if (!attrs.active)
+       {
+           boost::upgrade_to_unique_lock<boost::upgrade_mutex> 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<boost::upgrade_mutex> upg_lock(lv_mutex);
+
+       if (attrs.active)
+       {
+           boost::upgrade_to_unique_lock<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<string> 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<boost::upgrade_mutex> unique_lock(lv_mutex);
+
+       attrs = new_attrs;
+    }
+
+
+    bool
+    LogicalVolume::readonly()
+    {
+       boost::shared_lock<boost::upgrade_mutex> shared_lock(lv_mutex);
+
+       return attrs.readonly;
+    }
+
+
+    bool
+    LogicalVolume::thin()
+    {
+       boost::shared_lock<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<string> 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<boost::upgrade_mutex> unique_lock(upg_lock);
+           lv_info_map.insert(make_pair(lv_name, p_lv));
+       }
+    }
+
+
+    void
+    VolumeGroup::remove(const string& lv_name)
+    {
+       boost::unique_lock<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<boost::upgrade_mutex> 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<string>::const_iterator cit = cmd.stdout().begin(); cit != cmd.stdout().end(); cit++)
+       {
+           vector<string> 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<string>(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 (file)
index 0000000..81e219a
--- /dev/null
@@ -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 <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+#include <boost/thread.hpp>
+
+
+
+namespace snapper
+{
+    using std::map;
+    using std::set;
+    using std::string;
+    using std::vector;
+
+    class LvmCapabilities;
+    class VolumeGroup;
+
+    typedef map<string, vector<string>> 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<string>& 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<string, LogicalVolume*> 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<string, VolumeGroup*>::const_iterator const_iterator;
+       typedef map<string, VolumeGroup*>::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<string, VolumeGroup*> vgroups;
+    };
+}
+#endif // SNAPPER_LVM_CACHE_H
index 335d885c20591e50f3c4d0233543e887ffbcf3f4..fba548da46983f5dcf08e17ed974a7256f1698b2 100644 (file)
@@ -46,7 +46,8 @@ endif
 
 if ENABLE_LVM
 libsnapper_la_SOURCES +=                               \
-       Lvm.cc                  Lvm.h
+       Lvm.cc                  Lvm.h                   \
+       LvmCache.cc             LvmCache.h
 endif