diff options
Diffstat (limited to 'swift/obj/vfile_utils.py')
-rw-r--r-- | swift/obj/vfile_utils.py | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/swift/obj/vfile_utils.py b/swift/obj/vfile_utils.py new file mode 100644 index 000000000..8b9036b6c --- /dev/null +++ b/swift/obj/vfile_utils.py @@ -0,0 +1,228 @@ +# Copyright (c) 2010-2012 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import os.path +import pwd +import re + +from swift.common.storage_policy import split_policy_string +from swift.obj.fmgr_pb2 import VOLUME_DEFAULT, VOLUME_TOMBSTONE + +# regex to extract policy from path (one KV per policy) +# TODO: use split_policy_string or similar, not re +policy_re = re.compile(r"^objects(-\d+)?$") +volume_name_re = re.compile(r"^v\d{7}$") +losf_name_re = re.compile(r"^losf(-\d+)?$") + + +class VIOError(IOError): + """ + Exceptions are part of the interface, subclass IOError to make it easier + to interface with diskfile.py + """ + + +class VOSError(OSError): + """ + Exceptions are part of the interface, subclass OSError to make it easier + to interface with diskfile.py + """ + + +class VFileException(Exception): + pass + + +def get_volume_type(extension): + ext_map = { + ".ts": VOLUME_TOMBSTONE + } + + return ext_map.get(extension, VOLUME_DEFAULT) + + +def valid_volume_name(name): + """Returns True if name is a valid volume name, False otherwise""" + if volume_name_re.match(name): + return True + else: + return False + + +def valid_losf_name(name): + """Returns True if name is a valid losf dir name, False otherwise""" + if losf_name_re.match(name): + return True + else: + return False + + +# used by "fsck" to get the socket path from the volume path +def get_socket_path_from_volume_path(volume_path): + volume_path = os.path.normpath(volume_path) + volume_dir_path, volume_name = os.path.split(volume_path) + losf_path, volume_dir = os.path.split(volume_dir_path) + mount_path, losf_dir = os.path.split(losf_path) + if volume_dir != "volumes" or not valid_volume_name(volume_name) or \ + not valid_losf_name(losf_dir): + raise ValueError("Invalid volume path") + + socket_path = os.path.join(losf_path, "rpc.socket") + return socket_path + + +def get_mountpoint_from_volume_path(volume_path): + volume_path = os.path.normpath(volume_path) + volume_dir_path, volume_name = os.path.split(volume_path) + losf_path, volume_dir = os.path.split(volume_dir_path) + mount_path, losf_dir = os.path.split(losf_path) + if volume_dir != "volumes" or not valid_volume_name(volume_name) or \ + not valid_losf_name(losf_dir): + raise ValueError("Invalid volume path") + return mount_path + + +class SwiftPathInfo(object): + def __init__(self, type, socket_path=None, volume_dir=None, + policy_idx=None, partition=None, suffix=None, ohash=None, + filename=None): + self.type = type + self.socket_path = socket_path + self.volume_dir = volume_dir + self.policy_idx = policy_idx + self.partition = partition + self.suffix = suffix + self.ohash = ohash + self.filename = filename + + # parses a swift path, returns a SwiftPathInfo instance + @classmethod + def from_path(cls, path): + count_to_type = { + 4: "file", + 3: "ohash", + 2: "suffix", + 1: "partition", + 0: "partitions" # "objects" directory + } + + clean_path = os.path.normpath(path) + ldir = clean_path.split(os.sep) + + try: + obj_idx = [i for i, elem in enumerate(ldir) + if elem.startswith("objects")][0] + except IndexError: + raise VOSError("cannot parse object directory") + + elements = ldir[(obj_idx + 1):] + count = len(elements) + + if count > 4: + raise VOSError("cannot parse swift file path") + + _, policy = split_policy_string(ldir[obj_idx]) + policy_idx = policy.idx + + prefix = os.path.join("/", *ldir[0:obj_idx]) + m = policy_re.match(ldir[obj_idx]) + if not m: + raise VOSError( + "cannot parse object element of directory") + if m.group(1): + sofsdir = "losf{}".format(m.group(1)) + else: + sofsdir = "losf" + socket_path = os.path.join(prefix, sofsdir, "rpc.socket") + volume_dir = os.path.join(prefix, sofsdir, "volumes") + + type = count_to_type[count] + return cls(type, socket_path, volume_dir, policy_idx, *elements) + + +class SwiftQuarantinedPathInfo(object): + def __init__(self, type, socket_path=None, volume_dir=None, + policy_idx=None, ohash=None, filename=None): + self.type = type + self.socket_path = socket_path + self.volume_dir = volume_dir + self.policy_idx = policy_idx + self.ohash = ohash + self.filename = filename + + # parses a quarantined path (<device>/quarantined/objects-X or below), + # returns a SwiftQuarantinedPathInfo instance + @classmethod + def from_path(cls, path): + count_to_type = { + 3: "file", + 2: "ohash", + 1: "ohashes", + } + + clean_path = os.path.normpath(path) + ldir = clean_path.split(os.sep) + + try: + quar_idx = ldir.index("quarantined") + except ValueError: + raise VOSError("cannot parse quarantined path %s" % + path) + + elements = ldir[(quar_idx + 1):] + count = len(elements) + + if count < 1 or count > 3 or "objects" not in elements[0]: + raise VOSError("cannot parse quarantined path %s" % + path) + + _, policy = split_policy_string(elements[0]) + policy_idx = policy.idx + + prefix = os.path.join("/", *ldir[:quar_idx]) + prefix = os.path.join(prefix, elements[0].replace("objects", "losf")) + socket_path = os.path.join(prefix, "rpc.socket") + volume_dir = os.path.join(prefix, "volumes") + + type = count_to_type[count] + return cls(type, socket_path, volume_dir, policy_idx, *elements[1:]) + + +def get_volume_index(volume_path): + """ + returns the volume index, either from its basename, or full path + """ + name = os.path.split(volume_path)[1] + + if not valid_volume_name(name): + raise ValueError("Invalid volume name") + + index = int(name[1:8]) + return index + + +# given an offset and alignement, returns the next aligned offset +def next_aligned_offset(offset, alignment): + if offset % alignment != 0: + return (offset + (alignment - offset % alignment)) + else: + return offset + + +def change_user(username): + pw = pwd.getpwnam(username) + uid = pw.pw_uid + os.setuid(uid) |