237 lines
8.5 KiB
Python
237 lines
8.5 KiB
Python
'''
|
|
Implements the base CFSNode class and a few inherited variants.
|
|
|
|
This file is part of RTSLib Community Edition.
|
|
Copyright (c) 2011 by RisingTide Systems LLC
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, version 3 (AGPLv3).
|
|
|
|
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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
'''
|
|
|
|
import os
|
|
import stat
|
|
from utils import fread, fwrite, RTSLibError
|
|
|
|
class CFSNode(object):
|
|
|
|
# Where do we store the fabric modules spec files ?
|
|
spec_dir = "/var/target/fabric"
|
|
# Where is the configfs base LIO directory ?
|
|
configfs_dir = '/sys/kernel/config/target'
|
|
# TODO: Make the ALUA path generic, not iscsi-centric
|
|
# What is the ALUA directory ?
|
|
alua_metadata_dir = "/var/target/alua/iSCSI"
|
|
|
|
# CFSNode private stuff
|
|
|
|
def __init__(self):
|
|
self._path = self.configfs_dir
|
|
|
|
def __nonzero__(self):
|
|
if os.path.isdir(self.path):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def __str__(self):
|
|
return self.path
|
|
|
|
def _get_path(self):
|
|
return self._path
|
|
|
|
def _create_in_cfs_ine(self, mode):
|
|
'''
|
|
Creates the configFS node if it does not already exists depending on
|
|
the mode.
|
|
any -> makes sure it exists, also works if the node already does exists
|
|
lookup -> make sure it does NOT exists
|
|
create -> create the node which must not exists beforehand
|
|
Upon success (no exception raised), self._fresh is True if a node was
|
|
created, else self._fresh is False.
|
|
'''
|
|
if mode not in ['any', 'lookup', 'create']:
|
|
raise RTSLibError("Invalid mode: %s" % mode)
|
|
if self and mode == 'create':
|
|
raise RTSLibError("This %s already exists in configFS."
|
|
% self.__class__.__name__)
|
|
elif not self and mode == 'lookup':
|
|
raise RTSLibError("No such %s in configfs: %s."
|
|
% (self.__class__.__name__, self.path))
|
|
if not self:
|
|
os.mkdir(self.path)
|
|
self._fresh = True
|
|
else:
|
|
self._fresh = False
|
|
|
|
def _exists(self):
|
|
return bool(self)
|
|
|
|
def _check_self(self):
|
|
if not self:
|
|
raise RTSLibError("This %s does not exist in configFS."
|
|
% self.__class__.__name__)
|
|
|
|
def _is_fresh(self):
|
|
return self._fresh
|
|
|
|
def _list_files(self, path, writable=None):
|
|
'''
|
|
List files under a path depending on their owner's write permissions.
|
|
@param path: The path under which the files are expected to be. If the
|
|
path itself is not a directory, an empty list will be returned.
|
|
@type path: str
|
|
@param writable: If None (default), returns all parameters, if True,
|
|
returns read-write parameters, if False, returns just the read-only
|
|
parameters.
|
|
@type writable: bool or None
|
|
@return: List of file names filtered according to their write perms.
|
|
'''
|
|
if not os.path.isdir(path):
|
|
return []
|
|
|
|
if writable is None:
|
|
names = os.listdir(path)
|
|
elif writable:
|
|
names = [name for name in os.listdir(path)
|
|
if (os.stat("%s/%s" % (path, name))[stat.ST_MODE] \
|
|
& stat.S_IWUSR)]
|
|
else:
|
|
names = [os.path.basename(name) for name in os.listdir(path)
|
|
if not (os.stat("%s/%s" % (path, name))[stat.ST_MODE] \
|
|
& stat.S_IWUSR)]
|
|
names.sort()
|
|
return names
|
|
|
|
# CFSNode public stuff
|
|
|
|
def list_parameters(self, writable=None):
|
|
'''
|
|
@param writable: If None (default), returns all parameters, if True,
|
|
returns read-write parameters, if False, returns just the read-only
|
|
parameters.
|
|
@type writable: bool or None
|
|
@return: The list of existing RFC-3720 parameter names.
|
|
'''
|
|
self._check_self()
|
|
path = "%s/param" % self.path
|
|
return self._list_files(path, writable)
|
|
|
|
def list_attributes(self, writable=None):
|
|
'''
|
|
@param writable: If None (default), returns all attributes, if True,
|
|
returns read-write attributes, if False, returns just the read-only
|
|
attributes.
|
|
@type writable: bool or None
|
|
@return: A list of existing attribute names as strings.
|
|
'''
|
|
self._check_self()
|
|
path = "%s/attrib" % self.path
|
|
return self._list_files(path, writable)
|
|
|
|
def set_attribute(self, attribute, value):
|
|
'''
|
|
Sets the value of a named attribute.
|
|
The attribute must exist in configFS.
|
|
@param attribute: The attribute's name. It is case-sensitive.
|
|
@type attribute: string
|
|
@param value: The attribute's value.
|
|
@type value: string
|
|
'''
|
|
self._check_self()
|
|
path = "%s/attrib/%s" % (self.path, str(attribute))
|
|
if not os.path.isfile(path):
|
|
raise RTSLibError("Cannot find attribute: %s."
|
|
% str(attribute))
|
|
else:
|
|
try:
|
|
fwrite(path, "%s\n" % str(value))
|
|
except IOError:
|
|
raise RTSLibError("Cannot set attribute %s."
|
|
% str(attribute))
|
|
|
|
def get_attribute(self, attribute):
|
|
'''
|
|
@param attribute: The attribute's name. It is case-sensitive.
|
|
@return: The named attribute's value, as a string.
|
|
'''
|
|
self._check_self()
|
|
path = "%s/attrib/%s" % (self.path, str(attribute))
|
|
if not os.path.isfile(path):
|
|
raise RTSLibError("Cannot find attribute: %s."
|
|
% str(attribute))
|
|
else:
|
|
return fread(path).strip()
|
|
|
|
def set_parameter(self, parameter, value):
|
|
'''
|
|
Sets the value of a named RFC-3720 parameter.
|
|
The parameter must exist in configFS.
|
|
@param parameter: The RFC-3720 parameter's name. It is case-sensitive.
|
|
@type parameter: string
|
|
@param value: The parameter's value.
|
|
@type value: string
|
|
'''
|
|
self._check_self()
|
|
path = "%s/param/%s" % (self.path, str(parameter))
|
|
if not os.path.isfile(path):
|
|
raise RTSLibError("Cannot find parameter: %s."
|
|
% str(parameter))
|
|
else:
|
|
try:
|
|
fwrite(path, "%s \n" % str(value))
|
|
except IOError:
|
|
raise RTSLibError("Cannot set parameter %s."
|
|
% str(parameter))
|
|
|
|
def get_parameter(self, parameter):
|
|
'''
|
|
@param parameter: The RFC-3720 parameter's name. It is case-sensitive.
|
|
@type parameter: string
|
|
@return: The named parameter value as a string.
|
|
'''
|
|
self._check_self()
|
|
path = "%s/param/%s" % (self.path, str(parameter))
|
|
if not os.path.isfile(path):
|
|
raise RTSLibError("Cannot find RFC-3720 parameter: %s."
|
|
% str(parameter))
|
|
else:
|
|
return fread(path).rstrip()
|
|
|
|
def delete(self):
|
|
'''
|
|
If the underlying configFS object does not exists, this method does
|
|
nothing. If the underlying configFS object exists, this method attempts
|
|
to delete it.
|
|
'''
|
|
if self:
|
|
os.rmdir(self.path)
|
|
|
|
path = property(_get_path,
|
|
doc="Get the configFS object path.")
|
|
exists = property(_exists,
|
|
doc="Is True as long as the underlying configFS object exists. " \
|
|
+ "If the underlying configFS objects gets deleted " \
|
|
+ "either by calling the delete() method, or by any " \
|
|
+ "other means, it will be False.")
|
|
is_fresh = property(_is_fresh,
|
|
doc="Is True if the underlying configFS object has been created " \
|
|
+ "when instanciating this particular object. Is " \
|
|
+ "False if this object instanciation just looked " \
|
|
+ "up the underlying configFS object.")
|
|
|
|
def _test():
|
|
import doctest
|
|
doctest.testmod()
|
|
|
|
if __name__ == "__main__":
|
|
_test()
|