[PATCH 10/24] Implement save/restore for backstores and fabrics

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



As discussed on the list, this adds save and restore commands to the root.
When saving, the object hierarchy generates a dict of its state via dump()
methods, which is saved to a file.

The restore command reads in a saved file and reconstitutes the objects
based on it.

Signed-off-by: Andy Grover <agrover@xxxxxxxxxx>
---
 rtslib/node.py   |    9 +++++
 rtslib/root.py   |   94 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 rtslib/target.py |   51 +++++++++++++++++++++++++++++
 rtslib/tcm.py    |   39 ++++++++++++++++++++++
 4 files changed, 191 insertions(+), 2 deletions(-)

diff --git a/rtslib/node.py b/rtslib/node.py
index 142e330..3fc5383 100644
--- a/rtslib/node.py
+++ b/rtslib/node.py
@@ -231,6 +231,15 @@ class CFSNode(object):
                         + "False if this object instanciation just looked " \
                         + "up the underlying configFS object.")
 
+    def dump(self):
+        d = {}
+        attrs = {}
+        for item in self.list_attributes(writable=True):
+            attrs[item] = int(self.get_attribute(item))
+        if attrs:
+            d['attributes'] = attrs
+        return d
+
 def _test():
     import doctest
     doctest.testmod()
diff --git a/rtslib/root.py b/rtslib/root.py
index d405b5e..033659b 100644
--- a/rtslib/root.py
+++ b/rtslib/root.py
@@ -22,11 +22,18 @@ import os
 import glob
 
 from node import CFSNode
-from target import Target, FabricModule
+from target import Target, FabricModule, TPG, MappedLUN, LUN, NetworkPortal, NodeACL
 from tcm import FileIOBackstore, BlockBackstore
 from tcm import PSCSIBackstore, RDMCPBackstore
 from utils import RTSLibError, RTSLibBrokenLink, flatten_nested_list, modprobe
 
