def handle_create_cache_tier(request, service): """Create a cache tier on a cold pool. Modes supported are "writeback" and "readonly". :param request: dict of request operations and params :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0 """ # mode = "writeback" | "readonly" storage_pool = request.get('cold-pool') cache_pool = request.get('hot-pool') cache_mode = request.get('mode') if cache_mode is None: cache_mode = "writeback" # cache and storage pool must exist first if not pool_exists(service=service, name=storage_pool) or not pool_exists( service=service, name=cache_pool): msg = ("cold-pool: {} and hot-pool: {} must exist. Please create " "them first".format(storage_pool, cache_pool)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} p = Pool(service=service, name=storage_pool) p.add_cache_tier(cache_pool=cache_pool, mode=cache_mode)
def make_cache_tier(): backer_pool = action_get("backer-pool") cache_pool = action_get("cache-pool") cache_mode = action_get("cache-mode") # Pre flight checks if not pool_exists('admin', backer_pool): log("Please create {} pool before calling create-cache-tier".format( backer_pool)) action_fail("create-cache-tier failed. Backer pool {} must exist " "before calling this".format(backer_pool)) if not pool_exists('admin', cache_pool): log("Please create {} pool before calling create-cache-tier".format( cache_pool)) action_fail("create-cache-tier failed. Cache pool {} must exist " "before calling this".format(cache_pool)) pool = Pool(service='admin', name=backer_pool) try: pool.add_cache_tier(cache_pool=cache_pool, mode=cache_mode) except CalledProcessError as err: log("Add cache tier failed with message: {}".format(err.message)) action_fail("create-cache-tier failed. Add cache tier failed with " "message: {}".format(err.message))
def make_cache_tier(): backer_pool = action_get("backer-pool") cache_pool = action_get("cache-pool") cache_mode = action_get("cache-mode") # Pre flight checks if not pool_exists('admin', backer_pool): log("Please create {} pool before calling create-cache-tier".format( backer_pool)) action_fail("create-cache-tier failed. Backer pool {} must exist " "before calling this".format(backer_pool)) if not pool_exists('admin', cache_pool): log("Please create {} pool before calling create-cache-tier".format( cache_pool)) action_fail("create-cache-tier failed. Cache pool {} must exist " "before calling this".format(cache_pool)) pool = Pool(service='admin', name=backer_pool) try: pool.add_cache_tier(cache_pool=cache_pool, mode=cache_mode) except CalledProcessError as err: log("Add cache tier failed with message: {}".format( err.message)) action_fail("create-cache-tier failed. Add cache tier failed with " "message: {}".format(err.message))
def delete_cache_tier(): backer_pool = action_get("backer-pool") cache_pool = action_get("cache-pool") # Pre flight checks if not pool_exists('admin', backer_pool): log("Backer pool {} must exist before calling this".format( backer_pool)) action_fail("remove-cache-tier failed. Backer pool {} must exist " "before calling this".format(backer_pool)) if not pool_exists('admin', cache_pool): log("Cache pool {} must exist before calling this".format( cache_pool)) action_fail("remove-cache-tier failed. Cache pool {} must exist " "before calling this".format(cache_pool)) pool = Pool(service='admin', name=backer_pool) try: pool.remove_cache_tier(cache_pool=cache_pool) except CalledProcessError as err: log("Removing the cache tier failed with message: {}".format( err.message)) action_fail("remove-cache-tier failed. Removing the cache tier failed " "with message: {}".format(err.message))
def handle_remove_cache_tier(request, service): storage_pool = request.get('cold-pool') cache_pool = request.get('hot-pool') # cache and storage pool must exist first if not pool_exists(service=service, name=storage_pool) or not pool_exists( service=service, name=cache_pool): msg = ("cold-pool: {} or hot-pool: {} doesn't exist. Not " "deleting cache tier".format(storage_pool, cache_pool)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} pool = Pool(name=storage_pool, service=service) pool.remove_cache_tier(cache_pool=cache_pool)
def handle_erasure_pool(request, service): pool_name = request.get('name') erasure_profile = request.get('erasure-profile') quota = request.get('max-bytes') if erasure_profile is None: erasure_profile = "default-canonical" # Check for missing params if pool_name is None: msg = "Missing parameter. name is required for the pool" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} # TODO: Default to 3/2 erasure coding. I believe this requires min 5 osds if not erasure_profile_exists(service=service, name=erasure_profile): # TODO: Fail and tell them to create the profile or default msg = ("erasure-profile {} does not exist. Please create it with: " "create-erasure-profile".format(erasure_profile)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} pool = ErasurePool(service=service, name=pool_name, erasure_code_profile=erasure_profile) # Ok make the erasure pool if not pool_exists(service=service, name=pool_name): log("Creating pool '%s' (erasure_profile=%s)" % (pool.name, erasure_profile), level=INFO) pool.create() # Set a quota if requested if quota is not None: set_pool_quota(service=service, pool_name=pool_name, max_bytes=quota)
def handle_replicated_pool(request, service): pool_name = request.get('name') replicas = request.get('replicas') quota = request.get('max-bytes') # Optional params pg_num = request.get('pg_num') if pg_num: # Cap pg_num to max allowed just in case. osds = get_osds(service) if osds: pg_num = min(pg_num, (len(osds) * 100 // replicas)) # Check for missing params if pool_name is None or replicas is None: msg = "Missing parameter. name and replicas are required" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} pool = ReplicatedPool(service=service, name=pool_name, replicas=replicas, pg_num=pg_num) if not pool_exists(service=service, name=pool_name): log("Creating pool '%s' (replicas=%s)" % (pool.name, replicas), level=INFO) pool.create() else: log("Pool '%s' already exists - skipping create" % pool.name, level=DEBUG) # Set a quota if requested if quota is not None: set_pool_quota(service=service, pool_name=pool_name, max_bytes=quota)
def handle_erasure_pool(request, service): """Create a new erasure coded pool. :param request: dict of request operations and params. :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0. """ pool_name = request.get('name') erasure_profile = request.get('erasure-profile') max_bytes = request.get('max-bytes') max_objects = request.get('max-objects') weight = request.get('weight') group_name = request.get('group') allow_ec_overwrites = request.get('allow-ec-overwrites') if erasure_profile is None: erasure_profile = "default-canonical" app_name = request.get('app-name') # Check for missing params if pool_name is None: msg = "Missing parameter. name is required for the pool" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if group_name: group_namespace = request.get('group-namespace') # Add the pool to the group named "group_name" add_pool_to_group(pool=pool_name, group=group_name, namespace=group_namespace) # TODO: Default to 3/2 erasure coding. I believe this requires min 5 osds if not erasure_profile_exists(service=service, name=erasure_profile): # TODO: Fail and tell them to create the profile or default msg = ("erasure-profile {} does not exist. Please create it with: " "create-erasure-profile".format(erasure_profile)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} pool = ErasurePool(service=service, name=pool_name, erasure_code_profile=erasure_profile, percent_data=weight, app_name=app_name, allow_ec_overwrites=allow_ec_overwrites) # Ok make the erasure pool if not pool_exists(service=service, name=pool_name): log("Creating pool '{}' (erasure_profile={})".format( pool.name, erasure_profile), level=INFO) pool.create() # Set a quota if requested if max_bytes or max_objects: set_pool_quota(service=service, pool_name=pool_name, max_bytes=max_bytes, max_objects=max_objects)
def handle_replicated_pool(request, service): """Create a new replicated pool. :param request: dict of request operations and params. :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0. """ pool_name = request.get('name') replicas = request.get('replicas') quota = request.get('max-bytes') weight = request.get('weight') group_name = request.get('group') # Optional params pg_num = request.get('pg_num') if pg_num: # Cap pg_num to max allowed just in case. osds = get_osds(service) if osds: pg_num = min(pg_num, (len(osds) * 100 // replicas)) app_name = request.get('app-name') # Check for missing params if pool_name is None or replicas is None: msg = "Missing parameter. name and replicas are required" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if group_name: group_namespace = request.get('group-namespace') # Add the pool to the group named "group_name" add_pool_to_group(pool=pool_name, group=group_name, namespace=group_namespace) kwargs = {} if pg_num: kwargs['pg_num'] = pg_num if weight: kwargs['percent_data'] = weight if replicas: kwargs['replicas'] = replicas if app_name: kwargs['app_name'] = app_name pool = ReplicatedPool(service=service, name=pool_name, **kwargs) if not pool_exists(service=service, name=pool_name): log("Creating pool '{}' (replicas={})".format(pool.name, replicas), level=INFO) pool.create() else: log("Pool '{}' already exists - skipping create".format(pool.name), level=DEBUG) # Set a quota if requested if quota is not None: set_pool_quota(service=service, pool_name=pool_name, max_bytes=quota)
def handle_create_cephfs(request, service): """Create a new cephfs. :param request: The broker request :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0 """ cephfs_name = request.get('mds_name') data_pool = request.get('data_pool') metadata_pool = request.get('metadata_pool') # Check if the user params were provided if not cephfs_name or not data_pool or not metadata_pool: msg = "Missing mds_name, data_pool or metadata_pool params" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} # Sanity check that the required pools exist if not pool_exists(service=service, name=data_pool): msg = "CephFS data pool does not exist. Cannot create CephFS" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if not pool_exists(service=service, name=metadata_pool): msg = "CephFS metadata pool does not exist. Cannot create CephFS" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if get_cephfs(service=service): # CephFS new has already been called log("CephFS already created") return # Finally create CephFS try: check_output(["ceph", '--id', service, "fs", "new", cephfs_name, metadata_pool, data_pool]) except CalledProcessError as err: if err.returncode == 22: log("CephFS already created") return else: log(err.output, level=ERROR) return {'exit-code': 1, 'stderr': err.output}
def handle_create_cephfs(request, service): """ Create a new cephfs. :param request: The broker request :param service: The cephx user to run this command under :return: """ cephfs_name = request.get('mds_name') data_pool = request.get('data_pool') metadata_pool = request.get('metadata_pool') # Check if the user params were provided if not cephfs_name or not data_pool or not metadata_pool: msg = "Missing mds_name, data_pool or metadata_pool params" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} # Sanity check that the required pools exist if not pool_exists(service=service, name=data_pool): msg = "CephFS data pool does not exist. Cannot create CephFS" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if not pool_exists(service=service, name=metadata_pool): msg = "CephFS metadata pool does not exist. Cannot create CephFS" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if get_cephfs(service=service): # CephFS new has already been called log("CephFS already created") return # Finally create CephFS try: check_output(["ceph", '--id', service, "fs", "new", cephfs_name, metadata_pool, data_pool]) except CalledProcessError as err: if err.returncode == 22: log("CephFS already created") return else: log(err.output, level=ERROR) return {'exit-code': 1, 'stderr': err.output}
def process_requests_v1(reqs): """Process v1 requests. Takes a list of requests (dicts) and processes each one. If an error is found, processing stops and the client is notified in the response. Returns a response dict containing the exit code (non-zero if any operation failed along with an explanation). """ log("Processing %s ceph broker requests" % (len(reqs)), level=INFO) for req in reqs: op = req.get('op') log("Processing op='%s'" % (op), level=DEBUG) # Use admin client since we do not have other client key locations # setup to use them for these operations. svc = 'admin' if op == "create-pool": params = {'pool': req.get('name'), 'replicas': req.get('replicas')} if not all(params.iteritems()): msg = ( "Missing parameter(s): %s" % (' '.join([k for k in params.iterkeys() if not params[k]]))) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} # Mandatory params pool = params['pool'] replicas = params['replicas'] # Optional params pg_num = req.get('pg_num') if pg_num: # Cap pg_num to max allowed just in case. osds = get_osds(svc) if osds: pg_num = min(pg_num, (len(osds) * 100 // replicas)) # Ensure string pg_num = str(pg_num) if not pool_exists(service=svc, name=pool): log("Creating pool '%s' (replicas=%s)" % (pool, replicas), level=INFO) create_pool(service=svc, name=pool, replicas=replicas, pg_num=pg_num) else: log("Pool '%s' already exists - skipping create" % (pool), level=DEBUG) else: msg = "Unknown operation '%s'" % (op) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} return {'exit-code': 0}
def handle_create_cache_tier(request, service): # mode = "writeback" | "readonly" storage_pool = request.get('cold-pool') cache_pool = request.get('hot-pool') cache_mode = request.get('mode') if cache_mode is None: cache_mode = "writeback" # cache and storage pool must exist first if not pool_exists(service=service, name=storage_pool) or not pool_exists( service=service, name=cache_pool): msg = "cold-pool: {} and hot-pool: {} must exist. Please create " \ "them first".format(storage_pool, cache_pool) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} p = Pool(service=service, name=storage_pool) p.add_cache_tier(cache_pool=cache_pool, mode=cache_mode)
def handle_remove_cache_tier(request, service): """Remove a cache tier from the cold pool. :param request: dict of request operations and params :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0 """ storage_pool = request.get('cold-pool') cache_pool = request.get('hot-pool') # cache and storage pool must exist first if not pool_exists(service=service, name=storage_pool) or not pool_exists( service=service, name=cache_pool): msg = ("cold-pool: {} or hot-pool: {} doesn't exist. Not " "deleting cache tier".format(storage_pool, cache_pool)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} pool = Pool(name=storage_pool, service=service) pool.remove_cache_tier(cache_pool=cache_pool)
def process_requests_v1(reqs): """Process v1 requests. Takes a list of requests (dicts) and processes each one. If an error is found, processing stops and the client is notified in the response. Returns a response dict containing the exit code (non-zero if any operation failed along with an explanation). """ log("Processing %s ceph broker requests" % (len(reqs)), level=INFO) for req in reqs: op = req.get('op') log("Processing op='%s'" % (op), level=DEBUG) # Use admin client since we do not have other client key locations # setup to use them for these operations. svc = 'admin' if op == "create-pool": params = {'pool': req.get('name'), 'replicas': req.get('replicas')} if not all(params.iteritems()): msg = ("Missing parameter(s): %s" % (' '.join([k for k in params.iterkeys() if not params[k]]))) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} # Mandatory params pool = params['pool'] replicas = params['replicas'] # Optional params pg_num = req.get('pg_num') if pg_num: # Cap pg_num to max allowed just in case. osds = get_osds(svc) if osds: pg_num = min(pg_num, (len(osds) * 100 // replicas)) # Ensure string pg_num = str(pg_num) if not pool_exists(service=svc, name=pool): log("Creating pool '%s' (replicas=%s)" % (pool, replicas), level=INFO) create_pool(service=svc, name=pool, replicas=replicas, pg_num=pg_num) else: log("Pool '%s' already exists - skipping create" % (pool), level=DEBUG) else: msg = "Unknown operation '%s'" % (op) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} return {'exit-code': 0}
def handle_erasure_pool(request, service): """Create a new erasure coded pool. :param request: dict of request operations and params. :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0. """ pool_name = request.get('name') erasure_profile = request.get('erasure-profile') quota = request.get('max-bytes') weight = request.get('weight') group_name = request.get('group') if erasure_profile is None: erasure_profile = "default-canonical" app_name = request.get('app-name') # Check for missing params if pool_name is None: msg = "Missing parameter. name is required for the pool" log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if group_name: group_namespace = request.get('group-namespace') # Add the pool to the group named "group_name" add_pool_to_group(pool=pool_name, group=group_name, namespace=group_namespace) # TODO: Default to 3/2 erasure coding. I believe this requires min 5 osds if not erasure_profile_exists(service=service, name=erasure_profile): # TODO: Fail and tell them to create the profile or default msg = ("erasure-profile {} does not exist. Please create it with: " "create-erasure-profile".format(erasure_profile)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} pool = ErasurePool(service=service, name=pool_name, erasure_code_profile=erasure_profile, percent_data=weight, app_name=app_name) # Ok make the erasure pool if not pool_exists(service=service, name=pool_name): log("Creating pool '{}' (erasure_profile={})" .format(pool.name, erasure_profile), level=INFO) pool.create() # Set a quota if requested if quota is not None: set_pool_quota(service=service, pool_name=pool_name, max_bytes=quota)
def delete_cache_tier(): backer_pool = action_get("backer-pool") cache_pool = action_get("cache-pool") # Pre flight checks if not pool_exists('admin', backer_pool): log("Backer pool {} must exist before calling this".format( backer_pool)) action_fail("remove-cache-tier failed. Backer pool {} must exist " "before calling this".format(backer_pool)) if not pool_exists('admin', cache_pool): log("Cache pool {} must exist before calling this".format(cache_pool)) action_fail("remove-cache-tier failed. Cache pool {} must exist " "before calling this".format(cache_pool)) pool = Pool(service='admin', name=backer_pool) try: pool.remove_cache_tier(cache_pool=cache_pool) except CalledProcessError as err: log("Removing the cache tier failed with message: {}".format(str(err))) action_fail("remove-cache-tier failed. Removing the cache tier failed " "with message: {}".format(str(err)))
def handle_replicated_pool(request, service): """Create a new replicated pool. :param request: dict of request operations and params. :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0. """ pool_name = request.get('name') group_name = request.get('group') # Optional params # NOTE: Check this against the handling in the Pool classes, reconcile and # remove. pg_num = request.get('pg_num') replicas = request.get('replicas') if pg_num: # Cap pg_num to max allowed just in case. osds = get_osds(service) if osds: pg_num = min(pg_num, (len(osds) * 100 // replicas)) request.update({'pg_num': pg_num}) if group_name: group_namespace = request.get('group-namespace') # Add the pool to the group named "group_name" add_pool_to_group(pool=pool_name, group=group_name, namespace=group_namespace) try: pool = ReplicatedPool(service=service, op=request) except KeyError: msg = "Missing parameter." log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} if not pool_exists(service=service, name=pool_name): log("Creating pool '{}' (replicas={})".format(pool.name, replicas), level=INFO) pool.create() else: log("Pool '{}' already exists - skipping create".format(pool.name), level=DEBUG) # Set/update properties that are allowed to change after pool creation. pool.update()
def handle_erasure_pool(request, service): """Create a new erasure coded pool. :param request: dict of request operations and params. :param service: The ceph client to run the command under. :returns: dict. exit-code and reason if not 0. """ pool_name = request.get('name') erasure_profile = request.get('erasure-profile') group_name = request.get('group') if erasure_profile is None: erasure_profile = "default-canonical" if group_name: group_namespace = request.get('group-namespace') # Add the pool to the group named "group_name" add_pool_to_group(pool=pool_name, group=group_name, namespace=group_namespace) # TODO: Default to 3/2 erasure coding. I believe this requires min 5 osds if not erasure_profile_exists(service=service, name=erasure_profile): # TODO: Fail and tell them to create the profile or default msg = ("erasure-profile {} does not exist. Please create it with: " "create-erasure-profile".format(erasure_profile)) log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} try: pool = ErasurePool(service=service, op=request) except KeyError: msg = "Missing parameter." log(msg, level=ERROR) return {'exit-code': 1, 'stderr': msg} # Ok make the erasure pool if not pool_exists(service=service, name=pool_name): log("Creating pool '{}' (erasure_profile={})" .format(pool.name, erasure_profile), level=INFO) pool.create() # Set/update properties that are allowed to change after pool creation. pool.update()
def test_pool_exists(self): '''It detects an rbd pool exists''' self.check_output.return_value = LS_POOLS self.assertTrue(ceph_utils.pool_exists('cinder', 'volumes'))
def test_pool_exists_error(self): ''' Ensure subprocess errors and sandboxed with False ''' self.check_output.side_effect = CalledProcessError(1, 'rados') self.assertFalse(ceph_utils.pool_exists('cinder', 'foo'))
def test_pool_does_not_exist(self): '''It detects an rbd pool exists''' self.check_output.return_value = LS_POOLS self.assertFalse(ceph_utils.pool_exists('cinder', 'foo'))