Example #1
0
    def rolling_configuration_remove(self, apply_on_all=True):
        cluster_notification = self.context.notification
        request_info = cluster_notification.serialize(self.context)
        self.validate_cluster_available()
        self.db_info.update(task_status=ClusterTasks.UPDATING_CLUSTER)
        try:
            instances = [
                inst_models.Instance.load(self.context, instance.id)
                for instance in self.instances
            ]

            LOG.debug("Removing changes from cluster nodes.")
            for instance in instances:
                if instance.configuration:
                    self.context.notification = (
                        DBaaSInstanceDetachConfiguration(
                            self.context, **request_info))
                    with StartNotification(self.context,
                                           instance_id=instance.id):
                        with EndNotification(self.context):
                            instance.delete_configuration()
                else:
                    LOG.debug("Node '%s' has no configuration attached.",
                              instance.id)

            # The cluster is in a consistent state with all nodes
            # requiring restart.
            # New configuration can be safely attached at this point.
            configuration_id = self.configuration_id
            self.update_db(configuration_id=None)

            LOG.debug("Applying runtime configuration changes.")
            if instances[0].reset_configuration(configuration_id):
                LOG.debug(
                    "Runtime changes have been applied successfully to the "
                    "first node.")
                remaining_nodes = instances[1:]
                if apply_on_all:
                    LOG.debug("Applying the changes to the remaining nodes.")
                    for instance in remaining_nodes:
                        instance.reset_configuration(configuration_id)
                else:
                    LOG.debug(
                        "Releasing restart-required task on the remaining "
                        "nodes.")
                    for instance in remaining_nodes:
                        instance.update_db(task_status=InstanceTasks.NONE)
        finally:
            self.update_db(task_status=ClusterTasks.NONE)

        return self.__class__(self.context, self.db_info, self.ds,
                              self.ds_version)
Example #2
0
    def create_backup(self, context, backup_info):
        """
        Entry point for initiating a backup for this guest agents db instance.
        The call currently blocks until the backup is complete or errors. If
        device_path is specified, it will be mounted based to a point specified
        in configuration.

        :param context: User context object.
        :param backup_info: a dictionary containing the db instance id of the
                            backup task, location, type, and other data.
        """
        with EndNotification(context):
            self.app.create_backup(context, backup_info)
Example #3
0
 def create_instance(self, context, instance_id, name, flavor, image_id,
                     databases, users, datastore_manager, packages,
                     volume_size, backup_id, availability_zone,
                     root_password, nics, overrides, slave_of_id,
                     cluster_config, volume_type, modules, locality):
     with EndNotification(context,
                          instance_id=(instance_id[0] if isinstance(
                              instance_id, list) else instance_id)):
         self._create_instance(context, instance_id, name, flavor, image_id,
                               databases, users, datastore_manager,
                               packages, volume_size, backup_id,
                               availability_zone, root_password, nics,
                               overrides, slave_of_id, cluster_config,
                               volume_type, modules, locality)
Example #4
0
    def update_attributes(self, context, username, hostname, user_attrs):
        """Change the attributes of one existing user.

        The username and hostname parameters are strings.
        The user_attrs parameter is a dictionary in the following form:

            {"password": "", "name": ""}

        Each key/value pair in user_attrs is optional.
        """
        with EndNotification(context):
            if user_attrs.get('password') is not None:
                self.change_passwords(
                    context,
                    ({
                        "name": username,
                        "password": user_attrs['password'],
                    }, ),
                )

            if user_attrs.get('name') is not None:
                access = self.list_access(context, username, None)
                LOG.info(
                    _("{guest_id}: Changing username for {old} to {new}.").
                    format(
                        guest_id=CONF.guest_id,
                        old=username,
                        new=user_attrs['name'],
                    ))
                pgutil.psql(
                    pgutil.psql.UserQuery.update_name(
                        old=username,
                        new=user_attrs['name'],
                    ),
                    timeout=30,
                )
                # Regrant all previous access after the name change.
                LOG.info(
                    _("{guest_id}: Regranting permissions from {old} "
                      "to {new}.").format(
                          guest_id=CONF.guest_id,
                          old=username,
                          new=user_attrs['name'],
                      ))
                self.grant_access(context,
                                  username=user_attrs['name'],
                                  hostname=None,
                                  databases=(db['_name'] for db in access))
