def test_tempdir_error_suppression(self): """test tempdir suppresses errors during directory removal.""" with self.assertRaises(OSError): with tempdir(prefix='cloud-init-dhcp-') as tdir: os.rmdir(tdir) # As a result, the directory is already gone, # so shutil.rmtree should raise OSError with tempdir(rmtree_ignore_errors=True, prefix='cloud-init-dhcp-') as tdir: os.rmdir(tdir)
def install_puppet_aio(url=AIO_INSTALL_URL, version=None, collection=None, cleanup=True): """Install puppet-agent from the puppetlabs repositories using the one-shot shell script :param url: URL from where to download the install script :param version: version to install, blank defaults to latest :param collection: collection to install, blank defaults to latest :param cleanup: whether to purge the puppetlabs repo after installation """ args = [] if version is not None: args = ['-v', version] if collection is not None: args += ['-c', collection] # Purge puppetlabs repos after installation if cleanup: args += ['--cleanup'] content = url_helper.readurl(url=url, retries=5).contents # Use tmpdir over tmpfile to avoid 'text file busy' on execute with temp_utils.tempdir(needs_exe=True) as tmpd: tmpf = os.path.join(tmpd, 'puppet-install') util.write_file(tmpf, content, mode=0o700) return subp.subp([tmpf] + args, capture=False)
def maybe_perform_dhcp_discovery(nic=None): """Perform dhcp discovery if nic valid and dhclient command exists. If the nic is invalid or undiscoverable or dhclient command is not found, skip dhcp_discovery and return an empty dict. @param nic: Name of the network interface we want to run dhclient on. @return: A list of dicts representing dhcp options for each lease obtained from the dhclient discovery if run, otherwise an empty list is returned. """ if nic is None: nic = find_fallback_nic() if nic is None: LOG.debug('Skip dhcp_discovery: Unable to find fallback nic.') return [] elif nic not in get_devicelist(): LOG.debug( 'Skip dhcp_discovery: nic %s not found in get_devicelist.', nic) return [] dhclient_path = util.which('dhclient') if not dhclient_path: LOG.debug('Skip dhclient configuration: No dhclient command found.') return [] with temp_utils.tempdir(rmtree_ignore_errors=True, prefix='cloud-init-dhcp-', needs_exe=True) as tdir: # Use /var/tmp because /run/cloud-init/tmp is mounted noexec return dhcp_discovery(dhclient_path, nic, tdir)
def resize(self, diskdev, partnum, partdev): myenv = os.environ.copy() myenv["LANG"] = "C" before = get_size(partdev) # growpart uses tmp dir to store intermediate states # and may conflict with systemd-tmpfiles-clean with temp_utils.tempdir(needs_exe=True) as tmpd: growpart_tmp = os.path.join(tmpd, "growpart") if not os.path.exists(growpart_tmp): os.mkdir(growpart_tmp, 0o700) myenv["TMPDIR"] = growpart_tmp try: subp.subp( ["growpart", "--dry-run", diskdev, partnum], env=myenv ) except subp.ProcessExecutionError as e: if e.exit_code != 1: util.logexc( LOG, "Failed growpart --dry-run for (%s, %s)", diskdev, partnum, ) raise ResizeFailedException(e) from e return (before, before) try: subp.subp(["growpart", diskdev, partnum], env=myenv) except subp.ProcessExecutionError as e: util.logexc(LOG, "Failed: growpart %s %s", diskdev, partnum) raise ResizeFailedException(e) from e return (before, get_size(partdev))
def maybe_perform_dhcp_discovery(nic=None, dhcp_log_func=None): """Perform dhcp discovery if nic valid and dhclient command exists. If the nic is invalid or undiscoverable or dhclient command is not found, skip dhcp_discovery and return an empty dict. @param nic: Name of the network interface we want to run dhclient on. @param dhcp_log_func: A callable accepting the dhclient output and error streams. @return: A list of dicts representing dhcp options for each lease obtained from the dhclient discovery if run, otherwise an empty list is returned. """ if nic is None: nic = find_fallback_nic() if nic is None: LOG.debug("Skip dhcp_discovery: Unable to find fallback nic.") raise NoDHCPLeaseInterfaceError() elif nic not in get_devicelist(): LOG.debug("Skip dhcp_discovery: nic %s not found in get_devicelist.", nic) raise NoDHCPLeaseInterfaceError() dhclient_path = subp.which("dhclient") if not dhclient_path: LOG.debug("Skip dhclient configuration: No dhclient command found.") raise NoDHCPLeaseMissingDhclientError() with temp_utils.tempdir(rmtree_ignore_errors=True, prefix="cloud-init-dhcp-", needs_exe=True) as tdir: # Use /var/tmp because /run/cloud-init/tmp is mounted noexec return dhcp_discovery(dhclient_path, nic, tdir, dhcp_log_func)
def collect_logs(tarfile, include_userdata): """Collect all cloud-init logs and tar them up into the provided tarfile. @param tarfile: The path of the tar-gzipped file to create. @param include_userdata: Boolean, true means include user-data. """ tarfile = os.path.abspath(tarfile) date = datetime.utcnow().date().strftime('%Y-%m-%d') log_dir = 'cloud-init-logs-{0}'.format(date) with tempdir(dir='/tmp') as tmp_dir: log_dir = os.path.join(tmp_dir, log_dir) _write_command_output_to_file( ['dpkg-query', '--show', "-f=${Version}\n", 'cloud-init'], os.path.join(log_dir, 'version')) _write_command_output_to_file(['dmesg'], os.path.join(log_dir, 'dmesg.txt')) _write_command_output_to_file(['journalctl', '-o', 'short-precise'], os.path.join(log_dir, 'journal.txt')) for log in CLOUDINIT_LOGS: copy(log, log_dir) if include_userdata: copy(USER_DATA_FILE, log_dir) run_dir = os.path.join(log_dir, 'run') ensure_dir(run_dir) shutil.copytree(CLOUDINIT_RUN_DIR, os.path.join(run_dir, 'cloud-init')) with chdir(tmp_dir): subp(['tar', 'czvf', tarfile, log_dir.replace(tmp_dir + '/', '')])
def collect_logs(tarfile, include_userdata, verbosity=0): """Collect all cloud-init logs and tar them up into the provided tarfile. @param tarfile: The path of the tar-gzipped file to create. @param include_userdata: Boolean, true means include user-data. """ if include_userdata and os.getuid() != 0: sys.stderr.write("To include userdata, root user is required." " Try sudo cloud-init collect-logs\n") return 1 tarfile = os.path.abspath(tarfile) date = datetime.utcnow().date().strftime('%Y-%m-%d') log_dir = 'cloud-init-logs-{0}'.format(date) with tempdir(dir='/tmp') as tmp_dir: log_dir = os.path.join(tmp_dir, log_dir) version = _write_command_output_to_file(['cloud-init', '--version'], os.path.join( log_dir, 'version'), "cloud-init --version", verbosity) dpkg_ver = _write_command_output_to_file( ['dpkg-query', '--show', "-f=${Version}\n", 'cloud-init'], os.path.join(log_dir, 'dpkg-version'), "dpkg version", verbosity) if not version: version = dpkg_ver if dpkg_ver else "not-available" _debug("collected cloud-init version: %s\n" % version, 1, verbosity) _write_command_output_to_file(['dmesg'], os.path.join(log_dir, 'dmesg.txt'), "dmesg output", verbosity) _write_command_output_to_file( ['journalctl', '--boot=0', '-o', 'short-precise'], os.path.join(log_dir, 'journal.txt'), "systemd journal of current boot", verbosity) for log in CLOUDINIT_LOGS: _collect_file(log, log_dir, verbosity) if include_userdata: _collect_file(USER_DATA_FILE, log_dir, verbosity) run_dir = os.path.join(log_dir, 'run') ensure_dir(run_dir) if os.path.exists(CLOUDINIT_RUN_DIR): try: shutil.copytree(CLOUDINIT_RUN_DIR, os.path.join(run_dir, 'cloud-init'), ignore=_copytree_rundir_ignore_files) except shutil.Error as e: sys.stderr.write("Failed collecting file(s) due to error:\n") sys.stderr.write(str(e) + '\n') _debug("collected dir %s\n" % CLOUDINIT_RUN_DIR, 1, verbosity) else: _debug("directory '%s' did not exist\n" % CLOUDINIT_RUN_DIR, 1, verbosity) with chdir(tmp_dir): subp(['tar', 'czvf', tarfile, log_dir.replace(tmp_dir + '/', '')]) sys.stderr.write("Wrote %s\n" % tarfile) return 0
def collect_logs(tarfile, include_userdata, verbosity=0): """Collect all cloud-init logs and tar them up into the provided tarfile. @param tarfile: The path of the tar-gzipped file to create. @param include_userdata: Boolean, true means include user-data. """ if include_userdata and os.getuid() != 0: sys.stderr.write( "To include userdata, root user is required." " Try sudo cloud-init collect-logs\n") return 1 tarfile = os.path.abspath(tarfile) date = datetime.utcnow().date().strftime('%Y-%m-%d') log_dir = 'cloud-init-logs-{0}'.format(date) with tempdir(dir='/tmp') as tmp_dir: log_dir = os.path.join(tmp_dir, log_dir) version = _write_command_output_to_file( ['cloud-init', '--version'], os.path.join(log_dir, 'version'), "cloud-init --version", verbosity) dpkg_ver = _write_command_output_to_file( ['dpkg-query', '--show', "-f=${Version}\n", 'cloud-init'], os.path.join(log_dir, 'dpkg-version'), "dpkg version", verbosity) if not version: version = dpkg_ver if dpkg_ver else "not-available" _debug("collected cloud-init version: %s\n" % version, 1, verbosity) _write_command_output_to_file( ['dmesg'], os.path.join(log_dir, 'dmesg.txt'), "dmesg output", verbosity) _write_command_output_to_file( ['journalctl', '--boot=0', '-o', 'short-precise'], os.path.join(log_dir, 'journal.txt'), "systemd journal of current boot", verbosity) for log in CLOUDINIT_LOGS: _collect_file(log, log_dir, verbosity) if include_userdata: _collect_file(USER_DATA_FILE, log_dir, verbosity) run_dir = os.path.join(log_dir, 'run') ensure_dir(run_dir) if os.path.exists(CLOUDINIT_RUN_DIR): shutil.copytree(CLOUDINIT_RUN_DIR, os.path.join(run_dir, 'cloud-init'), ignore=_copytree_ignore_sensitive_files) _debug("collected dir %s\n" % CLOUDINIT_RUN_DIR, 1, verbosity) else: _debug("directory '%s' did not exist\n" % CLOUDINIT_RUN_DIR, 1, verbosity) with chdir(tmp_dir): subp(['tar', 'czvf', tarfile, log_dir.replace(tmp_dir + '/', '')]) sys.stderr.write("Wrote %s\n" % tarfile) return 0
def subp_blob_in_tempfile(blob, *args, **kwargs): """Write blob to a tempfile, and call subp with args, kwargs. Then cleanup. 'basename' as a kwarg allows providing the basename for the file. The 'args' argument to subp will be updated with the full path to the filename as the first argument. """ basename = kwargs.pop('basename', "subp_blob") if len(args) == 0 and 'args' not in kwargs: args = [tuple()] # Use tmpdir over tmpfile to avoid 'text file busy' on execute with temp_utils.tempdir(needs_exe=True) as tmpd: tmpf = os.path.join(tmpd, basename) if 'args' in kwargs: kwargs['args'] = [tmpf] + list(kwargs['args']) else: args = list(args) args[0] = [tmpf] + args[0] util.write_file(tmpf, blob, mode=0o700) return subp.subp(*args, **kwargs)
def collect_logs(tarfile, include_userdata: bool, verbosity=0): """Collect all cloud-init logs and tar them up into the provided tarfile. @param tarfile: The path of the tar-gzipped file to create. @param include_userdata: Boolean, true means include user-data. """ if include_userdata and os.getuid() != 0: sys.stderr.write("To include userdata, root user is required." " Try sudo cloud-init collect-logs\n") return 1 tarfile = os.path.abspath(tarfile) date = datetime.utcnow().date().strftime("%Y-%m-%d") log_dir = "cloud-init-logs-{0}".format(date) with tempdir(dir="/tmp") as tmp_dir: log_dir = os.path.join(tmp_dir, log_dir) version = _write_command_output_to_file( ["cloud-init", "--version"], os.path.join(log_dir, "version"), "cloud-init --version", verbosity, ) dpkg_ver = _write_command_output_to_file( ["dpkg-query", "--show", "-f=${Version}\n", "cloud-init"], os.path.join(log_dir, "dpkg-version"), "dpkg version", verbosity, ) if not version: version = dpkg_ver if dpkg_ver else "not-available" _debug("collected cloud-init version: %s\n" % version, 1, verbosity) _write_command_output_to_file( ["dmesg"], os.path.join(log_dir, "dmesg.txt"), "dmesg output", verbosity, ) _write_command_output_to_file( ["journalctl", "--boot=0", "-o", "short-precise"], os.path.join(log_dir, "journal.txt"), "systemd journal of current boot", verbosity, ) for log in CLOUDINIT_LOGS: _collect_file(log, log_dir, verbosity) if include_userdata: user_data_file = _get_user_data_file() _collect_file(user_data_file, log_dir, verbosity) run_dir = os.path.join(log_dir, "run") ensure_dir(run_dir) if os.path.exists(CLOUDINIT_RUN_DIR): try: shutil.copytree( CLOUDINIT_RUN_DIR, os.path.join(run_dir, "cloud-init"), ignore=_copytree_rundir_ignore_files, ) except shutil.Error as e: sys.stderr.write("Failed collecting file(s) due to error:\n") sys.stderr.write(str(e) + "\n") _debug("collected dir %s\n" % CLOUDINIT_RUN_DIR, 1, verbosity) else: _debug( "directory '%s' did not exist\n" % CLOUDINIT_RUN_DIR, 1, verbosity, ) with chdir(tmp_dir): subp(["tar", "czvf", tarfile, log_dir.replace(tmp_dir + "/", "")]) sys.stderr.write("Wrote %s\n" % tarfile) return 0