Current Path :
/
lib
/
python2.7
/
site-packages
/
redhat_support_tool
/
helpers
/
Or
Select Your Path :
Upload File :
New :
File
Dir
//lib/python2.7/site-packages/redhat_support_tool/helpers/common.py
# -*- coding: utf-8 -*- # # Copyright (c) 2012 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ''' A helper module with various global utility functions. ''' from redhat_support_lib.infrastructure.errors import RequestError, \ ConnectionError from redhat_support_tool.helpers.soscleaner import SOSCleaner from redhat_support_tool.helpers.confighelper import EmptyValueError, _ import dateutil.parser as parser import dateutil.tz as tz import inspect import os import os.path import redhat_support_tool.helpers.apihelper as apihelper import redhat_support_tool.helpers.confighelper as confighelper import re import logging import struct import sys import tempfile import textwrap import socket import json # To support pagination/obtaining terminal sizes _terminfosupport = True try: import fcntl # @UnresolvedImport (PyDev can't always find it) import termios except: _terminfosupport = False _sha256support = False try: from hashlib import sha256 _sha256support = True except: import sha __author__ = 'Keith Robertson <kroberts@redhat.com>' _interactive = True _plugins = None logger = logging.getLogger("redhat_support_tool.helpers.common") def is_interactive(): ''' Is redhat-support-tool being run as an interactive shell? Example of interactive: Welcome to the Red Hat Support Tool. Command (? for help): Example of non-interactive: redhat-support-tool addcomment -c 123456 Here are the logs... ''' return _interactive def set_interactive(boolean): ''' Set the operating mode. ''' global _interactive _interactive = boolean def set_plugin_dict(plugins): ''' Save the dictionary of plugins ''' global _plugins _plugins = plugins def get_plugin_dict(): ''' Get the dictionary of loaded plugins ''' return _plugins def iso8601tolocal(iso8601): ''' Given an ISO8601 datetime, convert to local. Returns: Empty string if there is a conversion error. ''' if iso8601: try: return parser.parse(iso8601).astimezone( tz.tzlocal()).strftime("%a %b %d %H:%M:%S %Z %Y") except: return '' else: return '' def get_products(): ''' A utility function to get the available products from the API. ''' api = None try: api = apihelper.get_api() productsAry = api.products.list() return productsAry except EmptyValueError, eve: msg = _('ERROR: %s') % str(eve) print msg logger.log(logging.WARNING, msg) raise except RequestError, rerr: msg = _('Unable to fulfill request with support services API. ' 'Reason: %s') % rerr.reason print msg logger.log(logging.WARNING, msg) if rerr.reason == "Unauthorized": remove_user() raise except ConnectionError: msg = _('Problem connecting to the support services ' 'API. Is the service accessible from this host?') print msg logger.log(logging.WARNING, msg) raise except: msg = _("Unable to find products") print msg logger.log(logging.WARNING, msg) raise def print_versions(versionsAry): try: for index, val in enumerate(versionsAry): print ' %-3s %-75s' % (index + 1, val) except: msg = _('ERROR: problem parsing the versions.') print msg logger.log(logging.WARNING, msg) raise def print_products(productsAry): try: for index, val in enumerate(productsAry): print ' %-3s %-75s' % (index + 1, val.get_name()) except: # TODO: log this msg = _('ERROR: problem parsing the products.') print msg logger.log(logging.WARNING, msg) raise def get_types(): ''' A utility function to get the available type from the API. ''' api = None try: api = apihelper.get_api() typeAry = api.values.getType() return typeAry except EmptyValueError, eve: msg = _('ERROR: %s') % str(eve) print msg logger.log(logging.WARNING, msg) raise eve except RequestError, re: msg = _('Unable to fulfill request with support services API. ' 'Reason: %s') % re.reason print msg logger.log(logging.WARNING, msg) if re.reason == "Unauthorized": remove_user() raise except ConnectionError: msg = _('Problem connecting to the support services ' 'API. Is the service accessible from this host?') print msg logger.log(logging.WARNING, msg) raise except: msg = _("Unable to find type") print msg logger.log(logging.WARNING, msg) raise def print_types(typesAry): try: for index, val in enumerate(typesAry): print ' %-3s %-75s' % (index + 1, val) except: msg = _('ERROR: problem parsing the types.') print msg logger.log(logging.WARNING, msg) raise def get_severities(): ''' A utility function to get the available severities from the API. ''' api = None try: api = apihelper.get_api() severitiesAry = api.values.getSeverity() return severitiesAry except EmptyValueError, eve: msg = _('ERROR: %s') % str(eve) print msg logger.log(logging.WARNING, msg) raise except RequestError, re: msg = _('Unable to fulfill request with support services API. ' 'Reason: %s') % re.reason print msg logger.log(logging.WARNING, msg) if re.reason == "Unauthorized": remove_user() raise except ConnectionError: msg = _('Problem connecting to the support services ' 'API. Is the service accessible from this host?') print msg logger.log(logging.WARNING, msg) raise except: msg = _("Unable to find severities") print msg logger.log(logging.WARNING, msg) raise def print_severities(severitiesAry): regex = re.compile("\((.+?)\)") try: for index, val in enumerate(severitiesAry): r = regex.search(val) print ' %-3s %-75s' % (index + 1, str(r.groups()[0]).strip()) except: msg = _('ERROR: problem parsing the severities.') print msg logger.log(logging.WARNING, msg) raise def get_statuses(): ''' A utility function to get the available statuses from the API. ''' api = None try: api = apihelper.get_api() statusesAry = api.values.getStatus() return statusesAry except EmptyValueError, eve: msg = _('ERROR: %s') % str(eve) print msg logger.log(logging.WARNING, msg) raise except RequestError, re: msg = _('Unable to fulfill request with support services API. ' 'Reason: %s') % re.reason print msg logger.log(logging.WARNING, msg) if re.reason == "Unauthorized": remove_user() raise except ConnectionError: msg = _('Problem connecting to the support services ' 'API. Is the service accessible from this host?') print msg logger.log(logging.WARNING, msg) raise except: msg = _("Unable to find statuses") print msg logger.log(logging.WARNING, msg) raise def get_internal_statuses(): ''' A utility function to get the available internal statuses from the API. ''' api = None try: api = apihelper.get_api() internalStatusesAry = api.values.getInternalStatus() return internalStatusesAry except EmptyValueError, eve: msg = _('ERROR: %s') % str(eve) print msg logger.log(logging.WARNING, msg) raise except RequestError, re: msg = _('Unable to fulfill request with support services API. ' 'Reason: %s') % re.reason print msg logger.log(logging.WARNING, msg) if re.reason == "Unauthorized": remove_user() raise except ConnectionError: msg = _('Problem connecting to the support services ' 'API. Is the service accessible from this host?') print msg logger.log(logging.WARNING, msg) raise except: msg = _("Unable to find internal statuses") print msg logger.log(logging.WARNING, msg) raise def print_statuses(statusesAry): try: for index, val in enumerate(statusesAry): print ' %-3s %-75s' % (index + 1, val) except: msg = _('ERROR: problem parsing the statuses.') print msg logger.log(logging.WARNING, msg) raise def get_groups(): ''' A utility function to get the available groups from the API. ''' api = None try: api = apihelper.get_api() groupsAry = api.groups.list() return groupsAry except EmptyValueError, eve: msg = _('ERROR: %s') % str(eve) print msg logger.log(logging.WARNING, msg) raise except RequestError, re: msg = _('Unable to fulfill request with support services API. ' 'Reason: %s') % re.reason print msg logger.log(logging.WARNING, msg) if re.reason == "Unauthorized": remove_user() raise except ConnectionError: msg = _('Problem connecting to the support services ' 'API. Is the service accessible from this host?') print msg logger.log(logging.WARNING, msg) raise except: msg = _("Unable to find groups") print msg logger.log(logging.WARNING, msg) raise def print_groups(groupsAry): try: for index, val in enumerate(groupsAry): print ' %-3s %-75s' % (index + 1, val.get_name()) except: msg = _('ERROR: problem parsing the groups.') print msg logger.log(logging.WARNING, msg) raise def get_terminfo(): ''' A utility function to return information about the terminal as a tuple returns: - tuple consisting of (height, width, xpixels, ypixels); or - None if not interactive, or output is not a tty Note: xpixels and ypixels is unused in the kernel, and will return 0 ''' ret = None if is_interactive() and _terminfosupport and sys.stdout.isatty(): # Get current window size via TIOCGWINSZ method # TIOCGWINSZ returns 4 unsigned short values (8 bytes) tiocgwinsz_fmt = '4H' rawwinsize = fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, ' ' * 8) ret = struct.unpack(tiocgwinsz_fmt, rawwinsize) return ret def set_docstring(doctext): def docstring(function): function.__doc__ = doctext return function return docstring def do_help(clsobj): if is_interactive(): HEADER = _("Documented commands (type help <topic>):") else: HEADER = _("Documented commands (<topic> -h,--help):") ruler = '=' help_dict = {} funcs = dir(clsobj.__class__) terminfo = get_terminfo() if terminfo: termwidth = terminfo[1] else: termwidth = 80 if '_get_plugins' in funcs: plugin_dict = clsobj._get_plugins() for plugin in plugin_dict: usage = plugin_dict[plugin].get_desc() usage = str(usage).replace('%prog', plugin_dict[plugin].get_name()) help_dict[plugin] = usage for func in funcs: if func[:5] == 'help_': item = getattr(clsobj, func) docstring = item.__doc__ if inspect.ismethod(item) and docstring: help_dict[func[5:]] = docstring longest_cmd = max(len(k) for k in help_dict.keys()) print print HEADER print ("%s" % str(ruler * len(HEADER))) tuple_ary = sorted(help_dict.iteritems()) for tpl in tuple_ary: output = u'%-*s %-s' % (longest_cmd, tpl[0], tpl[1]) output_wrapped = textwrap.wrap(output, termwidth, subsequent_indent=' ' * (longest_cmd + 1)) for line in output_wrapped: print line def get_linecount(width, most=True, *args): longest_line = None for line in args: linelen = len(line) (div, mod) = linelen / width, linelen % width if mod > 0: div = div + 1 if ((longest_line is None) or (div > longest_line and most is True) or (div < longest_line and most is False)): longest_line = div return longest_line def split_file(file_path, chunk_size): chunk_num = 1 chunks = [] file_name = os.path.basename(file_path) file_size = os.stat(file_path).st_size tempdir = tempfile.mkdtemp(suffix=".rhst") tempdir_statvfs = os.statvfs(tempdir) tempdir_free = tempdir_statvfs.f_bavail * tempdir_statvfs.f_frsize if (tempdir_free < file_size): print _('Not enough space available in /tmp to split %s') % (file_name) while True: line = raw_input(_('Please provide an alternative location to' ' split the file: ')) line = str(line).strip() tempdir = os.path.expanduser(line) try: os.mkdir(tempdir) tempdir_statvfs = os.statvfs(tempdir) tempdir_free = (tempdir_statvfs.f_bavail * tempdir_statvfs.f_frsize) if (tempdir_free < file_size): print _('Not enough space available in %s, %d bytes' ' required') % (tempdir, file_size) else: continue except OSError: print _('Unable to create directory at %s') % (tempdir) in_file = open(file_path) while True: msg = '' shasum = None if _sha256support: shasum = sha256() msg = _('SHA256: %s') else: shasum = sha.new() msg = _('SHA1: %s') data = in_file.read(chunk_size) if not data: break out_filename = os.path.join(tempdir, "%s.%03d" % (file_name, chunk_num)) out_file = open(out_filename, 'w') out_file.write(data) shasum.update(data) chunks.append({'file': out_filename, 'msg': msg % (shasum.hexdigest())}) chunk_num += 1 return chunks def ssl_check(): '''Check SSL configuration options Will print warnings for various potentially unsafe situations as a means to alert of possible Man-in-the-Middle vectors, or SSL communication is likely to fail.''' # Import confighelper - we need to know how various bits are configured. cfg = confighelper.get_config_helper() if cfg.get(option='no_verify_ssl'): # Unsafe/Not Recommended. Warn user, suggest loading the CA cert for # the proxy/server msg = _('Warning: no_ssl_verify is enabled in the Red Hat Support Tool' ' configuration, this may allow other servers to intercept' ' communications with Red Hat. If you have a transparent SSL' ' proxy server, you can trust it\'s Certificate Authority' ' using: \'config ssl_ca <filepath>\'') logging.warn(msg) print msg elif (cfg.get(option='ssl_ca') and not os.access(cfg.get(option='ssl_ca'), os.R_OK)): # Customer has configured a path to a CA certificate to trust, but we # can't find or access the Certificate Authority file that we will pass # to the API. It's not a failure, but we should warn, in case they do # use Red Hat APIs msg = _('Warning: Red Hat Support Tool is unable to access the' ' designated Certificate Authority certificate for server' ' verification at %s. Please correct/replace this file,' ' otherwise functionality may be limited.') logging.warn(msg) print msg return def get_friendly_file_length(file_len): ''' A utility function to return a more human friendly file length string. ''' len_str = "%s bytes" % file_len if file_len >= 1024*1024: len_str += " (%s MB)" % (file_len/1024/1024) elif file_len >= 1024: len_str += " (%s KB)" % (file_len/1024) return len_str def str_to_bool(s): ''' Converts a string representing a boolean to an actual boolean ''' if isinstance(s, bool): return s if not isinstance(s, str): return None s = s.strip().lower() if s in ['false', 'f', 'no', 'n', '0']: return False if s in ['true', 't', 'yes', 'y', '1']: return True return None def attachment_exists_locally(filepath, filesize): """Checks if the attachment (in filepath) already exists locally before downloading it""" try: s = os.stat(filepath) if s.st_size == filesize: print _('Skipping %s, file already exists' % os.path.realpath(filepath)) # Download exists and finished return True # Partial download, cleanup os.unlink(filepath) except OSError: pass return False def remove_user(): """ BZ1273976 - clear incorrect username/password """ cfg = confighelper.get_config_helper() val = cfg.get_from_prompt(_("Clear RHN user ID '%s' from the redhat-support-tool cache (y/[n]): " % cfg.get(option='user')), 'n') if str(val).strip().lower() == 'y': cfg.remove_option(option='user') cfg.remove_option(option='password') # necessary so we will be prompted for user information confighelper._prompted_for_userid_save = False confighelper._prompted_for_password_save = False # necessary so we make an API object again and get the user information again apihelper.disconnect_api() def obfuscate_file(file_name): cleaner = SOSCleaner(quiet=True) cleaner.dn_db = get_soscleaner_domains() # very crude way of determining if the file is a sosreport! if 'sosreport' in file_name.lower(): print _("Obfuscating sosreport file: %s" % file_name) options = CleanerOptions() result = cleaner.clean_report(options, file_name) else: print _("Obfuscating non-sosreport file: %s" % file_name) options = CleanerOptions(file_name=file_name) result = cleaner.clean_report(options, None) # the obfuscated filename is in result[0] return result[0] def obfuscate_text(text): cleaner = SOSCleaner(quiet=True) cleaner.dn_db = get_soscleaner_domains() cleaner.logger = logger # need to do this otherwise an exception occurs return cleaner._clean_line(text) class CleanerOptions(object): def __init__(self, report_dir='/tmp', file_name=None): self.report_dir = report_dir self.domains = [] self.files = [] if file_name: self.files.append(file_name) self.quiet = True self.keyword_file = None self.keywords = None def get_soscleaner_domains(): """ Set the dn_db property of soscleaner if necessary """ cfg = confighelper.get_config_helper() if not cfg.has_option(option='dn_db'): dn_db = {} domains = [] if cfg.has_option(option='domains'): # get the domain from the redhat-support-tool config domains = [d.strip() for d in cfg.get(option='domains').split(',')] else: # else try to get the domain from the hostname or machine's DNS name hostname = '' try: hostname = socket.gethostname() if '.' not in hostname: # hostname doesn't contain domain, try DNS lookup of IP s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.settimeout(1) s.connect((cfg.get(option='url').split('/')[-1], 443)) hostname = socket.gethostbyaddr(s.getsockname()[0]) s.close() except: pass if '.' in hostname: domains = [hostname.split('.', 1)[1]] if domains: if len(domains) > 1: for i, d in enumerate(domains): dn_db['example%d.com' % i] = d else: dn_db['example.com'] = domains[0] cfg.set(option='dn_db', value=json.dumps(dn_db)) if dn_db: msg = _('soscleaner domains: %s' % json.dumps(dn_db)) else: msg = _('No domains configured/detected for soscleaner, hostname ' 'obfuscation will not work') logger.log(logging.INFO, msg) return json.loads(cfg.get(option='dn_db'))