import types
import uuid
+
class ArgumentError(Exception):
"""
Something wrong with arguments
"""
pass
+
class ArgumentNumber(ArgumentError):
"""
Wrong number of a repeated argument
"""
pass
+
class ArgumentFormat(ArgumentError):
"""
Argument value has wrong format
"""
pass
+
class ArgumentValid(ArgumentError):
"""
Argument value is otherwise invalid (doesn't match choices, for instance)
"""
pass
+
class ArgumentTooFew(ArgumentError):
"""
Fewer arguments than descriptors in signature; may mean to continue
the search, so gets a special exception type
"""
+
class ArgumentPrefix(ArgumentError):
"""
Special for mismatched prefix; less severe, don't report by default
"""
pass
+
class JsonFormat(Exception):
"""
some syntactic or semantic issue with the JSON
"""
pass
+
class CephArgtype(object):
"""
Base class for all Ceph argument types
"""
return '<{0}>'.format(self.__class__.__name__)
+
class CephInt(CephArgtype):
"""
range-limited integers, [+|-][0-9]+ or 0x[0-9a-f]+
r = '[{0}-{1}]'.format(self.range[0], self.range[1])
return '<float{0}>'.format(r)
+
class CephString(CephArgtype):
"""
String; pretty generic. goodchars is a RE char class of valid chars
b += '(goodchars {0})'.format(self.goodchars)
return '<string{0}>'.format(b)
+
class CephSocketpath(CephArgtype):
"""
Admin socket path; check that it's readable and S_ISSOCK
def __str__(self):
return '<admin-socket-path>'
+
class CephIPAddr(CephArgtype):
"""
IP address (v4 or v6) with optional port
def __str__(self):
return '<IPaddr[:port]>'
+
class CephEntityAddr(CephIPAddr):
"""
EntityAddress, that is, IP address[/nonce]
def __str__(self):
return '<EntityAddr>'
+
class CephPoolname(CephArgtype):
"""
Pool name; very little utility
def __str__(self):
return '<poolname>'
+
class CephObjectname(CephArgtype):
"""
Object name. Maybe should be combined with Pool name as they're always
def __str__(self):
return '<objectname>'
+
class CephPgid(CephArgtype):
"""
pgid, in form N.xxx (N = pool number, xxx = hex pgnum)
def __str__(self):
return '<pgid>'
+
class CephName(CephArgtype):
"""
Name (type.id) where:
def __str__(self):
return '<name (type.id)>'
+
class CephOsdName(CephArgtype):
"""
Like CephName, but specific to osds: allow <id> alone
def __str__(self):
return '<osdname (id|osd.id)>'
+
class CephChoices(CephArgtype):
"""
Set of string literals; init with valid choices
else:
return '{0}'.format('|'.join(self.strings))
+
class CephFilepath(CephArgtype):
"""
Openable file
def __str__(self):
return '<outfilename>'
+
class CephFragment(CephArgtype):
"""
'Fragment' ??? XXX
s = '{' + s + '}'
return s
+
def concise_sig(sig):
"""
Return string representation of sig useful for syntax reference in help
"""
return ' '.join([d.helpstr() for d in sig])
+
def descsort(sh1, sh2):
"""
sort descriptors by prefixes, defined as the concatenation of all simple
"""
return cmp(concise_sig(sh1['sig']), concise_sig(sh2['sig']))
+
def parse_funcsig(sig):
"""
parse a single descriptor (array of strings or dicts) into a
sigdict[cmdtag] = cmd
return sigdict
+
def validate_one(word, desc, partial=False):
"""
validate_one(word, desc, partial=False)
if desc.N:
desc.n = desc.numseen + 1
+
def matchnum(args, signature, partial=False):
"""
matchnum(s, signature, partial=False)
matchcnt += 1
return matchcnt
+
def get_next_arg(desc, args):
'''
Get either the value matching key 'desc.name' or the next arg in
arg = arg[0]
return arg
+
def store_arg(desc, d):
'''
Store argument described by, and held in, thanks to valid(),
# if first CephPrefix or any other type, just set it
d[desc.name] = desc.instance.val
+
def validate(args, signature, partial=False):
"""
validate(args, signature, partial=False)
# Finally, success
return d
+
def cmdsiglen(sig):
sigdict = sig.values()
assert len(sigdict) == 1
return len(sig.values()[0]['sig'])
+
def validate_command(sigdict, args, verbose=False):
"""
turn args into a valid dictionary ready to be sent off as JSON,
return valid_dict
+
def find_cmd_target(childargs):
"""
Using a minimal validation, figure out whether the command
return 'mon', ''
+
def send_command(cluster, target=('mon', ''), cmd=None, inbuf='', timeout=0,
verbose=False):
"""
return ret, outbuf, outs
+
def json_command(cluster, target=('mon', ''), prefix=None, argdict=None,
inbuf='', timeout=0, verbose=False):
"""
return ret, outbuf, outs
-
'debug':logging.DEBUG,
}
+
def find_up_osd(app):
'''
Find an up OSD. Return the last one that's up.
METHOD_DICT = {'r':['GET'], 'w':['PUT', 'DELETE']}
+
def api_setup(app, conf, cluster, clientname, clientid, args):
'''
This is done globally, and cluster connection kept open for
#
# end setup (import-time) functions, begin request-time functions
#
-
def concise_sig_for_uri(sig, flavor):
'''
Return a generic description of how one would send a REST request for sig
ret += '?' + '&'.join(args)
return ret
+
def show_human_help(prefix):
'''
Dump table showing commands matching prefix
else:
return ''
+
@app.before_request
def log_request():
'''
app.logger.info(flask.request.url + " from " + flask.request.remote_addr + " " + flask.request.user_agent.string)
app.logger.debug("Accept: %s", flask.request.accept_mimetypes.values())
+
@app.route('/')
def root_redir():
return flask.redirect(app.ceph_baseurl)
+
def make_response(fmt, output, statusmsg, errorcode):
'''
If formatted output, cobble up a response object that contains the
return flask.make_response(response, errorcode)
+
def handler(catchall_path=None, fmt=None, target=None):
'''
Main endpoint handler; generic for every endpoint, including catchall.
response.headers['Content-Type'] = contenttype
return response
+
#
# Main entry point from wrapper/WSGI server: call with cmdline args,
# get back the WSGI app entry point
from ctypes.util import find_library
import errno
+
class Error(Exception):
pass
+
class PermissionError(Error):
pass
+
class ObjectNotFound(Error):
pass
+
class NoData(Error):
pass
+
class ObjectExists(Error):
pass
+
class IOError(Error):
pass
+
class NoSpace(Error):
pass
+
class IncompleteWriteError(Error):
pass
+
class LibCephFSStateError(Error):
pass
+
def make_ex(ret, msg):
"""
Translate a libcephfs return code into an exception.
else:
return Error(msg + (": error code %d" % ret))
+
class cephfs_statvfs(Structure):
_fields_ = [("f_bsize", c_uint),
("f_frsize", c_uint),
("f_flag", c_uint),
("f_namemax", c_uint)]
+
# struct timespec {
# long int tv_sec;
# long int tv_nsec;
_fields_ = [('tv_sec', c_long),
('tv_nsec', c_long)]
+
# struct stat {
# unsigned long st_dev;
# unsigned long st_ino;
('__unused2', c_long),
('__unused3', c_long) ]
+
def load_libcephfs():
"""
Load the libcephfs shared library.
except OSError as e:
raise EnvironmentError("Unable to load libcephfs: %s" % e)
+
class LibCephFS(object):
"""libcephfs python wrapper"""
def require_state(self, *args):
LIBRADOS_OP_FLAG_FADVISE_DONTNEED = 0x20
LIBRADOS_OP_FLAG_FADVISE_NOCACHE = 0x40
+
class Error(Exception):
""" `Error` class, derived from `Exception` """
pass
+
class InterruptedOrTimeoutError(Error):
""" `InterruptedOrTimeoutError` class, derived from `Error` """
pass
+
class PermissionError(Error):
""" `PermissionError` class, derived from `Error` """
pass
+
class ObjectNotFound(Error):
""" `ObjectNotFound` class, derived from `Error` """
pass
+
class NoData(Error):
""" `NoData` class, derived from `Error` """
pass
+
class ObjectExists(Error):
""" `ObjectExists` class, derived from `Error` """
pass
+
class ObjectBusy(Error):
""" `ObjectBusy` class, derived from `Error` """
pass
+
class IOError(Error):
""" `IOError` class, derived from `Error` """
pass
+
class NoSpace(Error):
""" `NoSpace` class, derived from `Error` """
pass
+
class IncompleteWriteError(Error):
""" `IncompleteWriteError` class, derived from `Error` """
pass
+
class RadosStateError(Error):
""" `RadosStateError` class, derived from `Error` """
pass
+
class IoctxStateError(Error):
""" `IoctxStateError` class, derived from `Error` """
pass
+
class ObjectStateError(Error):
""" `ObjectStateError` class, derived from `Error` """
pass
+
class LogicError(Error):
""" `` class, derived from `Error` """
pass
+
class TimedOut(Error):
""" `TimedOut` class, derived from `Error` """
pass
+
def make_ex(ret, msg):
"""
Translate a librados return code into an exception.
else:
return Error(msg + (": errno %s" % errno.errorcode[ret]))
+
class rados_pool_stat_t(Structure):
""" Usage information for a pool """
_fields_ = [("num_bytes", c_uint64),
("num_wr", c_uint64),
("num_wr_kb", c_uint64)]
+
class rados_cluster_stat_t(Structure):
""" Cluster-wide usage information """
_fields_ = [("kb", c_uint64),
("kb_avail", c_uint64),
("num_objects", c_uint64)]
+
class timeval(Structure):
_fields_ = [("tv_sec", c_long), ("tv_usec", c_long)]
def __str__(self):
return "%d.%d.%d" % (self.major, self.minor, self.extra)
+
class RadosThread(threading.Thread):
def __init__(self, target, args=None):
self.args = args
# time in seconds between each call to t.join() for child thread
POLL_TIME_INCR = 0.5
+
def run_in_thread(target, args, timeout=0):
interrupt = False
t.retval = -errno.EINTR
return t.retval
+
class Rados(object):
"""librados python wrapper"""
def require_state(self, *args):
if ret < 0:
raise make_ex(ret, "error blacklisting client '%s'" % client_address)
+
class ObjectIterator(object):
"""rados.Ioctx Object iterator"""
def __init__(self, ioctx):
def __del__(self):
run_in_thread(self.ioctx.librados.rados_nobjects_list_close, (self.ctx,))
+
class XattrIterator(object):
"""Extended attribute iterator"""
def __init__(self, ioctx, it, oid):
def __del__(self):
run_in_thread(self.ioctx.librados.rados_getxattrs_end, (self.it,))
+
class SnapIterator(object):
"""Snapshot iterator"""
def __init__(self, ioctx):
self.cur_snap = self.cur_snap + 1
return snap
+
class Snap(object):
"""Snapshot object"""
def __init__(self, ioctx, name, snap_id):
raise make_ex(ret, "rados_ioctx_snap_get_stamp error")
return datetime.fromtimestamp(snap_time.value)
+
class Completion(object):
"""completion object"""
def __init__(self, ioctx, rados_comp, oncomplete, onsafe,
RADOS_CB = CFUNCTYPE(c_int, c_void_p, c_void_p)
+
class Ioctx(object):
"""rados.Ioctx object"""
def __init__(self, name, librados, io):
raise make_ex(ret, "Ioctx.rados_lock_exclusive(%s): failed to set lock %s on %s" % (self.name, name, key))
-
def set_object_locator(func):
def retfunc(self, *args, **kwargs):
if self.locator_key is not None:
return func(self, *args, **kwargs)
return retfunc
+
def set_object_namespace(func):
def retfunc(self, *args, **kwargs):
if self.nspace is None:
return retval
return retfunc
+
class Object(object):
"""Rados object wrapper, makes the object look like a file"""
def __init__(self, ioctx, key, locator_key=None, nspace=None):
RBD_FLAG_OBJECT_MAP_INVALID = 1
+
class Error(Exception):
pass
+
class PermissionError(Error):
pass
+
class ImageNotFound(Error):
pass
+
class ImageExists(Error):
pass
+
class IOError(Error):
pass
+
class NoSpace(Error):
pass
+
class IncompleteWriteError(Error):
pass
+
class InvalidArgument(Error):
pass
+
class LogicError(Error):
pass
+
class ReadOnlyImage(Error):
pass
+
class ImageBusy(Error):
pass
+
class ImageHasSnapshots(Error):
pass
+
class FunctionNotSupported(Error):
pass
+
class ArgumentOutOfRange(Error):
pass
+
class ConnectionShutdown(Error):
pass
+
class Timeout(Error):
pass
+
def make_ex(ret, msg):
"""
Translate a librbd return code into an exception.
else:
return Error(msg + (": error code %d" % ret))
+
class rbd_image_info_t(Structure):
_fields_ = [("size", c_uint64),
("obj_size", c_uint64),
("parent_pool", c_int64),
("parent_name", c_char * 96)]
+
class rbd_snap_info_t(Structure):
_fields_ = [("id", c_uint64),
("size", c_uint64),
("name", c_char_p)]
+
def load_librbd():
"""
Load the librbd shared library.
except OSError as e:
raise EnvironmentError("Unable to load librbd: %s" % e)
+
class RBD(object):
"""
This class wraps librbd CRUD functions.
if ret != 0:
raise make_ex(ret, 'error renaming image')
+
class Image(object):
"""
This class represents an RBD image. It is used to perform I/O on
if ret < 0:
raise make_ex(ret, 'error unlocking image')
+
class DiffIterateCB(object):
def __init__(self, cb):
self.cb = cb
self.cb(offset, length, exists == 1)
return 0
+
class SnapIterator(object):
"""
Iterator over snapshot info for an image.
import time
import commands
+
def parse_args():
parser = argparse.ArgumentParser(description='watch ceph perf')
parser.add_argument(