Example #5
0
 def detach_replica(self, context, instance_id):
     with EndNotification(context):
         slave = models.BuiltInstanceTasks.load(context, instance_id)
         master_id = slave.slave_of_id
         master = models.BuiltInstanceTasks.load(context, master_id)
         slave.detach_replica(master)
         if master.post_processing_required_for_replication():
             slave_instances = [
                 BuiltInstanceTasks.load(context, slave_model.id)
                 for slave_model in master.slaves
             ]
             slave_detail = [
                 slave_instance.get_replication_detail()
                 for slave_instance in slave_instances
             ]
             master.complete_master_setup(slave_detail)
Example #6
0
    def create(self, req, body, tenant_id):
        LOG.debug("req : '%s'\n\n" % req)
        LOG.debug("body : '%s'\n\n" % req)

        context = req.environ[wsgi.CONTEXT_KEY]
        policy.authorize_on_tenant(context, 'configuration:create')
        context.notification = notification.DBaaSConfigurationCreate(
            context, request=req)
        name = body['configuration']['name']
        description = body['configuration'].get('description')
        values = body['configuration']['values']

        msg = _("Creating configuration group on tenant "
                "%(tenant_id)s with name: %(cfg_name)s")
        LOG.info(msg % {"tenant_id": tenant_id, "cfg_name": name})

        datastore_args = body['configuration'].get('datastore', {})
        datastore, datastore_version = (
            ds_models.get_datastore_version(**datastore_args))

        with StartNotification(context, name=name, datastore=datastore.name,
                               datastore_version=datastore_version.name):
            configItems = []
            if values:
                # validate that the values passed in are permitted by the
                # operator.
                ConfigurationsController._validate_configuration(
                    body['configuration']['values'],
                    datastore_version,
                    models.DatastoreConfigurationParameters.load_parameters(
                        datastore_version.id))

                for k, v in values.items():
                    configItems.append(DBConfigurationParameter(
                        configuration_key=k,
                        configuration_value=v))

            cfg_group = models.Configuration.create(name, description,
                                                    tenant_id, datastore.id,
                                                    datastore_version.id)
            with EndNotification(context, configuration_id=cfg_group.id):
                cfg_group_items = models.Configuration.create_items(
                    cfg_group.id, values)

        view_data = views.DetailedConfigurationView(cfg_group,
                                                    cfg_group_items)
        return wsgi.Result(view_data.data(), 200)
Example #7
0
    def delete_database(self, context, database):
        """Delete the specified database.

        The database parameter is a dictionary in the following form:

            {"_name": ""}
        """
        with EndNotification(context):
            LOG.info(
                _("{guest_id}: Dropping database {name}.").format(
                    guest_id=CONF.guest_id,
                    name=database['_name'],
                ))
            pgutil.psql(
                pgutil.DatabaseQuery.drop(name=database['_name']),
                timeout=30,
            )
Example #8
0
    def delete_user(self, context, user):
        """Delete the specified user.

        The user parameter is a dictionary in the following form:

            {"_name": ""}
        """
        with EndNotification(context):
            LOG.info(
                _("{guest_id}: Dropping user {name}.").format(
                    guest_id=CONF.guest_id,
                    name=user['_name'],
                ))
            pgutil.psql(
                pgutil.UserQuery.drop(name=user['_name']),
                timeout=30,
            )
Example #9
0
 def prepare(self,
             context,
             packages,
             databases,
             memory_mb,
             users,
             device_path=None,
             mount_point=None,
             backup_info=None,
             config_contents=None,
             root_password=None,
             overrides=None,
             cluster_config=None,
             snapshot=None):
     """Set up datastore on a Guest Instance."""
     LOG.info(_("Starting datastore prepare."))
     with EndNotification(context):
         self.status.begin_install()
         if (cluster_config or self._require_post_processing(snapshot)):
             post_processing = True
         else:
             post_processing = False
         try:
             self.do_prepare(context,
                             packages,
                             databases,
                             memory_mb,
                             users,
                             device_path=device_path,
                             mount_point=mount_point,
                             backup_info=backup_info,
                             config_contents=config_contents,
                             root_password=root_password,
                             overrides=overrides,
                             cluster_config=cluster_config,
                             snapshot=snapshot)
         except Exception:
             self.prepare_error = True
             LOG.exception("An error occurred preparing datastore")
             raise
         finally:
             LOG.info(_("Ending datastore prepare."))
             self.status.end_install(error_occurred=self.prepare_error,
                                     post_processing=post_processing)
     LOG.info(_('Completed setup of datastore successfully.'))