+backstores = dict(
+    fileio=FileIOBackstore,
+    block=BlockBackstore,
+    pscsi=PSCSIBackstore,
+    rd_mcp=RDMCPBackstore,
+    )
+
 class RTSRoot(CFSNode):
     '''
     This is an interface to the root of the configFS object tree.
@@ -96,7 +103,6 @@ class RTSRoot(CFSNode):
                         backstores.add(
                             RDMCPBackstore(int(regex.group(3)), 'lookup'))
         return backstores
-
     def _list_storage_objects(self):
         self._check_self()
         return set(flatten_nested_list([backstore.storage_objects
@@ -133,6 +139,90 @@ class RTSRoot(CFSNode):
 
     # RTSRoot public stuff
 
+    def dump(self):
+        d = super(RTSRoot, self).dump()
+        # backstores:storage_object is *usually* 1:1. In any case, they're an
+        # implementation detail that the user doesn't need to care about.
+        # Return an array of storageobject info with the crucial plugin name
+        # added from backstore, instead of a list of sos for each bs.
+        d['storage_objects'] = []
+        for bs in self.backstores:
+            for so in bs.storage_objects:
+                so_dump = so.dump()
+                so_dump['plugin'] = bs.plugin
+                d['storage_objects'].append(so_dump)
+        d['targets'] = [t.dump() for t in self.targets]
+        return d
+
+    def restore(self, config, clear_existing=False):
+
+        if clear_existing:
+            for so in self.storage_objects:
+                so.delete()
+            for t in self.targets:
+                t.delete()
+
+        if not clear_existing and (self.backstores or self.targets):
+            raise RTSLibError("backstores or targets present, not restoring." +
+                              " Set clear_existing=True?")
+
+        def del_if_there(d, items):
+            for item in items:
+                if item in d:
+                    del d[item]
+
+        def set_attributes(obj, attr_dict):
+            for name, value in attr_dict.iteritems():
+                try:
+                    obj.set_attribute(name, value)
+                except RTSLibError:
+                    # Setting some attributes may return an error, before kernel 3.3
+                    pass
+
+        for index, so in enumerate(config['storage_objects']):
+            # We need to create a Backstore object for each StorageObject
+            bs_obj = backstores[so['plugin']](index)
+
+            # Instantiate storageobject
+            kwargs = so.copy()
+            del_if_there(kwargs, ('exists', 'attributes', 'plugin'))
+            so_obj = bs_obj._storage_object_class(bs_obj, **kwargs)
+            set_attributes(so_obj, so['attributes'])
+
+        for t in config['targets']:
+            fm = FabricModule(t['fabric'])
+
+            # Instantiate target
+            t_obj = Target(fm, t.get('wwn'))
+
+            for tpg in t['tpgs']:
+                tpg_obj = TPG(t_obj)
+                set_attributes(tpg_obj, tpg['attributes'])
+
+                for lun in tpg['luns']:
+                    bs_name, so_name = lun['storage_object'].split('/')[2:]
+                    match_so = [x for x in self.storage_objects if so_name == x.name]
+                    match_so = [x for x in match_so if bs_name == x.backstore.plugin]
+                    if len(match_so) != 1:
+                        raise RTSLibError("Can't find storage object %s" %
+                                          lun['storage_object'])
+                    lun_obj = LUN(tpg_obj, lun.get('index'), storage_object=match_so[0])
+
+                for p in tpg['portals']:
+                    NetworkPortal(tpg_obj, p['ip_address'], p['port'])
+
+                for acl in tpg['node_acls']:
+                    acl_obj = NodeACL(tpg_obj, acl['node_wwn'])
+                    set_attributes(tpg_obj, tpg['attributes'])
+                    for mlun in acl['mapped_luns']:
+                        mlun_obj = MappedLUN(acl_obj, mlun['index'],
+                                             mlun['index'], mlun.get('write_protect'))
+
+                    del_if_there(acl, ('attributes', 'mapped_luns', 'node_wwn'))
+                    for name, value in acl.iteritems():
+                        if value:
+                            setattr(acl_obj, name, value)
+
     backstores = property(_list_backstores,
             doc="Get the list of Backstore objects.")
     targets = property(_list_targets,
diff --git a/rtslib/target.py b/rtslib/target.py
index 6403306..584809e 100644
--- a/rtslib/target.py
+++ b/rtslib/target.py
@@ -536,6 +536,14 @@ class LUN(CFSNode):
     mapped_luns = property(_list_mapped_luns,
             doc="List all MappedLUN objects referencing this LUN.")
 
+    def dump(self):
+        d = super(LUN, self).dump()
+        d['storage_object'] = "/backstores/%s/%s" % \
+            (self.storage_object.backstore.plugin,  self.storage_object.name)
+        d['index'] = self.lun
+        return d
+
+
 class MappedLUN(CFSNode):
     '''
     This is an interface to RTS Target Mapped LUNs.
@@ -704,6 +712,13 @@ class MappedLUN(CFSNode):
     node_wwn = property(_get_node_wwn,
             doc="Get the wwn of the node for which the TPG LUN is mapped.")
 
+    def dump(self):
+        d = super(MappedLUN, self).dump()
+        d['write_protect'] = self.write_protect
+        d['index'] = self.mapped_lun
+        return d
+
+
 class NodeACL(CFSNode):
     '''
     This is an interface to node ACLs in configFS.
@@ -887,6 +902,18 @@ class NodeACL(CFSNode):
     mapped_luns = property(_list_mapped_luns,
             doc="Get the list of all MappedLUN objects in this NodeACL.")
 
+    def dump(self):
+        d = super(NodeACL, self).dump()
+        d['chap_userid'] = self.chap_userid
+        d['chap_password'] = self.chap_password
+        d['chap_mutual_userid'] = self.chap_mutual_userid
+        d['chap_mutual_password'] = self.chap_mutual_password
+        d['tcq_depth'] = int(self.tcq_depth)
+        d['node_wwn'] = self.node_wwn
+        d['mapped_luns'] = [lun.dump() for lun in self.mapped_luns]
+        return d
+
+
 class NetworkPortal(CFSNode):
     '''
     This is an interface to NetworkPortals in configFS.  A NetworkPortal is
@@ -959,6 +986,13 @@ class NetworkPortal(CFSNode):
     ip_address = property(_get_ip_address,
             doc="Get the NetworkPortal's IP address as a string.")
 
