Beispiel #1
0
    def _service_is_active(self):
        """
        Check that the database guest is active.

        This function is meant to be called with poll_until to check that
        the guest is alive before sending a 'create' message. This prevents
        over billing a customer for a instance that they can never use.

        Returns: boolean if the service is active.
        Raises: TroveError if the service is in a failure state.
        """
        service = InstanceServiceStatus.find_by(instance_id=self.id)
        status = service.get_status()
        if status == rd_instance.ServiceStatuses.RUNNING:
            return True
        elif status not in [
                rd_instance.ServiceStatuses.NEW,
                rd_instance.ServiceStatuses.BUILDING
        ]:
            raise TroveError(_("Service not active, status: %s") % status)

        c_id = self.db_info.compute_instance_id
        nova_status = self.nova_client.servers.get(c_id).status
        if nova_status in [InstanceStatus.ERROR, InstanceStatus.FAILED]:
            raise TroveError(_("Server not active, status: %s") % nova_status)
        return False
Beispiel #2
0
 def validate(self, required_traits):
     required_keys = set(required_traits)
     provided_keys = set(self.payload.keys())
     if not required_keys.issubset(provided_keys):
         raise TroveError(_("The following required keys not defined for"
                            " notification %(name)s: %(keys)s")
                          % {'name': self.__class__.__name__,
                             'keys': list(required_keys - provided_keys)})
     if 'server_type' not in self.payload:
         raise TroveError(_("Notification %s must include a"
                          " 'server_type' for correct routing")
                          % self.__class__.__name__)
Beispiel #3
0
 def _most_current_replica(self, old_master, replica_models):
     last_txns = self._get_replica_txns(replica_models)
     master_ids = [txn[1] for txn in last_txns if txn[1]]
     if len(set(master_ids)) > 1:
         raise TroveError(_("Replicas of %s not all replicating"
                            " from same master") % old_master.id)
     return sorted(last_txns, key=lambda x: x[2], reverse=True)[0][0]
Beispiel #4
0
        def _grow_cluster():

            db_instances = DBInstance.find_all(cluster_id=cluster_id).all()
            cluster_head = next(
                Instance.load(context, db_inst.id) for db_inst in db_instances
                if db_inst.id not in new_instance_ids)
            if not cluster_head:
                raise TroveError(
                    _("Unable to determine existing Redis cluster"
                      " member"))

            (cluster_head_ip,
             cluster_head_port) = (self.get_guest(cluster_head).get_node_ip())

            # Wait for cluster members to get to cluster-ready status.
            if not self._all_instances_ready(new_instance_ids, cluster_id):
                return

            LOG.debug("All members ready, proceeding for cluster setup.")
            new_insts = [
                Instance.load(context, instance_id)
                for instance_id in new_instance_ids
            ]
            new_guests = map(self.get_guest, new_insts)

            # Connect nodes to the cluster head
            for guest in new_guests:
                guest.cluster_meet(cluster_head_ip, cluster_head_port)

            for guest in new_guests:
                guest.cluster_complete()
Beispiel #5
0
    def shrink(self, removal_ids):
        LOG.debug("Shrinking cluster %s.", self.id)

        self.validate_cluster_available()

        cluster_info = self.db_info
        cluster_info.update(task_status=ClusterTasks.SHRINKING_CLUSTER)
        try:
            removal_insts = [
                inst_models.Instance.load(self.context, inst_id)
                for inst_id in removal_ids
            ]
            node_ids = [
                Cluster.get_guest(instance).get_node_id_for_removal()
                for instance in removal_insts
            ]
            if None in node_ids:
                raise TroveError(
                    _("Some nodes cannot be removed (check slots)"))

            all_instances = (inst_models.DBInstance.find_all(
                cluster_id=self.id, deleted=False).all())
            remain_insts = [
                inst_models.Instance.load(self.context, inst.id)
                for inst in all_instances if inst.id not in removal_ids
            ]
            map(lambda x: Cluster.get_guest(x).remove_nodes(node_ids),
                remain_insts)
            map(lambda x: x.update_db(cluster_id=None), removal_insts)
            map(inst_models.Instance.delete, removal_insts)

            return RedisCluster(self.context, cluster_info, self.ds,
                                self.ds_version)
        finally:
            cluster_info.update(task_status=ClusterTasks.NONE)
