CEPH STORAGE CLUSTER OPERATION
ceph的python_api文档: http://docs.ceph.com/docs/master/rados/api/python/
#!/usr/bin/env python
# -*-coding: utf-8-*-
import json
import os
import rados
import rbd
from utils.constant import CSTOR_LOG_PATH
from utils.exception import ExecuteException
from utils.logger import set_logger
cstor_logger = set_logger(os.path.basename(__file__), CSTOR_LOG_PATH)
CEPH_RBD_ERROR = 'CEPH ERROR'
class CLUSTEROperation(object):
def __init__(self, conf):
if not conf:
raise ExecuteException(CEPH_RBD_ERROR, 'conf is none.')
if not os.path.exists(conf):
raise ExecuteException(CEPH_RBD_ERROR, 'conf file not exits.')
self.conf = conf
self.cluster = self._get_cluster()
self._connect_cluster()
def _get_cluster(self):
"""
获取ceph集群
:return:
"""
try:
return rados.Rados(conffile=self.conf)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get cluster failed.')
def _connect_cluster(self):
"""
连接ceph集群
:return:
"""
try:
self.cluster.connect()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'connect cluster failed.')
def close_cluster(self):
"""
断开ceph集群
:return:
"""
try:
self.cluster.shutdown()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'close cluster failed.')
def get_fsid(self):
"""
获取ceph集群fsid值
:return: 7c9c2ba8-dcec-42b0-8231-a2149988b913
"""
try:
return self.cluster.get_fsid().encode()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get fsid failed.')
def get_cluster_stats(self):
"""
获取ceph集群统计数据
:return: {'kb': 3221213184L, 'num_objects': 35390L, 'kb_avail': 2876726592L, 'kb_used': 344486592L}
"""
try:
return self.cluster.get_cluster_stats()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'close cluster failed.')
def get_conf(self, var):
"""
获取ceph集群配置文件(ceph.conf)的中的内容(即使文件中没记载的配置,也可以获取)
:param var: 配置项名称,如:fsid, osd pool default size等
:return:
"""
try:
return self.cluster.conf_get(var).encode()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'close cluster failed.')
def mon_command(self, **kwargs):
"""
执行 ceph 命令
:param kwargs: ceph 命令拼接:如:
ceph df: 即为 prefix='df', format='json',拼接过程中去除ceph,以接送格式输出
ceph status: 即为 prefix='status', format='json',拼接过程中去除ceph,以接送格式输出
ceph mon_status: 即为 prefix='mon_status', format='json',拼接过程中去除ceph,以接送格式输出
ceph osd pool ls detail: 即为 prefix='osd pool ls', detail='detail', format='json',拼接过程中去除ceph,以接送格式输出
:return: code: 执行结果(0成功,其余失败), buf:执行结果, err:错误原因
"""
cmd = json.dumps(kwargs)
try:
code, buf, error = self.cluster.mon_command(cmd, "", timeout=10)
return {"code": code,
"buf": json.loads(buf, encoding="utf-8") if buf else '',
"err": error.encode("utf-8")}
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'execute cmd: %s failed.' % cmd)
class POOLOperation(object):
def __init__(self, cluster):
"""
:param cluster: ceph集群
"""
if not cluster:
raise ExecuteException(CEPH_RBD_ERROR, 'cluster is none.')
self.cluster = cluster
def get_ioctx(self, pool):
"""
获取存储池上下文连接
:param pool: 存储池名称
:return:
"""
try:
return self.cluster.open_ioctx(pool)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get ioctx failed.')
@staticmethod
def close_ioctx(ioctx):
"""
断开存储池上下文连接
:param ioctx: 存储池上下文连接
:return:
"""
try:
ioctx.close()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'close ioctx failed.')
def get_pools(self):
"""
获取存储池列表
:return:
"""
try:
return [i.encode() for i in self.cluster.list_pools()]
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get pool failed.')
def is_pool_exists(self, pool):
"""
检查存储池是否存在
:param pool: 存储池名称
:return: 布尔值
"""
try:
return self.cluster.pool_exists(pool)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'check pool exists failed.')
def create_pool(self, pool):
"""
创建存储池
:param pool: 存储池名称
:return:
"""
try:
if self.is_pool_exists(pool):
raise ExecuteException(CEPH_RBD_ERROR, 'pool already exists.')
self.cluster.create_pool(pool)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'create pool failed.')
def delete_pool(self, pool):
"""
删除存储池(慎用,存储池中若还存有镜像,则会连同数据一起删除)
:param pool: 存储池名称
:return:
"""
try:
self.cluster.delete_pool(pool)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'delete pool failed.')
class IMAGEOperation(object):
def __init__(self, ioctx):
"""
:param ioctx: 存储池上下文连接
"""
if not ioctx:
raise ExecuteException(CEPH_RBD_ERROR, 'ioctx is none.')
self.ioctx = ioctx
self.rbd_inst = self._get_rbd_inst()
@staticmethod
def _get_rbd_inst():
"""
获取rbd对象
:return:
"""
try:
return rbd.RBD()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get rbd object failed.')
def get_images(self):
"""
获取存储池中镜像列表
:return:
"""
try:
return [i.encode() for i in self.rbd_inst.list(self.ioctx)]
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get images failed.')
def is_image_exists(self, name):
"""
检查镜像是否存在
:param name: 镜像名称
:return: 布尔值
"""
try:
flag = False
if name in self.get_images():
flag = True
return flag
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'check images exists failed.')
def create_image(self, name, size):
"""
创建镜像
:param name: 镜像名称
:param size: 镜像大小(bytes)
:return:
"""
try:
if self.is_image_exists(name):
raise ExecuteException(CEPH_RBD_ERROR, 'image already exists.')
self.rbd_inst.create(self.ioctx, name, size)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'create images failed.')
def rename_image(self, name, new_name):
"""
重命名镜像
:param name: 镜像名称
:param new_name: 镜像新名称
:return:
"""
if self.is_image_exists(new_name):
raise ExecuteException(CEPH_RBD_ERROR, 'images %s exits.' % new_name)
try:
self.rbd_inst.rename(self.ioctx, name, new_name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'rename images failed.')
def remove_image(self, name):
"""
删除镜像
:param name: 镜像名称
:return:
"""
try:
self.rbd_inst.remove(self.ioctx, name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'remove images failed.')
def get_mirror_image_status_list(self):
"""
获取镜像状态列表
:return:
"""
try:
return self.rbd_inst.mirror_image_status_list(self.ioctx)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get images status list failed.')
def get_mirror_image_status_summary(self):
"""
获取镜像状态摘要
:return:
"""
try:
return self.rbd_inst.mirror_image_status_summary(self.ioctx)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get images status summary failed.')
def get_mirror_mode_get(self):
"""
获取镜像模式
:return: 返回数字,如:0
"""
try:
return self.rbd_inst.mirror_mode_get(self.ioctx)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get images mode failed.')
def _get_image(self, name):
"""
获取镜像对象
:param name: 镜像名称
:return:
"""
try:
return rbd.Image(self.ioctx, name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get image object failed.')
@staticmethod
def _close_image(image):
"""
关闭镜像对象
:param image: 镜像对象
:return:
"""
try:
if image:
image.close()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'close image object failed.')
def get_stat(self, name):
"""
获取镜像统计数据
:param name: 镜像名称
:return: {'parent_name': '',
'parent_pool': 18446744073709551615 L,
'num_objs': 1 L,
'block_name_prefix': 'rbd_data.69687e2ae8944a',
'obj_size': 4194304 L,
'order': 22,
'size': 20480 L
}
"""
image = None
try:
image = self._get_image(name)
stat = image.stat()
for k, v in stat.items():
stat[k] = v.encode() if type(v) == unicode else v
return stat
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get image stat failed.')
finally:
self._close_image(image)
def get_size(self, name):
"""
获取镜像大小
:param name: 镜像名称
:return:
"""
image = None
try:
image = self._get_image(name)
return image.size()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get image size failed.')
finally:
self._close_image(image)
def resize(self, name, size):
"""
扩容镜像
:param name: 镜像名称
:param size: 大小
:return:
"""
image = None
try:
image = self._get_image(name)
image.resize(size)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'resize image failed.')
finally:
self._close_image(image)
def copy(self, name, dest_ioctx, dest_name):
"""
拷贝镜像(将镜像拷贝,形成新的镜像),此举是浅拷贝,不会拷贝原镜像的快照
:param name: 镜像名称
:param dest_ioctx: 目标存储池上下文连接
:param dest_name: 拷贝后生成的镜像名称
:return:
"""
image = None
try:
image = self._get_image(name)
image.copy(dest_ioctx, dest_name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'copy image failed.')
finally:
self._close_image(image)
class SNAPOperation(object):
def __init__(self, ioctx, name):
"""
:param ioctx: 存储池上下文连接
:param name: 镜像名称
"""
if not ioctx:
raise ExecuteException(CEPH_RBD_ERROR, 'ioctx is none.')
if not name:
raise ExecuteException(CEPH_RBD_ERROR, 'image is none.')
self.ioctx = ioctx
self.name = name
def _get_image(self):
"""
获取镜像对象
:return:
"""
try:
return rbd.Image(self.ioctx, self.name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get image object failed.')
@staticmethod
def _close_image(image):
try:
if image:
image.close()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'close image object failed.')
def get_snaps(self):
"""
获取镜像快照列表
:return:
"""
image = None
try:
image = self._get_image()
# <rbd.SnapIterator object at 0x7fe7451ad650>
snapIterator = image.list_snaps()
# <generator object at 0x7fe7451d0d70>
snapGenerator = snapIterator.__iter__()
snaps = []
while True:
try:
snaps.append(next(snapGenerator))
except StopIteration:
break
except Exception as e:
cstor_logger.error(e.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get snap list error.')
for j in snaps:
for k, v in j.items():
j[k] = v.encode() if type(v) == unicode else v
return snaps
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get snaps failed.')
finally:
self._close_image(image)
def is_snap_exits(self, name):
"""
检查镜像快照是否存在
:param name: 快照名称
:return:
"""
try:
flag = False
if name in [i['name'].encode() for i in self.get_snaps()]:
flag = True
return flag
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'check snap exits failed.')
def create_snap(self, name):
"""
创建快照
:param name: 快照名称
:return:
"""
image = None
try:
if self.is_snap_exits(name):
raise ExecuteException(CEPH_RBD_ERROR, 'snap already exists.')
image = self._get_image()
image.create_snap(name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'create snap failed.')
finally:
self._close_image(image)
@staticmethod
def _get_rbd_inst():
"""
获取rbd对象
:return:
"""
try:
return rbd.RBD()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get rbd object failed.')
def clone(self, name, dest_ioctx, dest_name):
"""
克隆(此处是对快照(快照需要被保护)进行克隆,生成新的镜像)
:param name: 快照名称
:param dest_ioctx: 目标存储池上下文连接
:param dest_name: 克隆后生成的镜像名称
:return:
"""
try:
if not self.is_snap_exits(name):
raise ExecuteException(CEPH_RBD_ERROR, 'snap not exists.')
# 被克隆的快照需要被保护
if not self.is_snap_protected(name):
raise ExecuteException(CEPH_RBD_ERROR, 'snap should be protected.')
rbd_inst = self._get_rbd_inst()
rbd_inst.clone(self.ioctx, self.name, name, dest_ioctx, dest_name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'clone snap failed.')
def get_children_list(self, name):
"""
获取快照的儿子列表(即由快照克隆出来的镜像),注意,跨集群的无法显示
:param name: 快照名称
:return: [{'pool': 'xx', 'image': '1'}, {'pool': 'xx', 'image': '2'}]
"""
image = None
try:
children = []
image = self._get_image()
image.set_snap(name)
# [(u'bb', u'1')]
list_children = image.list_children()
for i in list_children:
children.append(
{'pool': i[0].encode() if type(i[0]) == unicode else i[0],
'image': i[1].encode() if type(i[1]) == unicode else i[1]})
return children
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get children failed.')
finally:
self._close_image(image)
def delete_snap(self, name):
"""
删除快照
:param name: 快照名称
:return:
"""
image = None
try:
# 被保护的快照无法删除,需要先取消保护才可以删除
if self.is_snap_protected(name):
raise ExecuteException(CEPH_RBD_ERROR, 'cannot unprotect: snap has at least one children.')
image = self._get_image()
image.remove_snap(name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'delete snap failed.')
finally:
self._close_image(image)
def is_snap_protected(self, name):
"""
查看快照是否被保护
:param name: 快照名称
:return: 布尔值
"""
image = None
try:
image = self._get_image()
return image.is_protected_snap(name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'check snap protected failed.')
finally:
self._close_image(image)
def protect_snap(self, name):
"""
保护快照
:param name: 快照名称
:return: 布尔值
"""
image = None
try:
if not self.is_snap_protected(name):
image = self._get_image()
image.protect_snap(name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'protect snap failed.')
finally:
self._close_image(image)
def unprotect_snap(self, name):
"""
取消快照保护
:param name: 快照名称
:return: 布尔值
"""
image = None
try:
# 若此快照克隆出了新镜像(儿子),此时无法取消取消保护,需先删除儿子才可以进行操作
if self.get_children_list(name):
raise ExecuteException(CEPH_RBD_ERROR, 'cannot unprotect: snap has at least one children.')
if self.is_snap_protected(name):
image = self._get_image()
image.unprotect_snap(name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'unprotect snap failed.')
finally:
self._close_image(image)
def rollback_snap(self, name):
"""
回滚快照
:param name: 快照名称
:return:
"""
image = None
try:
if not self.is_snap_exits(name):
raise ExecuteException(CEPH_RBD_ERROR, 'snap not exists.')
image = self._get_image()
image.rollback_to_snap(name)
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'rollback snap failed.')
finally:
self._close_image(image)
def get_stripe_count(self):
image = None
try:
image = self._get_image()
return image.stripe_count()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get snap stripe count failed.')
finally:
self._close_image(image)
def get_stripe_unit(self):
image = None
try:
image = self._get_image()
return image.stripe_unit()
except Exception as err:
cstor_logger.error(err.message)
raise ExecuteException(CEPH_RBD_ERROR, 'get snap stripe unit failed.')
finally:
self._close_image(image)