Esempio n. 1
0
File: api.py Progetto: liuchin/trove
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
File: api.py Progetto: fabian4/trove
    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)
Esempio n. 4
0
 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)
Esempio n. 5
0
 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()
Esempio n. 6
0
 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
Esempio n. 7
0
 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()
Esempio n. 8
0
 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()
Esempio n. 9
0
 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)
Esempio n. 10
0
 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
         )
Esempio n. 11
0
 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)
Esempio n. 12
0
 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)
Esempio n. 13
0
 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)
Esempio n. 14
0
    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)
Esempio n. 15
0
    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)
Esempio n. 16
0
 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)
Esempio n. 17
0
 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
Esempio n. 18
0
 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
Esempio n. 19
0
    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)
Esempio n. 20
0
    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)
Esempio n. 21
0
    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)
Esempio n. 22
0
    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)
Esempio n. 23
0
    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 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())
Esempio n. 25
0
 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)
Esempio n. 26
0
 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
Esempio n. 27
0
    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)
Esempio n. 28
0
    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)
Esempio n. 29
0
    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)