Beispiel #6
0
 def ip_is_available(server):
     LOG.info("Polling for ip addresses: $%s " % server.addresses)
     if server.addresses != {}:
         return True
     elif (server.addresses == {}
           and server.status != InstanceStatus.ERROR):
         return False
     elif (server.addresses == {}
           and server.status == InstanceStatus.ERROR):
         msg = _("Instance IP not available, instance (%s): "
                 "server had status (%s).")
         LOG.error(msg % (self.id, server.status))
         raise TroveError(status=server.status)
Beispiel #7
0
 def ip_is_available(server):
     LOG.info("Polling for ip addresses: $%s " % server.addresses)
     if server.addresses != {}:
         return True
     elif (server.addresses == {}
           and server.status != InstanceStatus.ERROR):
         return False
     elif (server.addresses == {}
           and server.status == InstanceStatus.ERROR):
         LOG.error(
             _("Instance IP not available, "
               "instance (%(instance)s): "
               "server had status (%(status)s).") % {
                   'instance': self.id,
                   'status': server.status
               })
         raise TroveError(status=server.status)
Beispiel #8
0
    def _create_server_volume_heat(self, flavor, image_id, datastore_manager,
                                   volume_size, availability_zone):
        LOG.debug(_("begin _create_server_volume_heat for id: %s") % self.id)
        client = create_heat_client(self.context)
        novaclient = create_nova_client(self.context)
        cinderclient = create_cinder_client(self.context)

        template_obj = template.load_heat_template(datastore_manager)
        heat_template_unicode = template_obj.render()
        try:
            heat_template = heat_template_unicode.encode('ascii')
        except UnicodeEncodeError:
            LOG.error(_("heat template ascii encode issue"))
            raise TroveError("heat template ascii encode issue")

        parameters = {
            "Flavor": flavor["name"],
            "VolumeSize": volume_size,
            "InstanceId": self.id,
            "ImageId": image_id,
            "DatastoreManager": datastore_manager,
            "AvailabilityZone": availability_zone
        }
        stack_name = 'trove-%s' % self.id
        client.stacks.create(stack_name=stack_name,
                             template=heat_template,
                             parameters=parameters)
        stack = client.stacks.get(stack_name)

        utils.poll_until(lambda: client.stacks.get(stack_name),
                         lambda stack: stack.stack_status in
                         ['CREATE_COMPLETE', 'CREATE_FAILED'],
                         sleep_time=2,
                         time_out=HEAT_TIME_OUT)

        resource = client.resources.get(stack.id, 'BaseInstance')
        server = novaclient.servers.get(resource.physical_resource_id)

        resource = client.resources.get(stack.id, 'DataVolume')
        volume = cinderclient.volumes.get(resource.physical_resource_id)
        volume_info = self._build_volume(volume)

        self.update_db(compute_instance_id=server.id, volume_id=volume.id)
        LOG.debug(_("end _create_server_volume_heat for id: %s") % self.id)
        return server, volume_info
Beispiel #9
0
    def __init__(self, context, **kwargs):
        self.context = context
        self.needs_end_notification = True

        self.payload = {}

        if 'request' in kwargs:
            request = kwargs.pop('request')
            self.payload.update({
                                'request_id': context.request_id,
                                'server_type': 'api',
                                'client_ip': request.remote_addr,
                                'server_ip': request.host,
                                'tenant_id': context.tenant,
                                })
        elif 'request_id' not in kwargs:
            raise TroveError(_("Notification %s must include 'request'"
                             " property") % self.__class__.__name__)

        self.payload.update(kwargs)