Example #10
0
 def prepare(self,
             context,
             packages,
             databases,
             memory_mb,
             users,
             device_path=None,
             mount_point=None,
             backup_info=None,
             config_contents=None,
             root_password=None,
             overrides=None,
             cluster_config=None,
             snapshot=None,
             modules=None):
     """Set up datastore on a Guest Instance."""
     with EndNotification(context, instance_id=CONF.guest_id):
         self._prepare(context, packages, databases, memory_mb, users,
                       device_path, mount_point, backup_info,
                       config_contents, root_password, overrides,
                       cluster_config, snapshot, modules)
Example #11
0
    def create_backup(self, context, backup_info):
        """Create backup for the database.

        :param context: User context object.
        :param backup_info: a dictionary containing the db instance id of the
                            backup task, location, type, and other data.
        """
        LOG.info(f"Creating backup {backup_info['id']}")
        with EndNotification(context):
            volumes_mapping = {
                '/var/lib/postgresql/data': {
                    'bind': '/var/lib/postgresql/data', 'mode': 'rw'
                },
                "/var/run/postgresql": {"bind": "/var/run/postgresql",
                                        "mode": "ro"},
            }
            extra_params = f"--pg-wal-archive-dir {service.WAL_ARCHIVE_DIR}"

            self.app.create_backup(context, backup_info,
                                   volumes_mapping=volumes_mapping,
                                   need_dbuser=False,
                                   extra_params=extra_params)
Example #12
0
    def create_backup(self, context, backup_info):
        """Create backup for the database.

        :param context: User context object.
        :param backup_info: a dictionary containing the db instance id of the
                            backup task, location, type, and other data.
        """
        LOG.info(f"Creating backup {backup_info['id']}")
        with EndNotification(context):
            volumes_mapping = {
                '/var/lib/mysql': {
                    'bind': '/var/lib/mysql',
                    'mode': 'rw'
                },
                '/tmp': {
                    'bind': '/tmp',
                    'mode': 'rw'
                }
            }
            self.app.create_backup(context,
                                   backup_info,
                                   volumes_mapping=volumes_mapping,
                                   need_dbuser=True)
Example #13
0
    def update_attributes(self, context, username, hostname, user_attrs):
        """Change the attributes of one existing user.

        The username and hostname parameters are strings.
        The user_attrs parameter is a dictionary in the following form:

            {"password": "", "name": ""}

        Each key/value pair in user_attrs is optional.
        """
        with EndNotification(context):
            user = self._build_user(context, username)
            new_username = user_attrs.get('name')
            new_password = user_attrs.get('password')

            if new_username is not None:
                self._rename_user(context, user, new_username)
                # Make sure we can retrieve the renamed user.
                user = self._find_user(context, new_username)

            if new_password is not None:
                user.password = new_password
                self.alter_user(context, user)
Example #14
0
 def delete_user(self, context, user):
     LOG.debug("Deleting user.")
     with EndNotification(context):
         raise exception.DatastoreOperationNotSupported(
             operation='delete_user', datastore=MANAGER)
Example #15
0
 def delete_database(self, context, database):
     LOG.debug("Deleting database.")
     with EndNotification(context):
         raise exception.DatastoreOperationNotSupported(
             operation='delete_database', datastore=MANAGER)
Example #16
0
 def update_attributes(self, context, username, hostname, user_attrs):
     LOG.debug("Updating attributes.")
     with EndNotification(context):
         raise exception.DatastoreOperationNotSupported(
             operation='update_attributes', datastore=MANAGER)
Example #17
0
 def create_backup(self, context, backup_info):
     """Create a backup of the database."""
     LOG.debug("Creating backup.")
     with EndNotification(context):
         backup.backup(context, backup_info)