+    def dump(self):
+        d = super(NetworkPortal, self).dump()
+        d['port'] = self.port
+        d['ip_address'] = self.ip_address
+        return d
+
+
 class TPG(CFSNode):
     '''
     This is a an interface to Target Portal Groups in configFS.
@@ -1219,6 +1253,15 @@ class TPG(CFSNode):
     nexus = property(_get_nexus, _set_nexus,
                      doc="Get or set (once) the TPG's Nexus is used.")
 
+    def dump(self):
+        d = super(TPG, self).dump()
+        d['tag'] = self.tag
+        d['luns'] = [lun.dump() for lun in self.luns]
+        d['portals'] = [portal.dump() for portal in self.network_portals]
+        d['node_acls'] =  [acl.dump() for acl in self.node_acls]
+        return d
+
+
 class Target(CFSNode):
     '''
     This is an interface to Targets in configFS.
@@ -1299,6 +1342,14 @@ class Target(CFSNode):
 
     tpgs = property(_list_tpgs, doc="Get the list of TPG for the Target.")
 
+    def dump(self):
+        d = super(Target, self).dump()
+        d['wwn'] = self.wwn
+        d['fabric'] = self.fabric_module.name
+        d['tpgs'] = [tpg.dump() for tpg in self.tpgs]
+        return d
+
+
 def _test():
     testmod()
 
diff --git a/rtslib/tcm.py b/rtslib/tcm.py
index 0b48d67..3a19712 100644
--- a/rtslib/tcm.py
+++ b/rtslib/tcm.py
@@ -117,6 +117,14 @@ class Backstore(CFSNode):
     name = property(_get_name,
             doc="Get the backstore name.")
 
+    def dump(self):
+        d = super(Backstore, self).dump()
+        d['storage_objects'] = [so.dump() for so in self.storage_objects]
+        d['plugin'] = self.plugin
+        d['name'] = self.name
+        return d
+
+
 class PSCSIBackstore(Backstore):
     '''
     This is an interface to pscsi backstore plugin objects in configFS.
@@ -169,6 +177,11 @@ class PSCSIBackstore(Backstore):
             doc="Get the legacy mode flag. If True, the Vitualbackstore "
                 + " index must match the StorageObjects real HBAs.")
 
+    def dump(self):
+        d = super(PSCSIBackstore, self).dump()
+        d['legacy_mode'] = self.legacy_mode
+        return d
+
 
 class RDMCPBackstore(Backstore):
     '''
@@ -443,6 +456,13 @@ class StorageObject(CFSNode):
     attached_luns = property(_list_attached_luns,
             doc="Get the list of all LUN objects attached.")
 
+    def dump(self):
+        d = super(StorageObject, self).dump()
+        d['name'] = self.name
+        d['wwn'] = self.wwn
+        return d
+
+
 class PSCSIStorageObject(StorageObject):
     '''
     An interface to configFS storage objects for pscsi backstore.
@@ -719,6 +739,11 @@ class RDMCPStorageObject(StorageObject):
     size = property(_get_size,
             doc="Get the ramdisk size in bytes.")
 
+    def dump(self):
+        d = super(RDMCPStorageObject, self).dump()
+        d['size'] = self.size
+        return d
+
 
 class FileIOStorageObject(StorageObject):
     '''
@@ -871,6 +896,14 @@ class FileIOStorageObject(StorageObject):
     size = property(_get_size,
             doc="Get the current FileIOStorage size in bytes")
 
+    def dump(self):
+        d = super(FileIOStorageObject, self).dump()
+        d['buffered_mode'] = self.buffered_mode
+        d['dev'] = self.udev_path
+        d['size'] = self.size
+        return d
+
+
 class BlockStorageObject(StorageObject):
     '''
     An interface to configFS storage objects for block backstore.
@@ -959,6 +992,12 @@ class BlockStorageObject(StorageObject):
     minor = property(_get_minor,
             doc="Get the block device minor number")
 
+    def dump(self):
+        d = super(BlockStorageObject, self).dump()
+        d['dev'] = self.udev_path
+        return d
+
+
 def _test():
     import doctest
     doctest.testmod()
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe target-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux SCSI]     [Kernel Newbies]     [Linux SCSI Target Infrastructure]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Device Mapper]

  Powered by Linux