Beispiel #10
0
 def delete_backup(cls, context, backup_id):
     #delete backup from swift
     backup = trove.backup.models.Backup.get_by_id(context, backup_id)
     try:
         filename = backup.filename
         if filename:
             BackupTasks.delete_files_from_swift(context, filename)
     except ValueError:
         backup.delete()
     except ClientException as e:
         if e.http_status == 404:
             # Backup already deleted in swift
             backup.delete()
         else:
             LOG.exception("Exception deleting from swift. Details: %s" % e)
             backup.state = trove.backup.models.BackupState.DELETE_FAILED
             backup.save()
             raise TroveError("Failed to delete swift objects")
     else:
         backup.delete()
Beispiel #11
0
    def _compute_mem_allocations_mb(self, ramsize_quota_mb, enabled_services):
        """Couchbase 4.x and higher split the available memory quota between
        data and index services.
        If the indexing service is turned on the quota value must be at least
        256MB.

        Compute the index quota as 25% of the total and use the rest for data
        services. Return '0' quota if the service is not enabled.
        """
        if 'index' in enabled_services:
            index_quota_mb = max(int(self.INDEX_MEM_RATIO * ramsize_quota_mb),
                                 Couchbase4App.MIN_RAMSIZE_QUOTA_MB)
        else:
            index_quota_mb = 0

        data_quota_mb = ramsize_quota_mb - index_quota_mb

        if data_quota_mb < Couchbase4App.MIN_RAMSIZE_QUOTA_MB:
            required = Couchbase4App.MIN_RAMSIZE_QUOTA_MB - data_quota_mb
            raise TroveError(
                _("Not enough memory for Couchbase services. "
                  "Additional %dMB is required.") % required)

        return data_quota_mb, index_quota_mb
Beispiel #12
0
 def _assert_nova_action_was_successful(self):
     # Do check to make sure the status and flavor id are correct.
     if str(self.instance.server.flavor['id']) != str(self.new_flavor_id):
         msg = "Assertion failed! flavor_id=%s and not %s" \
               % (self.instance.server.flavor['id'], self.new_flavor_id)
         raise TroveError(msg)
Beispiel #13
0
 def _assert_nova_status_is_ok(self):
     # Make sure Nova thinks things went well.
     if self.instance.server.status != "VERIFY_RESIZE":
         msg = "Migration failed! status=%s and not %s" \
               % (self.instance.server.status, 'VERIFY_RESIZE')
         raise TroveError(msg)
Beispiel #14
0
 def _log_and_raise(self, exc, message, task_status):
     LOG.error(message)
     LOG.error(exc)
     LOG.error(traceback.format_exc())
     self.update_db(task_status=task_status)
     raise TroveError(message=message)
