def prepare_for_install_vertica(self): """This method executes preparatory command for install_vertica.""" utils.execute("VERT_DBA_USR=dbadmin", "VERT_DBA_HOME=/home/dbadmin", "VERT_DBA_GRP=verticadba", "/opt/vertica/oss/python/bin/python", "-m", "vertica.local_coerce", run_as_root=True, root_helper="sudo")
def _get_actual_db_status(self): """Get the status of dbaas and report it back.""" try: out, err = utils.execute( "su", "-", "dbadmin", "-c", system.STATUS_ACTIVE_DB, run_as_root=True, root_helper="sudo") if out.strip() == DB_NAME: # UP status is confirmed LOG.info("Service Status is RUNNING.") return rd_instance.ServiceStatuses.RUNNING elif out.strip() == "": # nothing returned, means no db running lets verify out, err = utils.execute( "su", "-", "dbadmin", "-c", system.STATUS_DB_DOWN, run_as_root=True, root_helper="sudo") if out.strip() == DB_NAME: # DOWN status is confirmed LOG.info("Service Status is SHUTDOWN.") return rd_instance.ServiceStatuses.SHUTDOWN else: return rd_instance.ServiceStatuses.UNKNOWN except exception.ProcessExecutionError: LOG.error("Process execution ") return rd_instance.ServiceStatuses.FAILED
def _fix_package_selections(self, packages, config_opts): """ Sometimes you have to run this command before a pkg will install. This command sets package selections to configure package. """ selections = "" for package in packages: m = re.match('(.+)=(.+)', package) if m: package_name = m.group(1) else: package_name = package command = "sudo debconf-show %s" % package_name p = commands.getstatusoutput(command) std_out = p[1] for line in std_out.split("\n"): for selection, value in config_opts.items(): m = re.match(".* (.*/%s):.*" % selection, line) if m: selections += ("%s %s string '%s'\n" % (package_name, m.group(1), value)) if selections: with NamedTemporaryFile(delete=False) as f: fname = f.name f.write(selections) utils.execute("debconf-set-selections %s && dpkg --configure -a" % fname, run_as_root=True, root_helper="sudo", shell=True) os.remove(fname)
def _fix(self, time_out): """Sometimes you have to run this command before a pkg will install.""" try: utils.execute("dpkg", "--configure", "-a", run_as_root=True, root_helper="sudo") except ProcessExecutionError: LOG.error(_("Error fixing dpkg"))
def _incremental_restore(self, location, checksum): """Recursively apply backups from all parents. If we are the parent then we restore to the restore_location and we apply the logs to the restore_location only. Otherwise if we are an incremental we restore to a subfolder to prevent stomping on the full restore data. Then we run apply log with the '--incremental-dir' flag """ metadata = self.storage.load_metadata(location, checksum) incremental_dir = None if 'parent_location' in metadata: LOG.info(_("Restoring parent: %(parent_location)s" " checksum: %(parent_checksum)s") % metadata) parent_location = metadata['parent_location'] parent_checksum = metadata['parent_checksum'] # Restore parents recursively so backup are applied sequentially self._incremental_restore(parent_location, parent_checksum) # for *this* backup set the incremental_dir # just use the checksum for the incremental path as it is # sufficently unique /var/lib/mysql/<checksum> incremental_dir = os.path.join(self.restore_location, checksum) utils.execute("mkdir", "-p", incremental_dir, root_helper="sudo", run_as_root=True) command = self._incremental_restore_cmd(incremental_dir) else: # The parent (full backup) use the same command from InnobackupEx # super class and do not set an incremental_dir. command = self.restore_cmd self.content_length += self._unpack(location, checksum, command) self._incremental_prepare(incremental_dir)
def clear_expired_password(): """ Some mysql installations generate random root password and save it in /root/.mysql_secret, this password is expired and should be changed by client that supports expired passwords. """ LOG.debug("Removing expired password.") secret_file = "/root/.mysql_secret" try: out, err = utils.execute("cat", secret_file, run_as_root=True, root_helper="sudo") except exception.ProcessExecutionError: LOG.exception(_("/root/.mysql_secret does not exist.")) return m = re.match('# The random password set for the root user at .*: (.*)', out) if m: try: out, err = utils.execute("mysqladmin", "-p%s" % m.group(1), "password", "", run_as_root=True, root_helper="sudo") except exception.ProcessExecutionError: LOG.exception(_("Cannot change mysql password.")) return operating_system.remove(secret_file, force=True, as_root=True) LOG.debug("Expired password removed.")
def _fix_package_selections(self, packages, config_opts): """ Sometimes you have to run this command before a package will install. This command sets package selections to configure package. """ selections = "" for package in packages: m = re.match('(.+)=(.+)', package) if m: package_name = m.group(1) else: package_name = package std_out = getoutput("sudo", "debconf-show", package_name) for line in std_out.split("\n"): for selection, value in config_opts.items(): m = re.match(".* (.*/%s):.*" % selection, line) if m: selections += ("%s %s string '%s'\n" % (package_name, m.group(1), value)) if selections: with NamedTemporaryFile(delete=False) as f: fname = f.name f.write(selections) try: utils.execute("debconf-set-selections", fname, run_as_root=True, root_helper="sudo") utils.execute("dpkg", "--configure", "-a", run_as_root=True, root_helper="sudo") except ProcessExecutionError: raise PkgConfigureError("Error configuring package.") finally: os.remove(fname)
def _run_post_backup(self): """Get rid of WAL data we don't need any longer""" arch_cleanup_bin = os.path.join(self.app.pgsql_extra_bin_dir, "pg_archivecleanup") bk_file = os.path.basename(self.most_recent_backup_file()) cmd_full = " ".join((arch_cleanup_bin, WAL_ARCHIVE_DIR, bk_file)) utils.execute("sudo", "su", "-", self.app.pgsql_owner, "-c", "%s" % cmd_full)
def migrate_data(self, mysql_base): """Synchronize the data from the mysql directory to the new volume """ self.mount(TMP_MOUNT_POINT, write_to_fstab=False) if not mysql_base[-1] == '/': mysql_base = "%s/" % mysql_base utils.execute("sudo", "rsync", "--safe-links", "--perms", "--recursive", "--owner", "--group", "--xattrs", "--sparse", mysql_base, TMP_MOUNT_POINT) self.unmount(TMP_MOUNT_POINT)
def resize_fs(self): """Resize the filesystem on the specified device""" self._check_device_exists() try: utils.execute("sudo", "resize2fs", self.device_path) except ProcessExecutionError as err: LOG.error(err) raise GuestError("Error resizing the filesystem: %s" % self.device_path)
def unmount(self, mount_point): if operating_system.is_mount(mount_point): try: utils.execute("umount", mount_point, run_as_root=True, root_helper='sudo') except exception.ProcessExecutionError: msg = _("Error unmounting '%s'.") % mount_point log_and_raise(msg) else: LOG.debug("'%s' is not a mounted fs, cannot unmount", mount_point)
def _rpm_remove_nodeps(self, package_name): """ Sometimes transaction errors happens, easy way is to remove conflicted package without dependencies and hope it will replaced by anoter package """ try: utils.execute("rpm", "-e", "--nodeps", package_name, run_as_root=True, root_helper="sudo") except ProcessExecutionError: LOG.error(_("Error removing conflict %s") % package_name)
def migrate_data(self, mysql_base): """ Synchronize the data from the mysql directory to the new volume """ # Use sudo to have access to this spot. utils.execute("sudo", "mkdir", "-p", TMP_MOUNT_POINT) self._tmp_mount(TMP_MOUNT_POINT) if not mysql_base[-1] == '/': mysql_base = "%s/" % mysql_base utils.execute("sudo", "rsync", "--safe-links", "--perms", "--recursive", "--owner", "--group", "--xattrs", "--sparse", mysql_base, TMP_MOUNT_POINT) self.unmount()
def resize_fs(self, mount_point): """Resize the filesystem on the specified device.""" self._check_device_exists() try: # check if the device is mounted at mount_point before e2fsck if not os.path.ismount(mount_point): utils.execute("e2fsck", "-f", "-n", self.device_path, run_as_root=True, root_helper="sudo") utils.execute("resize2fs", self.device_path, run_as_root=True, root_helper="sudo") except ProcessExecutionError: LOG.exception(_("Error resizing file system.")) raise GuestError(_("Error resizing the filesystem: %s") % self.device_path)
def migrate_data(self, source_dir): """Synchronize the data from the source directory to the new volume. """ self.mount(TMP_MOUNT_POINT, write_to_fstab=False) if not source_dir[-1] == '/': source_dir = "%s/" % source_dir utils.execute("sudo", "rsync", "--safe-links", "--perms", "--recursive", "--owner", "--group", "--xattrs", "--sparse", source_dir, TMP_MOUNT_POINT) self.unmount(TMP_MOUNT_POINT)
def write_to_fstab(self): fstab_line = ("%s\t%s\t%s\t%s\t0\t0" % (self.device_path, self.mount_point, self.volume_fstype, self.mount_options)) LOG.debug("Writing new line to fstab:%s" % fstab_line) utils.execute("sudo", "cp", "/etc/fstab", "/etc/fstab.orig") utils.execute("sudo", "cp", "/etc/fstab", "/tmp/newfstab") utils.execute("sudo", "chmod", "666", "/tmp/newfstab") with open("/tmp/newfstab", 'a') as new_fstab: new_fstab.write("\n" + fstab_line) utils.execute("sudo", "chmod", "640", "/tmp/newfstab") utils.execute("sudo", "mv", "/tmp/newfstab", "/etc/fstab")
def set_readahead_size(self, readahead_size): """Set the readahead size of disk.""" self._check_device_exists() try: utils.execute("blockdev", "--setra", readahead_size, self.device_path, run_as_root=True, root_helper="sudo") except exception.ProcessExecutionError: msg = _("Error setting readahead size to %(size)s " "for device %(device)s.") % { 'size': readahead_size, 'device': self.device_path} log_and_raise(msg)
def mount(self): if not os.path.exists(self.mount_point): utils.execute("sudo", "mkdir", "-p", self.mount_point) LOG.debug(_("Mounting volume. Device path:{0}, mount_point:{1}, " "volume_type:{2}, mount options:{3}").format( self.device_path, self.mount_point, self.volume_fstype, self.mount_options)) cmd = ("sudo mount -t %s -o %s %s %s" % (self.volume_fstype, self.mount_options, self.device_path, self.mount_point)) child = pexpect.spawn(cmd) child.expect(pexpect.EOF)
def resize_fs(self, mount_point): """Resize the filesystem on the specified device""" self._check_device_exists() try: # check if the device is mounted at mount_point before e2fsck if not os.path.ismount(mount_point): utils.execute("sudo", "e2fsck", "-f", "-n", self.device_path) utils.execute("sudo", "resize2fs", self.device_path) except ProcessExecutionError as err: LOG.error(err) raise GuestError("Error resizing the filesystem: %s" % self.device_path)
def write_to_fstab(self): fstab_line = ("%s\t%s\t%s\t%s\t0\t0" % (self.device_path, self.mount_point, self.volume_fstype, self.mount_options)) LOG.debug(_("Writing new line to fstab:%s") % fstab_line) with open('/etc/fstab', "r") as fstab: fstab_content = fstab.read() with NamedTemporaryFile(delete=False) as tempfstab: tempfstab.write(fstab_content + fstab_line) utils.execute("sudo", "install", "-o", "root", "-g", "root", "-m", "644", tempfstab.name, "/etc/fstab") utils.execute("sudo", "rm", tempfstab.name)
def _check_device_exists(self): """Check that the device path exists. Verify that the device path has actually been created and can report it's size, only then can it be available for formatting, retry num_tries to account for the time lag. """ try: num_tries = CONF.num_tries utils.execute('sudo', 'blockdev', '--getsize64', self.device_path, attempts=num_tries) except ProcessExecutionError: raise GuestError("InvalidDevicePath(path=%s)" % self.device_path)
def pkg_install(self, package_name, time_out): """Installs a package.""" try: utils.execute("apt-get", "update", run_as_root=True, root_helper="sudo") except ProcessExecutionError: LOG.error(_("Error updating the apt sources")) result = self._install(package_name, time_out) if result != OK: if result == RUN_DPKG_FIRST: self._fix(time_out) result = self._install(package_name, time_out) if result != OK: raise PkgPackageStateError("Package %s is in a bad state." % package_name)
def migrate_data(self, source_dir, target_subdir=None): """Synchronize the data from the source directory to the new volume; optionally to a new sub-directory on the new volume. """ self.mount(TMP_MOUNT_POINT, write_to_fstab=False) if not source_dir[-1] == '/': source_dir = "%s/" % source_dir target_dir = TMP_MOUNT_POINT if target_subdir: target_dir = target_dir + "/" + target_subdir utils.execute("sudo", "rsync", "--safe-links", "--perms", "--recursive", "--owner", "--group", "--xattrs", "--sparse", source_dir, target_dir) self.unmount(TMP_MOUNT_POINT)
def _check_device_exists(self): """Check that the device path exists. Verify that the device path has actually been created and can report it's size, only then can it be available for formatting, retry num_tries to account for the time lag. """ try: num_tries = CONF.num_tries LOG.debug("Checking if %s exists." % self.device_path) utils.execute("sudo", "blockdev", "--getsize64", self.device_path, attempts=num_tries) except ProcessExecutionError: LOG.exception(_("Error getting device status")) raise GuestError(_("InvalidDevicePath(path=%s)") % self.device_path)
def mount(self): if not operating_system.exists(self.mount_point, is_directory=True, as_root=True): operating_system.create_directory(self.mount_point, as_root=True) LOG.debug("Mounting volume. Device path:{0}, mount_point:{1}, " "volume_type:{2}, mount options:{3}".format( self.device_path, self.mount_point, self.volume_fstype, self.mount_options)) try: utils.execute("mount", "-t", self.volume_fstype, "-o", self.mount_options, self.device_path, self.mount_point, run_as_root=True, root_helper="sudo") except exception.ProcessExecutionError: msg = _("Could not mount '%s'.") % self.mount_point log_and_raise(msg)
def _rewind_against_master(self, service): """Call pg_rewind to resync datadir against state of new master We should already have a recovery.conf file in PGDATA """ rconf = operating_system.read_file( service.pgsql_recovery_config, codec=stream_codecs.KeyValueCodec(line_terminator="\n"), as_root=True ) conninfo = rconf["primary_conninfo"].strip() # The recovery.conf file we want should already be there, but pg_rewind # will delete it, so copy it out first rec = service.pgsql_recovery_config tmprec = "/tmp/recovery.conf.bak" operating_system.move(rec, tmprec, as_root=True) cmd_full = " ".join( [ "pg_rewind", "-D", service.pgsql_data_dir, "--source-pgdata=" + service.pgsql_data_dir, "--source-server=" + conninfo, ] ) out, err = utils.execute("sudo", "su", "-", service.pgsql_owner, "-c", "%s" % cmd_full, check_exit_code=0) LOG.debug("Got stdout %s and stderr %s from pg_rewind" % (str(out), str(err))) operating_system.move(tmprec, rec, as_root=True)
def write_to_fstab(self): fstab_line = ("%s\t%s\t%s\t%s\t0\t0" % (self.device_path, self.mount_point, self.volume_fstype, self.mount_options)) LOG.debug("Writing new line to fstab:%s" % fstab_line) with open('/etc/fstab', "r") as fstab: fstab_content = fstab.read() with NamedTemporaryFile(mode='w', delete=False) as tempfstab: tempfstab.write(fstab_content + fstab_line) try: utils.execute("install", "-o", "root", "-g", "root", "-m", "644", tempfstab.name, "/etc/fstab", run_as_root=True, root_helper="sudo") except exception.ProcessExecutionError: msg = _("Could not add '%s' to fstab.") % self.mount_point log_and_raise(msg) os.remove(tempfstab.name)
def _check_device_exists(self): """Check that the device path exists. Verify that the device path has actually been created and can report its size, only then can it be available for formatting, retry num_tries to account for the time lag. """ try: num_tries = CONF.num_tries LOG.debug("Checking if %s exists." % self.device_path) utils.execute("blockdev", "--getsize64", self.device_path, run_as_root=True, root_helper="sudo", attempts=num_tries) except exception.ProcessExecutionError: msg = _("Device '%s' is not ready.") % self.device_path log_and_raise(msg)
def _no_innobackupex_running(self): cmd = "ps aux | grep innobackupex | grep -v grep -c" stdout, stderr = utils.execute(cmd, shell = True, check_exit_code = False) running = int(stdout) >= 1 if running: LOG.info("innobackupex of last backup task is still running.") return not running
def _run_post_backup(self): """Get rid of WAL data we don't need any longer""" arch_cleanup_bin = os.path.join(self.PGSQL_EXTRA_BIN_DIR, "pg_archivecleanup") f = os.path.basename(self.most_recent_backup_file()) cmd_full = " ".join((arch_cleanup_bin, WAL_ARCHIVE_DIR, f)) out, err = utils.execute("sudo", "su", "-", self.PGSQL_OWNER, "-c", "%s" % cmd_full)
def load_mysqld_options(): # find mysqld bin for bin in MYSQL_BIN_CANDIDATES: if os.path.isfile(bin): mysqld_bin = bin break else: return {} try: out, err = utils.execute(mysqld_bin, "--print-defaults", run_as_root=True, root_helper="sudo") arglist = re.split("\n", out)[1].split() args = defaultdict(list) for item in arglist: if "=" in item: key, value = item.split("=", 1) args[key.lstrip("--")].append(value) else: args[item.lstrip("--")].append(None) return args except exception.ProcessExecutionError: return {}
def _check_format(self): """Checks that a volume is formatted.""" LOG.debug("Checking whether '%s' is formatted.", self.device_path) try: stdout, stderr = utils.execute("dumpe2fs", self.device_path, run_as_root=True, root_helper="sudo") if 'has_journal' not in stdout: msg = _("Volume '%s' does not appear to be formatted.") % ( self.device_path) raise exception.GuestError(original_message=msg) except exception.ProcessExecutionError as pe: if 'Wrong magic number' in pe.stderr: volume_fstype = CONF.volume_fstype msg = _("'Device '%(dev)s' did not seem to be '%(type)s'.") % ( { 'dev': self.device_path, 'type': volume_fstype }) log_and_raise(msg) msg = _("Volume '%s' was not formatted.") % self.device_path log_and_raise(msg)
def _rewind_against_master(self): """Call pg_rewind to resync datadir against state of new master We should already have a recovery.conf file in PGDATA """ rconf = operating_system.read_file(self.PGSQL_RECOVERY_CONFIG, as_root=True) regex = re.compile("primary_conninfo = (.*)") m = regex.search(rconf) conninfo = m.group(1) # The recovery.conf file we want should already be there, but pg_rewind # will delete it, so copy it out first rec = self.PGSQL_RECOVERY_CONFIG tmprec = "/tmp/recovery.conf.bak" operating_system.move(rec, tmprec, as_root=True) cmd_full = " ".join(["pg_rewind", "-D", self.PGSQL_DATA_DIR, '--source-server=' + conninfo]) out, err = utils.execute("sudo", "su", "-", self.PGSQL_OWNER, "-c", "%s" % cmd_full, check_exit_code=0) LOG.debug("Got stdout %s and stderr %s from pg_rewind" % (str(out), str(err))) operating_system.move(tmprec, rec, as_root=True)
def _start_mysqld_safe_with_init_file(self, init_file, err_log_file): # This directory is added and removed by the mysql systemd service # as the database is started and stopped. The restore operation # takes place when the database is stopped, so the directory does # not exist, but it is assumed to exist by the mysqld_safe command # which starts the database. This command used to create this # directory if it didn't exist, but it was changed recently to # simply fail in this case. run_dir = "/var/run/mysqld" if not os.path.exists(run_dir): utils.execute("mkdir", run_dir, run_as_root=True, root_helper="sudo") utils.execute("chown", "mysql:mysql", run_dir, err_log_file.name, init_file.name, run_as_root=True, root_helper="sudo") command_mysql_safe = ("sudo mysqld_safe" " --init-file=%s" " --log-error=%s" % (init_file.name, err_log_file.name)) LOG.debug("Spawning: %s" % command_mysql_safe) child = pexpect.spawn(command_mysql_safe) try: index = child.expect(['Starting mysqld daemon']) if index == 0: LOG.info("Starting MySQL") except pexpect.TIMEOUT: LOG.exception("Got a timeout launching mysqld_safe") finally: # There is a race condition here where we kill mysqld before # the init file been executed. We need to ensure mysqld is up. # # mysqld_safe will start even if init-file statement(s) fail. # We therefore also check for errors in the log file. self.poll_until_then_raise( self.mysql_is_running, base.RestoreError("Reset root password failed:" " mysqld did not start!")) first_err_message = self._find_first_error_message(err_log_file) if first_err_message: raise base.RestoreError("Reset root password failed: %s" % first_err_message) LOG.info("Root password reset successfully.") LOG.debug("Cleaning up the temp mysqld process.") utils.execute_with_timeout("mysqladmin", "-uroot", "--protocol=tcp", "shutdown") LOG.debug("Polling for shutdown to complete.") try: utils.poll_until(self.mysql_is_not_running, sleep_time=self.RESET_ROOT_SLEEP_INTERVAL, time_out=self.RESET_ROOT_RETRY_TIMEOUT) LOG.debug("Database successfully shutdown") except exception.PollTimeOut: LOG.debug("Timeout shutting down database " "- performing killall on mysqld_safe.") utils.execute_with_timeout("killall", "mysqld_safe", root_helper="sudo", run_as_root=True) self.poll_until_then_raise( self.mysql_is_not_running, base.RestoreError("Reset root password failed: " "mysqld did not stop!"))
def _remove_snapshot(self, snapshot_name): utils.execute('/usr/libexec/k2hdkc-snapshot', '--remove', '%s' % self._app.k2hdkc_data_dir, '%s' % snapshot_name)
def _snapshot_all_keyspaces(self, snapshot_name): utils.execute('/usr/libexec/k2hdkc-snapshot', '--save', '%s' % self._app.k2hdkc_data_dir, '%s' % snapshot_name)
def _run_prepare(self): if hasattr(self, 'prepare_cmd'): LOG.info("Running innobackupex prepare...") self.prep_retcode = utils.execute(self.prepare_cmd, shell=True) LOG.info("Innobackupex prepare finished successfully")
def _run_prepare(self): LOG.debug("Running innobackupex prepare: %s.", self.prepare_cmd) self.prep_retcode = utils.execute(self.prepare_cmd, shell=True) LOG.info(_("Innobackupex prepare finished successfully."))
def _remove_snapshot(self, snapshot_name): LOG.debug('Clearing snapshot(s) for all keyspaces with snapshot name ' '"%s".', snapshot_name) utils.execute('nodetool', 'clearsnapshot', '-t %s' % snapshot_name)
def _snapshot_all_keyspaces(self, snapshot_name): LOG.debug('Creating snapshot(s) for all keyspaces with snapshot name ' '"%s".', snapshot_name) utils.execute('nodetool', 'snapshot', '-t %s' % snapshot_name)
def mount_points(self, device_path): """Returns a list of mount points on the specified device.""" stdout, stderr = utils.execute("grep '^%s ' /etc/mtab" % device_path, shell=True, check_exit_code=[0, 1]) return [entry.strip().split()[1] for entry in stdout.splitlines()]
def _incremental_prepare(self, incremental_dir): prepare_cmd = self._incremental_prepare_cmd(incremental_dir) LOG.debug("Running innobackupex prepare: %s.", prepare_cmd) utils.execute(prepare_cmd, shell=True) LOG.info(_("Innobackupex prepare finished successfully."))
def _incremental_prepare(self, incremental_dir): prepare_cmd = self._incremental_prepare_cmd(incremental_dir) LOG.debug("Running mariabackup prepare: %s.", prepare_cmd) utils.execute(prepare_cmd, shell=True) LOG.debug("mariabackup prepare finished successfully.")