def verifyDependencies(): """ Verify that we have no dependency issues in package manager. Dependency issues could appear because of aborted or terminated package installation process or invalid packages state after manual modification of packages list on the host :return True if no dependency issues found, False if dependency issue present :rtype bool """ check_str = None cmd = None if OSCheck.is_redhat_family(): cmd = ['/usr/bin/yum', '-d', '0', '-e', '0', 'check', 'dependencies'] check_str = "has missing requires|Error:" elif OSCheck.is_suse_family(): cmd = ['/usr/bin/zypper', '--quiet', '--non-interactive' 'verify', '--dry-run'] check_str = "\d+ new package(s)? to install" elif OSCheck.is_ubuntu_family(): cmd = ['/usr/bin/apt-get', '-qq', 'check'] check_str = "has missing dependency|E:" if check_str is None or cmd is None: raise Fail("Unsupported OSFamily on the Agent Host") code, out = rmf_shell.checked_call(cmd, sudo=True) output_regex = re.compile(check_str) if code or (out and output_regex.search(out)): err_msg = Logger.filter_text("Failed to verify package dependencies. Execution of '%s' returned %s. %s" % (cmd, code, out)) Logger.error(err_msg) return False return True
def get_user_call_output(command, user, quiet=False, is_checked_call=True, **call_kwargs): """ This function eliminates only output of command inside the su, ignoring the su ouput itself. This is useful since some users have motd messages setup by default on su -l. @return: code, stdout, stderr """ command_string = shell.string_cmd_from_args_list(command) if isinstance( command, (list, tuple)) else command out_files = [] try: out_files.append(tempfile.NamedTemporaryFile()) out_files.append(tempfile.NamedTemporaryFile()) # other user should be able to write to it for f in out_files: os.chmod(f.name, 0666) command_string += " 1>" + out_files[0].name command_string += " 2>" + out_files[1].name code, _ = shell.call(shell.as_user(command_string, user), quiet=quiet, **call_kwargs) files_output = [] for f in out_files: files_output.append(f.read().decode("utf-8").strip('\n')) if code: all_output = files_output[1] + '\n' + files_output[0] err_msg = Logger.filter_text( ("Execution of '%s' returned %d. %s") % (command_string, code, all_output)) if is_checked_call: raise Fail(err_msg) else: Logger.warning(err_msg) result = code, files_output[0], files_output[1] caller_filename = sys._getframe(1).f_code.co_filename is_internal_call = shell.NOT_LOGGED_FOLDER in caller_filename if quiet == False or (quiet == None and not is_internal_call): log_msg = "{0} returned {1}".format(get_user_call_output.__name__, result) Logger.info(log_msg) return result finally: for f in out_files: f.close()
def service_check(self, env): import params # If Ambari IS managing Kerberos identities (kerberos-env/manage_identities = true), it is # expected that a (smoke) test principal and its associated keytab file is available for use # ** If not available, this service check will fail # ** If available, this service check will execute # # If Ambari IS NOT managing Kerberos identities (kerberos-env/manage_identities = false), the # smoke test principal and its associated keytab file may not be available # ** If not available, this service check will execute # ** If available, this service check will execute if ((params.smoke_test_principal is not None) and (params.smoke_test_keytab_file is not None) and os.path.isfile(params.smoke_test_keytab_file)): print "Performing kinit using %s" % params.smoke_test_principal ccache_file_name = HASH_ALGORITHM("{0}|{1}".format( params.smoke_test_principal, params.smoke_test_keytab_file)).hexdigest() ccache_file_path = "{0}{1}kerberos_service_check_cc_{2}".format( params.tmp_dir, os.sep, ccache_file_name) kinit_path_local = functions.get_kinit_path( default('/configurations/kerberos-env/executable_search_paths', None)) kinit_command = "{0} -c {1} -kt {2} {3}".format( kinit_path_local, ccache_file_path, params.smoke_test_keytab_file, params.smoke_test_principal) try: # kinit Execute(kinit_command, user=params.smoke_user, wait_for_finish=True, tries=params.service_check_retry_count, try_sleep=params.service_check_retry_period_sec) finally: File( ccache_file_path, # Since kinit might fail to write to the cache file for various reasons, an existence check should be done before cleanup action="delete", ) elif params.manage_identities: err_msg = Logger.filter_text( "Failed to execute kinit test due to principal or keytab not found or available" ) raise Fail(err_msg) else: # Ambari is not managing identities so if the smoke user does not exist, indicate why.... print "Skipping this service check since Ambari is not managing Kerberos identities and the smoke user " \ "credentials are not available. To execute this service check, the smoke user principal name " \ "and keytab file location must be set in the cluster_env and the smoke user's keytab file must" \ "exist in the configured location."
def test_kinit(identity, user=None): principal = get_property_value(identity, 'principal') kinit_path_local = functions.get_kinit_path(default('/configurations/kerberos-env/executable_search_paths', None)) kdestroy_path_local = functions.get_kdestroy_path(default('/configurations/kerberos-env/executable_search_paths', None)) if principal is not None: keytab_file = get_property_value(identity, 'keytab_file') keytab = get_property_value(identity, 'keytab') password = get_property_value(identity, 'password') # If a test keytab file is available, simply use it if (keytab_file is not None) and (os.path.isfile(keytab_file)): command = '%s -k -t %s %s' % (kinit_path_local, keytab_file, principal) Execute(command, user = user, ) return shell.checked_call(kdestroy_path_local) # If base64-encoded test keytab data is available; then decode it, write it to a temporary file # use it, and then remove the temporary file elif keytab is not None: (fd, test_keytab_file) = tempfile.mkstemp() os.write(fd, base64.b64decode(keytab)) os.close(fd) try: command = '%s -k -t %s %s' % (kinit_path_local, test_keytab_file, principal) Execute(command, user = user, ) return shell.checked_call(kdestroy_path_local) except: raise finally: if test_keytab_file is not None: os.remove(test_keytab_file) # If no keytab data is available and a password was supplied, simply use it. elif password is not None: process = subprocess.Popen([kinit_path_local, principal], stdin=subprocess.PIPE) stdout, stderr = process.communicate(password) if process.returncode: err_msg = Logger.filter_text("Execution of kinit returned %d. %s" % (process.returncode, stderr)) raise Fail(err_msg) else: return shell.checked_call(kdestroy_path_local) else: return 0, '' else: return 0, ''
def get_user_call_output(command, user, quiet=False, is_checked_call=True, **call_kwargs): """ This function eliminates only output of command inside the su, ignoring the su ouput itself. This is useful since some users have motd messages setup by default on su -l. @return: code, stdout, stderr """ command_string = shell.string_cmd_from_args_list(command) if isinstance( command, (list, tuple)) else command out_files = [] try: out_files.append(tempfile.NamedTemporaryFile()) out_files.append(tempfile.NamedTemporaryFile()) # other user should be able to write to it for f in out_files: os.chmod(f.name, 0666) command_string += " 1>" + out_files[0].name command_string += " 2>" + out_files[1].name code, _ = shell.call(shell.as_user(command_string, user), quiet=quiet, **call_kwargs) files_output = [] for f in out_files: files_output.append(f.read().strip('\n')) if code: all_output = files_output[1] + '\n' + files_output[0] err_msg = Logger.filter_text( ("Execution of '%s' returned %d. %s") % (command_string, code, all_output)) if is_checked_call: raise Fail(err_msg) else: Logger.warning(err_msg) return code, files_output[0], files_output[1] finally: for f in out_files: f.close()
def install_package(self, name, use_repos=[], skip_repos=[], is_upgrade=False): if is_upgrade or use_repos or not self._check_existence(name): cmd = INSTALL_CMD[self.get_logoutput()] copied_sources_files = [] is_tmp_dir_created = False if use_repos: is_tmp_dir_created = True apt_sources_list_tmp_dir = tempfile.mkdtemp(suffix="-ambari-apt-sources-d") Logger.info("Temporal sources directory was created: %s" % apt_sources_list_tmp_dir) if 'base' not in use_repos: cmd = cmd + ['-o', 'Dir::Etc::SourceList=%s' % EMPTY_FILE] for repo in use_repos: if repo != 'base': new_sources_file = os.path.join(apt_sources_list_tmp_dir, repo + '.list') Logger.info("Temporal sources file will be copied: %s" % new_sources_file) sudo.copy(os.path.join(APT_SOURCES_LIST_DIR, repo + '.list'), new_sources_file) copied_sources_files.append(new_sources_file) cmd = cmd + ['-o', 'Dir::Etc::SourceParts=%s' % apt_sources_list_tmp_dir] cmd = cmd + [name] Logger.info("Installing package %s ('%s')" % (name, string_cmd_from_args_list(cmd))) code, out = self.call_with_retries(cmd, sudo=True, env=INSTALL_CMD_ENV, logoutput=self.get_logoutput()) if self.is_locked_output(out): err_msg = Logger.filter_text("Execution of '%s' returned %d. %s" % (cmd, code, out)) raise Fail(err_msg) # apt-get update wasn't done too long maybe? if code: Logger.info("Execution of '%s' returned %d. %s" % (cmd, code, out)) Logger.info("Failed to install package %s. Executing `%s`" % (name, string_cmd_from_args_list(REPO_UPDATE_CMD))) code, out = self.call_with_retries(REPO_UPDATE_CMD, sudo=True, logoutput=self.get_logoutput()) if code: Logger.info("Execution of '%s' returned %d. %s" % (REPO_UPDATE_CMD, code, out)) Logger.info("Retrying to install package %s" % (name)) self.checked_call_with_retries(cmd, sudo=True, env=INSTALL_CMD_ENV, logoutput=self.get_logoutput()) if is_tmp_dir_created: for temporal_sources_file in copied_sources_files: Logger.info("Removing temporal sources file: %s" % temporal_sources_file) os.remove(temporal_sources_file) Logger.info("Removing temporal sources directory: %s" % apt_sources_list_tmp_dir) os.rmdir(apt_sources_list_tmp_dir) else: Logger.info("Skipping installation of existing package %s" % (name))
def install_package(self, name, use_repos=[], skip_repos=[], is_upgrade=False): if is_upgrade or use_repos or not self._check_existence(name): cmd = INSTALL_CMD[self.get_logoutput()] copied_sources_files = [] is_tmp_dir_created = False if use_repos: is_tmp_dir_created = True apt_sources_list_tmp_dir = tempfile.mkdtemp(suffix="-ambari-apt-sources-d") Logger.info("Temporal sources directory was created: %s" % apt_sources_list_tmp_dir) if 'base' not in use_repos: cmd = cmd + ['-o', 'Dir::Etc::SourceList=%s' % EMPTY_FILE] for repo in use_repos: if repo != 'base': new_sources_file = os.path.join(apt_sources_list_tmp_dir, repo + '.list') Logger.info("Temporal sources file will be copied: %s" % new_sources_file) sudo.copy(os.path.join(APT_SOURCES_LIST_DIR, repo + '.list'), new_sources_file) copied_sources_files.append(new_sources_file) cmd = cmd + ['-o', 'Dir::Etc::SourceParts=%s' % apt_sources_list_tmp_dir] cmd = cmd + [name] Logger.info("Installing package %s ('%s')" % (name, string_cmd_from_args_list(cmd))) code, out = self.call_until_not_locked(cmd, sudo=True, env=INSTALL_CMD_ENV, logoutput=self.get_logoutput()) if self.is_locked_output(out): err_msg = Logger.filter_text("Execution of '%s' returned %d. %s" % (cmd, code, out)) raise Fail(err_msg) # apt-get update wasn't done too long maybe? if code: Logger.info("Execution of '%s' returned %d. %s" % (cmd, code, out)) Logger.info("Failed to install package %s. Executing `%s`" % (name, string_cmd_from_args_list(REPO_UPDATE_CMD))) code, out = self.call_until_not_locked(REPO_UPDATE_CMD, sudo=True, logoutput=self.get_logoutput()) if code: Logger.info("Execution of '%s' returned %d. %s" % (REPO_UPDATE_CMD, code, out)) Logger.info("Retrying to install package %s" % (name)) self.checked_call_until_not_locked(cmd, sudo=True, env=INSTALL_CMD_ENV, logoutput=self.get_logoutput()) if is_tmp_dir_created: for temporal_sources_file in copied_sources_files: Logger.info("Removing temporal sources file: %s" % temporal_sources_file) os.remove(temporal_sources_file) Logger.info("Removing temporal sources directory: %s" % apt_sources_list_tmp_dir) os.rmdir(apt_sources_list_tmp_dir) else: Logger.info("Skipping installation of existing package %s" % (name))
def verify_dependencies(self): """ Verify that we have no dependency issues in package manager. Dependency issues could appear because of aborted or terminated package installation process or invalid packages state after manual modification of packages list on the host :return True if no dependency issues found, False if dependency issue present :rtype bool """ code, out = self.checked_call(VERIFY_DEPENDENCY_CMD, sudo=True) pattern = re.compile("\d+ new package(s)? to install") if code or (out and pattern.search(out)): err_msg = Logger.filter_text("Failed to verify package dependencies. Execution of '%s' returned %s. %s" % (VERIFY_DEPENDENCY_CMD, code, out)) Logger.error(err_msg) return False return True
def verify_dependencies(self): """ Verify that we have no dependency issues in package manager. Dependency issues could appear because of aborted or terminated package installation process or invalid packages state after manual modification of packages list on the host :return True if no dependency issues found, False if dependency issue present :rtype bool """ r = shell.subprocess_executor(self.properties.verify_dependency_cmd) pattern = re.compile("\d+ new package(s)? to install") if r.code or (r.out and pattern.search(r.out)): err_msg = Logger.filter_text("Failed to verify package dependencies. Execution of '{0}' returned {1}. {2}".format( self.properties.verify_dependency_cmd, r.code, r.out)) Logger.error(err_msg) return False return True
def verify_dependencies(self): """ Verify that we have no dependency issues in package manager. Dependency issues could appear because of aborted or terminated package installation process or invalid packages state after manual modification of packages list on the host :return True if no dependency issues found, False if dependency issue present :rtype bool """ r = shell.subprocess_executor(self.properties.verify_dependency_cmd) pattern = re.compile("has missing dependency|E:") if r.code or (r.out and pattern.search(r.out)): err_msg = Logger.filter_text( "Failed to verify package dependencies. Execution of '%s' returned %s. %s" % (VERIFY_DEPENDENCY_CMD, code, out)) Logger.error(err_msg) return False return True
def as_sudo(command, env=None, auto_escape=True): """ command - list or tuple of arguments. env - when run as part of Execute resource, this SHOULD NOT be used. It automatically gets replaced later by call, checked_call. This should be used in not_if, only_if """ if isinstance(command, (list, tuple)): command = string_cmd_from_args_list(command, auto_escape=auto_escape) else: # Since ambari user sudoer privileges may be restricted, # without having /bin/bash permission, and /bin/su permission. # Running interpreted shell commands in scope of 'sudo' is not possible. # # In that case while passing string, # any bash symbols eventually added to command like && || ; < > | << >> would cause problems. err_msg = Logger.filter_text(("String command '%s' cannot be run as sudo. Please supply the command as a tuple of arguments") % (command)) raise Fail(err_msg) env = _get_environment_str(_add_current_path_to_env(env)) if env else ENV_PLACEHOLDER return "{0} {1} -H -E {2}".format(_get_sudo_binary(), env, command)
def get_user_call_output(command, user, quiet=False, is_checked_call=True, **call_kwargs): """ This function eliminates only output of command inside the su, ignoring the su ouput itself. This is useful since some users have motd messages setup by default on su -l. @return: code, stdout, stderr """ command_string = shell.string_cmd_from_args_list(command) if isinstance(command, (list, tuple)) else command out_files = [] try: out_files.append(tempfile.NamedTemporaryFile()) out_files.append(tempfile.NamedTemporaryFile()) # other user should be able to write to it for f in out_files: os.chmod(f.name, 0666) command_string += " 1>" + out_files[0].name command_string += " 2>" + out_files[1].name code, _ = shell.call(shell.as_user(command_string, user), quiet=quiet, **call_kwargs) files_output = [] for f in out_files: files_output.append(f.read().strip('\n')) if code: all_output = files_output[1] + '\n' + files_output[0] err_msg = Logger.filter_text(("Execution of '%s' returned %d. %s") % (command_string, code, all_output)) if is_checked_call: raise Fail(err_msg) else: Logger.warning(err_msg) return code, files_output[0], files_output[1] finally: for f in out_files: f.close()
proc.wait() finally: for fp in files_to_close: fp.close() out = fd_to_string[proc.stdout].strip('\n') err = fd_to_string[proc.stderr].strip('\n') all_output = all_output.strip('\n') if timeout: if not timeout_event.is_set(): t.cancel() # timeout occurred else: err_msg = Logger.filter_text(("Execution of '%s' was killed due timeout after %d seconds") % (command_alias, timeout)) raise ExecuteTimeoutException(err_msg) code = proc.returncode if throw_on_failure and code: err_msg = Logger.filter_text(("Execution of '%s' returned %d. %s") % (command_alias, code, all_output)) raise Fail(err_msg) # if separate stderr is enabled (by default it's redirected to out) if stderr == subprocess.PIPE: return code, out, err return code, out def as_sudo(command, env=None, auto_escape=True):
os.close(master_fd) proc.wait() out = out.strip('\n') if timeout: if not timeout_event.is_set(): t.cancel() # timeout occurred else: raise ExecuteTimeoutException() code = proc.returncode if throw_on_failure and code: err_msg = Logger.filter_text( ("Execution of '%s' returned %d. %s") % (command_alias, code, out)) raise Fail(err_msg) return code, out def as_sudo(command, env=None): """ command - list or tuple of arguments. env - when run as part of Execute resource, this SHOULD NOT be used. It automatically gets replaced later by call, checked_call. This should be used in not_if, only_if """ if isinstance(command, (list, tuple)): command = string_cmd_from_args_list(command) else: # Since ambari user sudoer privileges may be restricted,
all_output = all_output.strip('\n') if timeout: if not timeout_event.is_set(): t.cancel() # timeout occurred else: err_msg = "Execution of '{0}' was killed due timeout after {1} seconds".format( command, timeout) raise ExecuteTimeoutException(err_msg) code = proc.returncode if throw_on_failure and code: err_msg = Logger.filter_text( "Execution of '{0}' returned {1}. {2}".format( command_alias, code, all_output)) raise ExecutionFailed(err_msg, code, out, err) # if separate stderr is enabled (by default it's redirected to out) if stderr == subprocess.PIPE: return code, out, err return code, out def as_sudo(command, env=None, auto_escape=True): """ command - list or tuple of arguments. env - when run as part of Execute resource, this SHOULD NOT be used. It automatically gets replaced later by call, checked_call. This should be used in not_if, only_if
def _call(command, logoutput=None, throw_on_failure=True, stdout=subprocess32.PIPE, stderr=subprocess32.STDOUT, cwd=None, env=None, preexec_fn=preexec_fn, user=None, wait_for_finish=True, timeout=None, on_timeout=None, path=None, sudo=False, on_new_line=None, tries=1, try_sleep=0, timeout_kill_strategy=TerminateStrategy.TERMINATE_PARENT, returns=[0]): """ Execute shell command @param command: list/tuple of arguments (recommended as more safe - don't need to escape) or string of the command to execute @param logoutput: boolean, whether command output should be logged of not @param throw_on_failure: if true, when return code is not zero exception is thrown @param stdout,stderr: subprocess32.PIPE - enable output to variable subprocess32.STDOUT - redirect to stdout None - disable output to variable, and output to Python out straightly (even if logoutput is False) {int fd} - redirect to file with descriptor. {string filename} - redirects to a file with name. """ command_alias = Logger.format_command_for_output(command) command_alias = string_cmd_from_args_list(command_alias) if isinstance( command_alias, (list, tuple)) else command_alias # Append current PATH to env['PATH'] env = _add_current_path_to_env(env) # Append path to env['PATH'] if path: path = os.pathsep.join(path) if isinstance(path, (list, tuple)) else path env['PATH'] = os.pathsep.join([env['PATH'], path]) if sudo and user: raise ValueError( "Only one from sudo or user argument could be set to True") # prepare command cmd if sudo: command = as_sudo(command, env=env) elif user: command = as_user(command, user, env=env) # convert to string and escape if isinstance(command, (list, tuple)): command = string_cmd_from_args_list(command) # replace placeholder from as_sudo / as_user if present env_str = _get_environment_str(env) for placeholder, replacement in PLACEHOLDERS_TO_STR.iteritems(): command = command.replace(placeholder, replacement.format(env_str=env_str)) # --noprofile is used to preserve PATH set for ambari-agent subprocess32_command = [ "/bin/bash", "--login", "--noprofile", "-c", command ] files_to_close = [] if isinstance(stdout, basestring): stdout = open(stdout, 'wb') files_to_close.append(stdout) if isinstance(stderr, basestring): stderr = open(stderr, 'wb') files_to_close.append(stderr) try: proc = subprocess32.Popen(subprocess32_command, stdout=stdout, stderr=stderr, cwd=cwd, env=env, shell=False, close_fds=True, preexec_fn=preexec_fn) if timeout: timeout_event = threading.Event() timer = threading.Timer( timeout, _on_timeout, [proc, timeout_event, timeout_kill_strategy]) timer.start() if not wait_for_finish: return proc # in case logoutput == False, never log. logoutput = logoutput is True and Logger.logger.isEnabledFor( logging.INFO) or logoutput is None and Logger.logger.isEnabledFor( logging.DEBUG) read_set = [] if stdout == subprocess32.PIPE: read_set.append(proc.stdout) if stderr == subprocess32.PIPE: read_set.append(proc.stderr) fd_to_string = {proc.stdout: "", proc.stderr: ""} all_output = "" while read_set: is_proccess_running = proc.poll() is None ready, _, _ = select.select(read_set, [], [], 1) if not is_proccess_running and not ready: break for out_fd in read_set: if out_fd in ready: line = os.read(out_fd.fileno(), 1024) if not line: read_set = copy.copy(read_set) read_set.remove(out_fd) out_fd.close() continue fd_to_string[out_fd] += line all_output += line if on_new_line: try: on_new_line(line, out_fd == proc.stderr) except Exception: err_msg = "Caused by on_new_line function failed with exception for input argument '{0}':\n{1}".format( line, traceback.format_exc()) raise Fail(err_msg) if logoutput: sys.stdout.write(line) sys.stdout.flush() # Wait for process to terminate if not timeout or not timeout_event.is_set(): proc.wait() finally: for fp in files_to_close: fp.close() out = fd_to_string[proc.stdout].strip('\n') err = fd_to_string[proc.stderr].strip('\n') all_output = all_output.strip('\n') if timeout: if not timeout_event.is_set(): timer.cancel() # timeout occurred else: err_msg = "Execution of '{0}' was killed due timeout after {1} seconds".format( command, timeout) raise ExecuteTimeoutException(err_msg) code = proc.returncode if throw_on_failure and not code in returns: err_msg = Logger.filter_text( "Execution of '{0}' returned {1}. {2}".format( command_alias, code, all_output)) raise ExecutionFailed(err_msg, code, out, err) # if separate stderr is enabled (by default it's redirected to out) if stderr == subprocess32.PIPE: return code, out, err return code, out
out = fd_to_string[proc.stdout].strip('\n') err = fd_to_string[proc.stderr].strip('\n') all_output = all_output.strip('\n') if timeout: if not timeout_event.is_set(): t.cancel() # timeout occurred else: err_msg = "Execution of '{0}' was killed due timeout after {1} seconds".format(command, timeout) raise ExecuteTimeoutException(err_msg) code = proc.returncode if throw_on_failure and code: err_msg = Logger.filter_text("Execution of '{0}' returned {1}. {2}".format(command_alias, code, all_output)) raise Fail(err_msg) # if separate stderr is enabled (by default it's redirected to out) if stderr == subprocess.PIPE: return code, out, err return code, out def as_sudo(command, env=None, auto_escape=True): """ command - list or tuple of arguments. env - when run as part of Execute resource, this SHOULD NOT be used. It automatically gets replaced later by call, checked_call. This should be used in not_if, only_if """ if isinstance(command, (list, tuple)):
os.close(master_fd) proc.wait() out = out.strip('\n') if timeout: if not timeout_event.is_set(): t.cancel() # timeout occurred else: raise ExecuteTimeoutException() code = proc.returncode if throw_on_failure and code: err_msg = Logger.filter_text(("Execution of '%s' returned %d. %s") % (command_alias, code, out)) raise Fail(err_msg) return code, out def as_sudo(command, env=None, auto_escape=True): """ command - list or tuple of arguments. env - when run as part of Execute resource, this SHOULD NOT be used. It automatically gets replaced later by call, checked_call. This should be used in not_if, only_if """ if isinstance(command, (list, tuple)): command = string_cmd_from_args_list(command, auto_escape=auto_escape) else: # Since ambari user sudoer privileges may be restricted, # without having /bin/bash permission, and /bin/su permission.