Beispiel #15
0
        def _create_cluster():
            # Fetch instances by cluster_id against instances table.
            db_instances = DBInstance.find_all(cluster_id=cluster_id).all()
            instance_ids = [db_instance.id for db_instance in db_instances]

            LOG.debug("Waiting for instances to get to cluster-ready status.")
            # Wait for cluster members to get to cluster-ready status.
            if not self._all_instances_ready(instance_ids, cluster_id):
                raise TroveError(
                    _("Instances in cluster did not report "
                      "ACTIVE"))

            LOG.debug("All members ready, proceeding for cluster setup.")
            instances = [
                Instance.load(context, instance_id)
                for instance_id in instance_ids
            ]

            cluster_ips = [self.get_ip(instance) for instance in instances]
            instance_guests = [
                self.get_guest(instance) for instance in instances
            ]

            # Create replication user and password for synchronizing the
            # galera cluster
            replication_user = {
                "name": self.CLUSTER_REPLICATION_USER,
                "password": utils.generate_random_password(),
            }

            # Galera cluster name must be unique and be shorter than a full
            # uuid string so we remove the hyphens and chop it off. It was
            # recommended to be 16 chars or less.
            # (this is not currently documented on Galera docs)
            cluster_name = utils.generate_uuid().replace("-", "")[:16]

            LOG.debug("Configuring cluster configuration.")
            try:
                # Set the admin password for all the instances because the
                # password in the my.cnf will be wrong after the joiner
                # instances syncs with the donor instance.
                admin_password = str(utils.generate_random_password())
                for guest in instance_guests:
                    guest.reset_admin_password(admin_password)

                bootstrap = True
                for instance in instances:
                    guest = self.get_guest(instance)

                    # render the conf.d/cluster.cnf configuration
                    cluster_configuration = self._render_cluster_config(
                        context, instance, ",".join(cluster_ips), cluster_name,
                        replication_user)

                    # push the cluster config and bootstrap the first instance
                    guest.install_cluster(replication_user,
                                          cluster_configuration, bootstrap)
                    bootstrap = False

                LOG.debug("Finalizing cluster configuration.")
                for guest in instance_guests:
                    guest.cluster_complete()
            except Exception:
                LOG.exception(_("Error creating cluster."))
                self.update_statuses_on_failure(cluster_id)
Beispiel #16
0
        def _grow_cluster():

            db_instances = DBInstance.find_all(cluster_id=cluster_id,
                                               deleted=False).all()
            existing_instances = [
                Instance.load(context, db_inst.id) for db_inst in db_instances
                if db_inst.id not in new_instance_ids
            ]
            if not existing_instances:
                raise TroveError(
                    _("Unable to determine existing cluster "
                      "member(s)"))

            # get list of ips of existing cluster members
            existing_cluster_ips = [
                self.get_ip(instance) for instance in existing_instances
            ]
            existing_instance_guests = [
                self.get_guest(instance) for instance in existing_instances
            ]

            # get the cluster context to setup new members
            cluster_context = existing_instance_guests[0].get_cluster_context()

            # Wait for cluster members to get to cluster-ready status.
            if not self._all_instances_ready(new_instance_ids, cluster_id):
                raise TroveError(
                    _("Instances in cluster did not report "
                      "ACTIVE"))

            LOG.debug("All members ready, proceeding for cluster setup.")

            # Get the new instances to join the cluster
            new_instances = [
                Instance.load(context, instance_id)
                for instance_id in new_instance_ids
            ]
            new_cluster_ips = [
                self.get_ip(instance) for instance in new_instances
            ]
            for instance in new_instances:
                guest = self.get_guest(instance)

                guest.reset_admin_password(cluster_context['admin_password'])

                # render the conf.d/cluster.cnf configuration
                cluster_configuration = self._render_cluster_config(
                    context, instance, ",".join(existing_cluster_ips),
                    cluster_context['cluster_name'],
                    cluster_context['replication_user'])

                # push the cluster config and bootstrap the first instance
                bootstrap = False
                guest.install_cluster(cluster_context['replication_user'],
                                      cluster_configuration, bootstrap)

            self._check_cluster_for_root(context, existing_instances,
                                         new_instances)

            # apply the new config to all instances
            for instance in existing_instances + new_instances:
                guest = self.get_guest(instance)
                # render the conf.d/cluster.cnf configuration
                cluster_configuration = self._render_cluster_config(
                    context, instance,
                    ",".join(existing_cluster_ips + new_cluster_ips),
                    cluster_context['cluster_name'],
                    cluster_context['replication_user'])
                guest.write_cluster_configuration_overrides(
                    cluster_configuration)

            for instance in new_instances:
                guest = self.get_guest(instance)
                guest.cluster_complete()
Beispiel #17
0
 def test_invalid_error_message_format(self):
     error = TroveError("test%999999sdb")
     self.assertEqual(error.message, "test999999sdb")
Beispiel #18
0
 def test_valid_error_message_format(self):
     error = TroveError("%02d" % 1)
     self.assertEqual(error.message, "01")