def _prep_resize(self): """Get information about the cluster's current state.""" if self.db_info.task_status != ClusterTasks.NONE: current_task = self.db_info.task_status.name log_fmt = ("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") exc_fmt = _("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") LOG.error(log_fmt, current_task) raise exception.UnprocessableEntity(exc_fmt % current_task) def _instances_of_type(instance_type): return [ db_inst for db_inst in self.db_instances if db_inst.type == instance_type ] self.config_svrs = _instances_of_type('config_server') self.query_routers = _instances_of_type('query_router') self.members = _instances_of_type('member') self.shard_ids = set([member.shard_id for member in self.members]) self.arbitrary_query_router = inst_models.load_any_instance( self.context, self.query_routers[0].id) self.manager = task_api.load(self.context, self.datastore_version.manager)
def add_shard(self): if self.db_info.task_status != ClusterTasks.NONE: current_task = self.db_info.task_status.name msg = _("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") % current_task LOG.error(msg) raise exception.UnprocessableEntity(msg) db_insts = inst_models.DBInstance.find_all(cluster_id=self.id, type='member').all() num_unique_shards = len(set([db_inst.shard_id for db_inst in db_insts])) if num_unique_shards == 0: msg = _("This action cannot be performed on the cluster as no " "reference shard exists.") LOG.error(msg) raise exception.UnprocessableEntity(msg) arbitrary_shard_id = db_insts[0].shard_id members_in_shard = [db_inst for db_inst in db_insts if db_inst.shard_id == arbitrary_shard_id] num_members_per_shard = len(members_in_shard) a_member = inst_models.load_any_instance(self.context, members_in_shard[0].id) deltas = {'instances': num_members_per_shard} volume_size = a_member.volume_size if volume_size: deltas['volumes'] = volume_size * num_members_per_shard check_quotas(self.context.tenant, deltas) new_replica_set_name = "rs" + str(num_unique_shards + 1) new_shard_id = utils.generate_uuid() dsv_manager = (datastore_models.DatastoreVersion. load_by_uuid(db_insts[0].datastore_version_id).manager) manager = task_api.load(self.context, dsv_manager) key = manager.get_key(a_member) member_config = {"id": self.id, "shard_id": new_shard_id, "instance_type": "member", "replica_set_name": new_replica_set_name, "key": key} for i in range(1, num_members_per_shard + 1): instance_name = "%s-%s-%s" % (self.name, new_replica_set_name, str(i)) inst_models.Instance.create(self.context, instance_name, a_member.flavor_id, a_member.datastore_version.image_id, [], [], a_member.datastore, a_member.datastore_version, volume_size, None, availability_zone=None, nics=None, configuration_id=None, cluster_config=member_config) self.update_db(task_status=ClusterTasks.ADDING_SHARD) manager.mongodb_add_shard_cluster( self.id, new_shard_id, new_replica_set_name)
def delete(self, req, tenant_id, id): """Delete a single instance.""" LOG.info( _LI("Deleting database instance '%(instance_id)s' for tenant " "'%(tenant_id)s'"), { 'instance_id': id, 'tenant_id': tenant_id }) LOG.debug("req : '%s'\n\n", req) context = req.environ[wsgi.CONTEXT_KEY] instance = models.load_any_instance(context, id) context.notification = notification.DBaaSInstanceDelete(context, request=req) with StartNotification(context, instance_id=instance.id): marker = 'foo' while marker: instance_modules, marker = module_models.InstanceModules.load( context, instance_id=id) for instance_module in instance_modules: instance_module = module_models.InstanceModule.load( context, instance_module['instance_id'], instance_module['module_id']) module_models.InstanceModule.delete( context, instance_module) instance.delete() return wsgi.Result(None, 202)
def reset_status(self): LOG.info("Resetting status to NONE on cluster %s", self.id) self.reset_task() instances = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() for inst in instances: instance = inst_models.load_any_instance(self.context, inst.id) instance.reset_status()
def instances(self): db_instances = instance_models.DBInstance.find_all( cluster_id=self.db_info.id, deleted=False) instances = [ instance_models.load_any_instance(self.context, db_inst.id) for db_inst in db_instances ] return instances
def reset_status(self): LOG.info(_("Resetting status to NONE on cluster %s") % self.id) self.reset_task() instances = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() for inst in instances: instance = inst_models.load_any_instance(self.context, inst.id) instance.reset_status()
def reset_status(self): self.validate_cluster_available([ClusterTasks.BUILDING_INITIAL]) LOG.info(_("Resetting status to NONE on cluster %s") % self.id) self.reset_task() instances = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() for inst in instances: instance = inst_models.load_any_instance(self.context, inst.id) instance.reset_status()
def _check_shard_status(self, member_id): member = inst_models.load_any_instance(self.context, member_id) guest = self.get_guest(member) rs_name = guest.get_replica_set_name() if self.get_guest( self.arbitrary_query_router).is_shard_active(rs_name): raise exception.TroveError( _('Shard with instance %s is still active. Please remove the ' 'shard from the MongoDB cluster before shrinking.') % member_id)
def _check_shard_status(self, member_id): member = inst_models.load_any_instance(self.context, member_id) guest = self.get_guest(member) rs_name = guest.get_replica_set_name() if self.get_guest( self.arbitrary_query_router).is_shard_active(rs_name): raise exception.TroveError( _('Shard with instance %s is still active. Please remove the ' 'shard from the MongoDB cluster before shrinking.') % member_id )
def delete(self, req, tenant_id, id): """Delete a single instance.""" LOG.info(_("req : '%s'\n\n") % req) LOG.info(_("Deleting a database instance for tenant '%s'") % tenant_id) LOG.info(_("id : '%s'\n\n") % id) # TODO(hub-cap): turn this into middleware context = req.environ[wsgi.CONTEXT_KEY] instance = models.load_any_instance(context, id) instance.delete() # TODO(cp16net): need to set the return code correctly return wsgi.Result(None, 202)
def delete(self, req, tenant_id, id): """Delete a single instance.""" LOG.info(_LI("Deleting database instance '%(instance_id)s' for tenant " "'%(tenant_id)s'"), {'instance_id': id, 'tenant_id': tenant_id}) LOG.debug("req : '%s'\n\n", req) # TODO(hub-cap): turn this into middleware context = req.environ[wsgi.CONTEXT_KEY] instance = models.load_any_instance(context, id) instance.delete() # TODO(cp16net): need to set the return code correctly return wsgi.Result(None, 202)
def delete(self): self.validate_cluster_available([ClusterTasks.NONE, ClusterTasks.DELETING]) db_insts = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() self.update_db(task_status=ClusterTasks.DELETING) for db_inst in db_insts: instance = inst_models.load_any_instance(self.context, db_inst.id) instance.delete() task_api.API(self.context).delete_cluster(self.id)
def delete(self): self.validate_cluster_available( [ClusterTasks.NONE, ClusterTasks.DELETING]) db_insts = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() self.update_db(task_status=ClusterTasks.DELETING) for db_inst in db_insts: instance = inst_models.load_any_instance(self.context, db_inst.id) instance.delete() task_api.API(self.context).delete_cluster(self.id)
def delete(self, req, tenant_id, id): """Delete a single instance.""" LOG.info(_("req : '%s'\n\n") % req) LOG.info(_("Deleting a database instance for tenant '%s'") % tenant_id) LOG.info(_("id : '%s'\n\n") % id) # TODO(hub-cap): turn this into middleware context = req.environ[wsgi.CONTEXT_KEY] instance = models.load_any_instance(context, id) if instance.db_info.task_status in [InstanceTasks.DELETING, InstanceTasks.DELETEING_SECURITY_GROUP]: LOG.debug("instance %s task_status %s , delete is already running." % (id, instance.db_info.task_status)) return instance.delete() # TODO(cp16net): need to set the return code correctly return wsgi.Result(None, 202)
def _gen_replica_set_name(self): """Check the replica set names of all shards in the cluster to determine the next available name. Names are in the form 'rsX' where X is an integer. """ used_names = [] for shard_id in self.shard_ids: # query the guest for the replica name on one member of each shard members = [mem for mem in self.members if mem.shard_id == shard_id] member = inst_models.load_any_instance(self.context, members[0].id) used_names.append(self.get_guest(member).get_replica_set_name()) # find the first unused name i = 0 while True: i += 1 name = 'rs%s' % i if name not in used_names: return name
def _gen_replica_set_name(self): """Check the replica set names of all shards in the cluster to determine the next available name. Names are in the form 'rsX' where X is an integer. """ used_names = [] for shard_id in self.shard_ids: # query the guest for the replica name on one member of each shard members = [mem for mem in self.members if mem.shard_id == shard_id] member = inst_models.load_any_instance(self.context, members[0].id) used_names.append(self.get_guest(member).get_replica_set_name()) # find the first unused name i = 0 while True: i += 1 name = "rs%s" % i if name not in used_names: return name
def delete(self): self.validate_cluster_available([ClusterTasks.NONE, ClusterTasks.DELETING]) db_insts = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() self.update_db(task_status=ClusterTasks.DELETING) # we force the server-group delete here since we need to load the # group while the instances still exist. Also, since the instances # take a while to be removed they might not all be gone even if we # do it after the delete. srv_grp.ServerGroup.delete(self.context, self.server_group, force=True) for db_inst in db_insts: instance = inst_models.load_any_instance(self.context, db_inst.id) instance.delete() task_api.API(self.context).delete_cluster(self.id)
def _prep_resize(self): """Get information about the cluster's current state.""" if self.db_info.task_status != ClusterTasks.NONE: current_task = self.db_info.task_status.name msg = ( _("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") % current_task ) LOG.error(msg) raise exception.UnprocessableEntity(msg) def _instances_of_type(instance_type): return [db_inst for db_inst in self.db_instances if db_inst.type == instance_type] self.config_svrs = _instances_of_type("config_server") self.query_routers = _instances_of_type("query_router") self.members = _instances_of_type("member") self.shard_ids = set([member.shard_id for member in self.members]) self.arbitrary_query_router = inst_models.load_any_instance(self.context, self.query_routers[0].id) self.manager = task_api.load(self.context, self.datastore_version.manager)
def delete(self): if self.db_info.task_status not in (ClusterTasks.NONE, ClusterTasks.DELETING): current_task = self.db_info.task_status.name msg = _("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") % current_task LOG.error(msg) raise exception.UnprocessableEntity(msg) db_insts = inst_models.DBInstance.find_all(cluster_id=self.id, deleted=False).all() self.update_db(task_status=ClusterTasks.DELETING) for db_inst in db_insts: instance = inst_models.load_any_instance(self.context, db_inst.id) instance.delete() task_api.API(self.context).delete_cluster(self.id)
def test_delete(self): dbinst = models.DBInstance(InstanceTasks.NONE,name = 'name', created ='created', compute_instance_id = 'compute_instance_id', task_id = 'task_id', task_description = 'task_description', task_start_time = 'task_start_time', volume_id = 'volume_id', deleted = 'deleted', tenant_id = 'tenant_id', service_type = 'service_type') server = fake() service_status = fake() service_status.status = ServiceStatuses.RUNNING inst = models.BaseInstance(self.context,dbinst,server,service_status) when(models).load_instance(any(), any(), any(), needs_server=any()).thenReturn(inst) group_item = fake() group_item.type = DBInstanceType.MASTER group_item.group_id = "group_id" when(InstanceGroupItem).get_by_instance_id(any(),any()).thenReturn(group_item) when(Backup).running(any()).thenReturn(False) when(models.BaseInstance).update_db(task_status=any()).thenReturn(None) when(task_api.API).delete_instance(any()).thenReturn(None) standby = fake() standby.instance_id = "instance_id" when(InstanceGroupItem).get_by_gid_type(any(), any(),any()).thenReturn(standby) standby_dbinfo = fake() def save(): pass standby_dbinfo.save = save when(models.DBInstance).find_by(id=any(), deleted=any()).thenReturn(standby_dbinfo) instance = models.load_any_instance(self.context, "id") instance.delete() verify(InstanceGroupItem).get_by_gid_type(any(), any(),any()) verify(models.DBInstance,times=1).find_by(id=any(), deleted=any())
def delete(self, req, tenant_id, id): """Delete a single instance.""" LOG.info(_LI("Deleting database instance '%(instance_id)s' for tenant " "'%(tenant_id)s'"), {'instance_id': id, 'tenant_id': tenant_id}) LOG.debug("req : '%s'\n\n", req) context = req.environ[wsgi.CONTEXT_KEY] instance = models.load_any_instance(context, id) context.notification = notification.DBaaSInstanceDelete( context, request=req) with StartNotification(context, instance_id=instance.id): marker = 'foo' while marker: instance_modules, marker = module_models.InstanceModules.load( context, instance_id=id) for instance_module in instance_modules: instance_module = module_models.InstanceModule.load( context, instance_module['instance_id'], instance_module['module_id']) module_models.InstanceModule.delete( context, instance_module) instance.delete() return wsgi.Result(None, 202)
def instances(self): db_instances = instance_models.DBInstance.find_all( cluster_id=self.db_info.id, deleted=False) instances = [instance_models.load_any_instance( self.context, db_inst.id) for db_inst in db_instances] return instances
def shrink(self, instance_ids): """Removes instances from a cluster. Currently only supports removing entire replica sets from the cluster. """ if not len(instance_ids) > 0: raise exception.TroveError(_("Not instances specified for grow operation.")) self._prep_resize() all_member_ids = set([member.id for member in self.members]) all_query_router_ids = set([query_router.id for query_router in self.query_routers]) target_ids = set(instance_ids) target_member_ids = target_ids.intersection(all_member_ids) target_query_router_ids = target_ids.intersection(all_query_router_ids) other_ids = target_ids.difference(target_member_ids.union(target_query_router_ids)) if other_ids: raise exception.TroveError( _( "Instances %s cannot be deleted. MongoDB cluster shink only " "supports removing replicas and query routers." ) % list(other_ids) ) remaining_query_router_ids = all_query_router_ids.difference(target_query_router_ids) if len(remaining_query_router_ids) < 1: raise exception.TroveError( _( "Cannot delete all remaining query routers. At least one " "query router must be available in the cluster." ) ) if target_member_ids: target_members = [member for member in self.members if member.id in target_member_ids] target_shards = {} for member in target_members: if member.shard_id in target_shards: target_shards[member.shard_id].append(member.id) else: target_shards[member.shard_id] = [member.id] for target_shard_id in target_shards.keys(): # check the whole shard is being deleted target_shard_member_ids = [member.id for member in target_members if member.shard_id == target_shard_id] all_shard_member_ids = [member.id for member in self.members if member.shard_id == target_shard_id] if set(target_shard_member_ids) != set(all_shard_member_ids): raise exception.TroveError( _( "MongoDB cluster shrink only supports removing an " "entire shard. Shard %(shard)s has members: " "%(instances)s" ) % {"shard": target_shard_id, "instances": all_shard_member_ids} ) self._check_shard_status(target_shard_member_ids[0]) # all checks are done by now self.update_db(task_status=ClusterTasks.SHRINKING_CLUSTER) for instance_id in instance_ids: instance = inst_models.load_any_instance(self.context, instance_id) instance.delete() self.manager.shrink_cluster(self.id, instance_ids)
def shrink(self, instance_ids): """Removes instances from a cluster. Currently only supports removing entire replica sets from the cluster. """ if not len(instance_ids) > 0: raise exception.TroveError( _('Not instances specified for grow operation.') ) self._prep_resize() all_member_ids = set([member.id for member in self.members]) all_query_router_ids = set([query_router.id for query_router in self.query_routers]) target_ids = set(instance_ids) target_member_ids = target_ids.intersection(all_member_ids) target_query_router_ids = target_ids.intersection(all_query_router_ids) target_configsvr_ids = target_ids.difference( target_member_ids.union(target_query_router_ids) ) if target_configsvr_ids: raise exception.ClusterShrinkInstanceInUse( id=list(target_configsvr_ids), reason="Cannot remove config servers." ) remaining_query_router_ids = all_query_router_ids.difference( target_query_router_ids ) if len(remaining_query_router_ids) < 1: raise exception.ClusterShrinkInstanceInUse( id=list(target_query_router_ids), reason="Cannot remove all remaining query routers. At least " "one query router must be available in the cluster." ) if target_member_ids: target_members = [member for member in self.members if member.id in target_member_ids] target_shards = {} for member in target_members: if member.shard_id in target_shards: target_shards[member.shard_id].append(member.id) else: target_shards[member.shard_id] = [member.id] for target_shard_id in target_shards.keys(): # check the whole shard is being deleted target_shard_member_ids = [ member.id for member in target_members if member.shard_id == target_shard_id ] all_shard_member_ids = [ member.id for member in self.members if member.shard_id == target_shard_id ] if set(target_shard_member_ids) != set(all_shard_member_ids): raise exception.TroveError( _('MongoDB cluster shrink only supports removing an ' 'entire shard. Shard %(shard)s has members: ' '%(instances)s') % {'shard': target_shard_id, 'instances': all_shard_member_ids} ) self._check_shard_status(target_shard_member_ids[0]) # all checks are done by now self.update_db(task_status=ClusterTasks.SHRINKING_CLUSTER) for instance_id in instance_ids: instance = inst_models.load_any_instance(self.context, instance_id) instance.delete() self.manager.shrink_cluster(self.id, instance_ids)
def shrink(self, instance_ids): """Removes instances from a cluster. Currently only supports removing entire replica sets from the cluster. """ if not len(instance_ids) > 0: raise exception.TroveError( _('No instances specified for shrink operation.')) self._prep_resize() all_member_ids = set([member.id for member in self.members]) all_query_router_ids = set( [query_router.id for query_router in self.query_routers]) target_ids = set(instance_ids) target_member_ids = target_ids.intersection(all_member_ids) target_query_router_ids = target_ids.intersection(all_query_router_ids) target_configsvr_ids = target_ids.difference( target_member_ids.union(target_query_router_ids)) if target_configsvr_ids: raise exception.ClusterShrinkInstanceInUse( id=list(target_configsvr_ids), reason="Cannot remove config servers.") remaining_query_router_ids = all_query_router_ids.difference( target_query_router_ids) if len(remaining_query_router_ids) < 1: raise exception.ClusterShrinkInstanceInUse( id=list(target_query_router_ids), reason="Cannot remove all remaining query routers. At least " "one query router must be available in the cluster.") if target_member_ids: target_members = [ member for member in self.members if member.id in target_member_ids ] target_shards = {} for member in target_members: if member.shard_id in target_shards: target_shards[member.shard_id].append(member.id) else: target_shards[member.shard_id] = [member.id] for target_shard_id in target_shards.keys(): # check the whole shard is being deleted target_shard_member_ids = [ member.id for member in target_members if member.shard_id == target_shard_id ] all_shard_member_ids = [ member.id for member in self.members if member.shard_id == target_shard_id ] if set(target_shard_member_ids) != set(all_shard_member_ids): raise exception.TroveError( _('MongoDB cluster shrink only supports removing an ' 'entire shard. Shard %(shard)s has members: ' '%(instances)s') % { 'shard': target_shard_id, 'instances': all_shard_member_ids }) self._check_shard_status(target_shard_member_ids[0]) # all checks are done by now self.update_db(task_status=ClusterTasks.SHRINKING_CLUSTER) for instance_id in instance_ids: instance = inst_models.load_any_instance(self.context, instance_id) instance.delete() self.manager.shrink_cluster(self.id, instance_ids)