mirror of
https://github.com/munki/munki.git
synced 2026-05-01 17:59:56 -05:00
Make all munkicommon.display_* methods accept a message and optional arguments
to concatenate to the message (using Python concat ala logging.* methods); drop unicode chars. Fix problem (line 338 of updatecheck.py) where attempting to string-concat unicode characters before sending to munkicommon.display_debug1() causes UnicodeDecodeError. git-svn-id: http://munki.googlecode.com/svn/trunk@921 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
@@ -34,6 +34,7 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
import urllib2
|
||||
import warnings
|
||||
from distutils import version
|
||||
from xml.dom import minidom
|
||||
from Foundation import NSDate
|
||||
@@ -133,11 +134,39 @@ def display_percent_done(current, maximum):
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def display_status(msg):
|
||||
def str_to_ascii(s):
|
||||
"""Given str (unicode, latin-1, or not) return ascii.
|
||||
|
||||
Args:
|
||||
s: str, likely in Unicode-16BE, UTF-8, or Latin-1 charset
|
||||
Returns:
|
||||
str, ascii form, no >7bit chars
|
||||
"""
|
||||
try:
|
||||
return unicode(s).encode('ascii', 'ignore')
|
||||
except UnicodeDecodeError:
|
||||
return s.decode('ascii', 'ignore')
|
||||
|
||||
|
||||
def concat_log_message(msg, *args):
|
||||
"""Concatenates a string with any additional arguments; drops unicode."""
|
||||
if args:
|
||||
args = [str_to_ascii(arg) for arg in args]
|
||||
try:
|
||||
msg = msg % tuple(args)
|
||||
except TypeError, e:
|
||||
warnings.warn(
|
||||
'String format does not match concat args: %s' % (
|
||||
str(sys.exc_info())))
|
||||
return msg
|
||||
|
||||
|
||||
def display_status(msg, *args):
|
||||
"""
|
||||
Displays major status messages, formatting as needed
|
||||
for verbose/non-verbose and munkistatus-style output.
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
log(msg)
|
||||
if munkistatusoutput:
|
||||
munkistatus.detail(msg)
|
||||
@@ -149,11 +178,12 @@ def display_status(msg):
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def display_info(msg):
|
||||
def display_info(msg, *args):
|
||||
"""
|
||||
Displays info messages.
|
||||
Not displayed in MunkiStatus.
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
log(msg)
|
||||
if munkistatusoutput:
|
||||
pass
|
||||
@@ -162,13 +192,14 @@ def display_info(msg):
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def display_detail(msg):
|
||||
def display_detail(msg, *args):
|
||||
"""
|
||||
Displays minor info messages, formatting as needed
|
||||
for verbose/non-verbose and munkistatus-style output.
|
||||
These are usually logged only, but can be printed to
|
||||
stdout if verbose is set to 2 or higher
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
if munkistatusoutput:
|
||||
pass
|
||||
elif verbose > 1:
|
||||
@@ -178,11 +209,12 @@ def display_detail(msg):
|
||||
log(msg)
|
||||
|
||||
|
||||
def display_debug1(msg):
|
||||
def display_debug1(msg, *args):
|
||||
"""
|
||||
Displays debug messages, formatting as needed
|
||||
for verbose/non-verbose and munkistatus-style output.
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
if munkistatusoutput:
|
||||
pass
|
||||
elif verbose > 2:
|
||||
@@ -192,11 +224,12 @@ def display_debug1(msg):
|
||||
log('DEBUG1: %s' % msg)
|
||||
|
||||
|
||||
def display_debug2(msg):
|
||||
def display_debug2(msg, *args):
|
||||
"""
|
||||
Displays debug messages, formatting as needed
|
||||
for verbose/non-verbose and munkistatus-style output.
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
if munkistatusoutput:
|
||||
pass
|
||||
elif verbose > 3:
|
||||
@@ -213,10 +246,11 @@ def reset_warnings():
|
||||
rotatelog(warningsfile)
|
||||
|
||||
|
||||
def display_warning(msg):
|
||||
def display_warning(msg, *args):
|
||||
"""
|
||||
Prints warning msgs to stderr and the log
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
warning = 'WARNING: %s' % msg
|
||||
print >> sys.stderr, warning.encode('UTF-8')
|
||||
log(warning)
|
||||
@@ -233,10 +267,11 @@ def reset_errors():
|
||||
rotatelog(errorsfile)
|
||||
|
||||
|
||||
def display_error(msg):
|
||||
def display_error(msg, *args):
|
||||
"""
|
||||
Prints msg to stderr and the log
|
||||
"""
|
||||
msg = concat_log_message(msg, *args)
|
||||
errmsg = 'ERROR: %s' % msg
|
||||
print >> sys.stderr, errmsg.encode('UTF-8')
|
||||
log(errmsg)
|
||||
@@ -247,7 +282,7 @@ def display_error(msg):
|
||||
|
||||
|
||||
def format_time(timestamp=None):
|
||||
"""Return timestamp as an ISO 8601 formatted string, in the current
|
||||
"""Return timestamp as an ISO 8601 formatted string, in the current
|
||||
timezone.
|
||||
If timestamp isn't given the current time is used."""
|
||||
if timestamp is None:
|
||||
@@ -710,10 +745,10 @@ def getInstallerPkgInfo(filename):
|
||||
stderr=subprocess.PIPE)
|
||||
(out, err) = proc.communicate()
|
||||
if proc.returncode:
|
||||
display_error("installer -query failed: %s %s" %
|
||||
display_error("installer -query failed: %s %s" %
|
||||
(out.decode('UTF-8'), err.decode('UTF-8')))
|
||||
return None
|
||||
|
||||
|
||||
if out:
|
||||
restartAction = str(out).rstrip('\n')
|
||||
if restartAction != 'None':
|
||||
@@ -1449,7 +1484,7 @@ def getAppData():
|
||||
except Exception:
|
||||
pass
|
||||
return APPDATA
|
||||
|
||||
|
||||
|
||||
def getRunningProcesses():
|
||||
"""Returns a list of paths of running processes"""
|
||||
@@ -1471,7 +1506,7 @@ def getRunningProcesses():
|
||||
stderr=subprocess.PIPE)
|
||||
(output, unused_err) = proc.communicate()
|
||||
if proc.returncode == 0:
|
||||
carbon_apps = [item[len(LaunchCFMApp)+1:]
|
||||
carbon_apps = [item[len(LaunchCFMApp)+1:]
|
||||
for item in output.splitlines()
|
||||
if item.startswith(LaunchCFMApp)]
|
||||
if carbon_apps:
|
||||
@@ -1484,7 +1519,7 @@ def getRunningProcesses():
|
||||
# some utility functions
|
||||
|
||||
def isAppRunning(appname):
|
||||
"""Tries to determine if the application in appname is currently
|
||||
"""Tries to determine if the application in appname is currently
|
||||
running"""
|
||||
display_detail('Checking if %s is running...' % appname)
|
||||
proc_list = getRunningProcesses()
|
||||
@@ -1501,7 +1536,7 @@ def isAppRunning(appname):
|
||||
# try adding '.app' to the name and check again
|
||||
matching_items = [item for item in proc_list
|
||||
if '/'+ appname + '.app/' in item]
|
||||
|
||||
|
||||
if matching_items:
|
||||
# it's running!
|
||||
display_debug1('Matching process list: %s' % matching_items)
|
||||
|
||||
@@ -54,7 +54,7 @@ def makeCatalogDB(catalogitems):
|
||||
|
||||
if name == 'NO NAME' or vers == 'NO VERSION':
|
||||
munkicommon.display_warning('Bad pkginfo: %s' % item)
|
||||
|
||||
|
||||
# normalize the version number
|
||||
vers = munkicommon.padVersionString(vers, 5)
|
||||
|
||||
@@ -333,9 +333,9 @@ def compareApplicationVersion(app):
|
||||
if 'path' in item:
|
||||
if item['path'].startswith('/Users/') and \
|
||||
not item['path'].startswith('/Users/Shared/'):
|
||||
munkicommon.display_debug2(('Skipped '
|
||||
'app %s with path %s') % (
|
||||
item['name'], item['path']))
|
||||
munkicommon.display_debug2(
|
||||
'Skipped app %s with path %s',
|
||||
item['name'], item['path'])
|
||||
continue
|
||||
if bundleid and item['bundleid'] == bundleid:
|
||||
appinfo.append(item)
|
||||
@@ -622,15 +622,15 @@ class PackageVerificationError(MunkiDownloadError):
|
||||
pass
|
||||
|
||||
def download_installeritem(item_pl, uninstalling=False):
|
||||
"""Downloads an (un)installer item.
|
||||
"""Downloads an (un)installer item.
|
||||
Raises an error if there are issues..."""
|
||||
|
||||
|
||||
download_item_key = 'installer_item_location'
|
||||
item_hash_key = 'installer_item_hash'
|
||||
if uninstalling and 'uninstaller_item_location' in item_pl:
|
||||
download_item_key = 'uninstaller_item_location'
|
||||
item_hash_key = 'uninstaller_item_hash'
|
||||
|
||||
|
||||
location = item_pl.get(download_item_key)
|
||||
if not location:
|
||||
raise MunkiDownloadError("No %s in item info." % download_item_key)
|
||||
@@ -682,7 +682,7 @@ def isItemInInstallInfo(manifestitem_pl, thelist, vers=''):
|
||||
"""
|
||||
for item in thelist:
|
||||
try:
|
||||
if (item.get('name', item['manifestitem']) ==
|
||||
if (item.get('name', item['manifestitem']) ==
|
||||
manifestitem_pl['name']):
|
||||
if not vers:
|
||||
return True
|
||||
@@ -898,11 +898,11 @@ def getItemDetail(name, cataloglist, vers=''):
|
||||
if rejected_items:
|
||||
for reason in rejected_items:
|
||||
munkicommon.display_warning(reason)
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def enoughDiskSpace(manifestitem_pl, installlist=None,
|
||||
def enoughDiskSpace(manifestitem_pl, installlist=None,
|
||||
uninstalling=False, warn=True):
|
||||
"""Determine if there is enough disk space to download the manifestitem."""
|
||||
# fudgefactor is set to 100MB
|
||||
@@ -1223,7 +1223,7 @@ def processManagedUpdate(manifestitem, cataloglist, installinfo):
|
||||
manifestitemname = os.path.split(manifestitem)[1]
|
||||
munkicommon.display_debug1(
|
||||
'* Processing manifest item %s for update' % manifestitemname)
|
||||
|
||||
|
||||
item_pl = getItemDetail(manifestitem, cataloglist)
|
||||
|
||||
if not item_pl:
|
||||
@@ -1277,7 +1277,7 @@ def processOptionalInstall(manifestitem, cataloglist, installinfo):
|
||||
'No pkginfo for %s found in catalogs: %s' %
|
||||
(manifestitem, ', '.join(cataloglist)))
|
||||
return
|
||||
|
||||
|
||||
# check to see if item (any version) is already in the
|
||||
# optional_install list:
|
||||
for item in installinfo['optional_installs']:
|
||||
@@ -1291,7 +1291,7 @@ def processOptionalInstall(manifestitem, cataloglist, installinfo):
|
||||
# unless it was added because it's a managed_update
|
||||
if not manifestitemname in installinfo['managed_updates']:
|
||||
munkicommon.display_debug1(
|
||||
'%s has already been processed for install.' %
|
||||
'%s has already been processed for install.' %
|
||||
manifestitemname)
|
||||
return
|
||||
# check to see if item (any version) is already in the removallist:
|
||||
@@ -1322,7 +1322,7 @@ def processOptionalInstall(manifestitem, cataloglist, installinfo):
|
||||
warn=False):
|
||||
iteminfo['note'] = \
|
||||
'Insufficient disk space to download and install.'
|
||||
|
||||
|
||||
munkicommon.display_debug1(
|
||||
"Adding %s to the optional install list" % iteminfo['name'])
|
||||
installinfo['optional_installs'].append(iteminfo)
|
||||
@@ -1415,7 +1415,7 @@ def processInstall(manifestitem, cataloglist, installinfo):
|
||||
iteminfo['installer_item_size'] = item_pl.get('installer_item_size', 0)
|
||||
iteminfo['installed_size'] = item_pl.get('installer_item_size',
|
||||
iteminfo['installer_item_size'])
|
||||
|
||||
|
||||
# currently we will ignore the forced_install and forced_uninstall key if
|
||||
# the item is part of a dependency graph or needs a restart or logout...
|
||||
if (not item_pl.get('requires') and not item_pl.get('update_for') and
|
||||
@@ -1526,9 +1526,9 @@ def processManifestForKey(manifestpath, manifest_key, installinfo,
|
||||
Probably doesn't handle circular manifest references well.
|
||||
"""
|
||||
munkicommon.display_debug1(
|
||||
"** Processing manifest %s for %s" %
|
||||
"** Processing manifest %s for %s" %
|
||||
(os.path.basename(manifestpath), manifest_key))
|
||||
|
||||
|
||||
cataloglist = getManifestValueForKey(manifestpath, 'catalogs')
|
||||
if cataloglist:
|
||||
getCatalogs(cataloglist)
|
||||
@@ -1599,9 +1599,9 @@ def processRemoval(manifestitem, cataloglist, installinfo):
|
||||
"""
|
||||
manifestitemname_withversion = os.path.split(manifestitem)[1]
|
||||
munkicommon.display_debug1(
|
||||
'* Processing manifest item %s for removal' %
|
||||
'* Processing manifest item %s for removal' %
|
||||
manifestitemname_withversion)
|
||||
|
||||
|
||||
(manifestitemname, includedversion) = nameAndVersion(
|
||||
manifestitemname_withversion)
|
||||
infoitems = []
|
||||
@@ -1748,21 +1748,21 @@ def processRemoval(manifestitem, cataloglist, installinfo):
|
||||
iteminfo['display_name'] = uninstall_item.get('display_name', '')
|
||||
iteminfo['manifestitem'] = manifestitemname_withversion
|
||||
iteminfo['description'] = 'Will be removed.'
|
||||
|
||||
|
||||
# currently we will ignore the forced_install and forced_uninstall key if
|
||||
# the item is part of a dependency graph or needs a restart or logout...
|
||||
if (not uninstall_item.get('requires')
|
||||
and not uninstall_item.get('update_for')
|
||||
if (not uninstall_item.get('requires')
|
||||
and not uninstall_item.get('update_for')
|
||||
and not uninstall_item.get('RestartAction')):
|
||||
iteminfo['forced_uninstall'] = uninstall_item.get(
|
||||
'forced_uninstall', False)
|
||||
|
||||
|
||||
if 'blocking_applications' in uninstall_item:
|
||||
iteminfo['blocking_applications'] = \
|
||||
uninstall_item['blocking_applications']
|
||||
if 'installs' in uninstall_item:
|
||||
iteminfo['installs'] = uninstall_item['installs']
|
||||
|
||||
|
||||
if packagesToRemove:
|
||||
# remove references for each package
|
||||
packagesToReallyRemove = []
|
||||
|
||||
Reference in New Issue
Block a user