Example #18
0
    def promote_to_replica_source(self, context, instance_id):
        # TODO(atomic77) Promote and eject need to be able to handle the case
        # where a datastore like Postgresql needs to treat the slave to be
        # promoted differently from the old master and the slaves which will
        # be simply reassigned to a new master. See:
        # https://bugs.launchpad.net/trove/+bug/1553339

        def _promote_to_replica_source(old_master, master_candidate,
                                       replica_models):
            # First, we transition from the old master to new as quickly as
            # possible to minimize the scope of unrecoverable error
            old_master.make_read_only(True)
            master_ips = old_master.detach_public_ips()
            slave_ips = master_candidate.detach_public_ips()
            latest_txn_id = old_master.get_latest_txn_id()
            master_candidate.wait_for_txn(latest_txn_id)
            master_candidate.detach_replica(old_master, for_failover=True)
            master_candidate.enable_as_master()
            old_master.attach_replica(master_candidate)
            master_candidate.attach_public_ips(master_ips)
            master_candidate.make_read_only(False)
            old_master.attach_public_ips(slave_ips)

            # At this point, should something go wrong, there
            # should be a working master with some number of working slaves,
            # and possibly some number of "orphaned" slaves

            exception_replicas = []
            error_messages = ""
            for replica in replica_models:
                try:
                    if replica.id != master_candidate.id:
                        replica.detach_replica(old_master, for_failover=True)
                        replica.attach_replica(master_candidate)
                except exception.TroveError as ex:
                    msg = (_("Unable to migrate replica %(slave)s from "
                             "old replica source %(old_master)s to "
                             "new source %(new_master)s on promote.") % {
                                 "slave": replica.id,
                                 "old_master": old_master.id,
                                 "new_master": master_candidate.id
                             })
                    LOG.exception(msg)
                    exception_replicas.append(replica)
                    error_messages += "%s (%s)\n" % (msg, ex)

            try:
                old_master.demote_replication_master()
            except Exception as ex:
                msg = (_("Exception demoting old replica source %s.") %
                       old_master.id)
                LOG.exception(msg)
                exception_replicas.append(old_master)
                error_messages += "%s (%s)\n" % (msg, ex)

            self._set_task_status([old_master] + replica_models,
                                  InstanceTasks.NONE)
            if exception_replicas:
                self._set_task_status(exception_replicas,
                                      InstanceTasks.PROMOTION_ERROR)
                msg = (
                    _("promote-to-replica-source %(id)s: The following "
                      "replicas may not have been switched: %(replicas)s:"
                      "\n%(err)s") % {
                          "id": master_candidate.id,
                          "replicas": [repl.id for repl in exception_replicas],
                          "err": error_messages
                      })
                raise ReplicationSlaveAttachError(msg)

        with EndNotification(context):
            master_candidate = BuiltInstanceTasks.load(context, instance_id)
            old_master = BuiltInstanceTasks.load(context,
                                                 master_candidate.slave_of_id)
            replicas = []
            for replica_dbinfo in old_master.slaves:
                if replica_dbinfo.id == instance_id:
                    replica = master_candidate
                else:
                    replica = BuiltInstanceTasks.load(context,
                                                      replica_dbinfo.id)
                replicas.append(replica)

            try:
                _promote_to_replica_source(old_master, master_candidate,
                                           replicas)
            except ReplicationSlaveAttachError:
                raise
            except Exception:
                self._set_task_status([old_master] + replicas,
                                      InstanceTasks.PROMOTION_ERROR)
                raise
Example #19
0
 def detach_replica(self, context, instance_id):
     with EndNotification(context):
         slave = models.BuiltInstanceTasks.load(context, instance_id)
         master_id = slave.slave_of_id
         master = models.BuiltInstanceTasks.load(context, master_id)
         slave.detach_replica(master)
Example #20
0
 def restart(self, context, instance_id):
     with EndNotification(context):
         instance_tasks = models.BuiltInstanceTasks.load(
             context, instance_id)
         instance_tasks.restart()
Example #21
0
 def resize_flavor(self, context, instance_id, old_flavor, new_flavor):
     with EndNotification(context):
         instance_tasks = models.BuiltInstanceTasks.load(
             context, instance_id)
         instance_tasks.resize_flavor(old_flavor, new_flavor)
Example #22
0
 def delete_user(self, context, user):
     with EndNotification(context):
         self.adm.delete_user(user)
Example #23
0
 def change_passwords(self, context, users):
     with EndNotification(context):
         self.adm.change_passwords(users)
Example #24
0
 def create_database(self, context, databases):
     with EndNotification(context):
         return self.adm.create_databases(databases)
Example #25
0
 def delete_database(self, context, database):
     with EndNotification(context):
         return self.adm.delete_database(database)
Example #26
0
 def create_cluster(self, context, cluster_id):
     with EndNotification(context, cluster_id=cluster_id):
         cluster_tasks = models.load_cluster_tasks(context, cluster_id)
         cluster_tasks.create_cluster(context, cluster_id)
Example #27
0
 def create_user(self, context, users):
     with EndNotification(context):
         self.adm.create_users(users)
Example #28
0
 def delete_cluster(self, context, cluster_id):
     with EndNotification(context):
         cluster_tasks = models.load_cluster_tasks(context, cluster_id)
         cluster_tasks.delete_cluster(context, cluster_id)
Example #29
0
 def update_attributes(self, context, username, hostname, user_attrs):
     with EndNotification(context):
         self.adm.update_attributes(username, hostname, user_attrs)
Example #30
0
 def resize_volume(self, context, instance_id, new_size):
     with EndNotification(context):
         instance_tasks = models.BuiltInstanceTasks.load(
             context, instance_id)
         instance_tasks.resize_volume(new_size)