mirror of
https://github.com/munki/munki.git
synced 2026-04-22 04:28:21 -05:00
Lots of logging and reporting changes.
Added munkicommon.validPlist() to check for plist validity when getting a manifest or catalog file. Added call to updatecheck.checkServer() to test for server availability before doing a check for updates. git-svn-id: http://munki.googlecode.com/svn/trunk@319 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
@@ -245,7 +245,7 @@ def runAdobeSetup(dmgpath):
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
munkicommon.display_error("***Adobe Setup error: %s: %s***" % (retcode, adobeSetupError(retcode)))
|
||||
munkicommon.display_error("Adobe Setup error: %s: %s" % (retcode, adobeSetupError(retcode)))
|
||||
else:
|
||||
munkicommon.display_error("%s doesn't appear to contain an Adobe CS4 update." % os.path.basename(dmgpath))
|
||||
retcode = -1
|
||||
@@ -304,21 +304,21 @@ def runAdobeUberTool(dmgpath, pkgname='', uninstalling=False):
|
||||
payloadpath = loginfo[26:]
|
||||
payloadfilename = os.path.basename(payloadpath)
|
||||
payloadname = os.path.splitext(payloadfilename)[0]
|
||||
munkicommon.display_status("Installing payload: %s" % payloadname)
|
||||
munkicommon.display_status("Installing payload: %s (%s of %s)" % (payloadname, payload_completed_count, number_of_payloads))
|
||||
except:
|
||||
pass
|
||||
# uninstalling
|
||||
if loginfo.startswith("Physical payload uninstall result"):
|
||||
# increment payload_completed_count
|
||||
payload_completed_count = payload_completed_count + 1
|
||||
munkicommon.display_status("Removed Adobe payload %s" % payload_completed_count)
|
||||
munkicommon.display_status("Removed Adobe payload %s of %s" % (payload_completed_count, number_of_payloads))
|
||||
if munkicommon.munkistatusoutput:
|
||||
munkistatus.percent(getPercent(payload_completed_count, number_of_payloads))
|
||||
|
||||
# ubertool completed
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
munkicommon.display_error("***Adobe Setup error: %s: %s***" % (retcode, adobeSetupError(retcode)))
|
||||
munkicommon.display_error("Adobe Setup error: %s: %s" % (retcode, adobeSetupError(retcode)))
|
||||
else:
|
||||
munkicommon.display_error("No %s found" % ubertool)
|
||||
retcode = -1
|
||||
|
||||
@@ -49,7 +49,7 @@ def selectSoftwareUpdateServer():
|
||||
oldsusserver = out.rstrip('\n')
|
||||
|
||||
cmd = ['/usr/bin/defaults', 'write', '/Library/Preferences/com.apple.SoftwareUpdate',
|
||||
'CatalogURL', munkilib.pref('SoftwareUpdateServerURL')]
|
||||
'CatalogURL', munkicommon.pref('SoftwareUpdateServerURL')]
|
||||
retcode = subprocess.call(cmd)
|
||||
|
||||
|
||||
@@ -203,7 +203,12 @@ def kickOffUpdatesAndRestart():
|
||||
# need to restart loginwindow so it notices the change
|
||||
cmd = [ '/usr/bin/killall', 'loginwindow' ]
|
||||
retcode = subprocess.call(cmd)
|
||||
# and now we can remove the AccessibilityAPIFile
|
||||
# argh! big problem. killing loginwindow also kills us if we're
|
||||
# running as a LaunchAgent in the LoginWindow context
|
||||
# We'll get relaunched, but then we lose our place in the code
|
||||
# and have to start over.
|
||||
|
||||
# now we can remove the AccessibilityAPIFile
|
||||
os.unlink(AccessibilityAPIFile)
|
||||
|
||||
# before we kick off the update, leave a trigger file so munki will install stuff
|
||||
@@ -229,8 +234,7 @@ def kickOffUpdatesAndRestart():
|
||||
while True:
|
||||
line = p.stdout.readline()
|
||||
if not line and (p.poll() != None):
|
||||
break
|
||||
|
||||
break
|
||||
return
|
||||
else:
|
||||
# unsupported OS version
|
||||
@@ -446,11 +450,13 @@ def installAppleUpdates():
|
||||
appleupdatelist = []
|
||||
if appleupdatelist == []:
|
||||
# we don't have any updates in appleUpdatesFile,
|
||||
# or appleUpdatesFile is out-of-date, so check updatesindexfile
|
||||
# or appleUpdatesFile is out-of-date, so check updatesindexfile
|
||||
appleupdatelist = getSoftwareUpdateInfo()
|
||||
|
||||
# did we find some Apple updates?
|
||||
if appleupdatelist:
|
||||
munkicommon.report['AppleUpdateList'] = appleupdatelist
|
||||
munkicommon.savereport()
|
||||
try:
|
||||
# once we start, we should remove /Library/Updates/index.plist
|
||||
# because it will point to items we've already installed
|
||||
@@ -462,8 +468,10 @@ def installAppleUpdates():
|
||||
except:
|
||||
pass
|
||||
# now try to install the updates
|
||||
restartneeded = installer.installWithInfo("/Library/Updates", appleupdatelist, appleupdates=True)
|
||||
|
||||
restartneeded = installer.installWithInfo("/Library/Updates", appleupdatelist)
|
||||
if restartneeded:
|
||||
munkicommon.report['RestartRequired'] = True
|
||||
munkicommon.savereport()
|
||||
return restartneeded
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,13 @@ import FoundationPlist
|
||||
from removepackages import removepackages
|
||||
|
||||
|
||||
# initialize our report fields
|
||||
# we do this here because appleupdates.installAppleUpdates()
|
||||
# calls installWithInfo()
|
||||
munkicommon.report['InstallResults'] = []
|
||||
munkicommon.report['RemovalResults'] = []
|
||||
|
||||
|
||||
def install(pkgpath, choicesXMLpath=None):
|
||||
"""
|
||||
Uses the apple installer to install the package or metapackage
|
||||
@@ -102,12 +109,15 @@ def install(pkgpath, choicesXMLpath=None):
|
||||
print status.encode('UTF-8')
|
||||
sys.stdout.flush()
|
||||
elif msg.startswith("%"):
|
||||
percent = float(msg[1:])
|
||||
if osvers < 10:
|
||||
# Leopard uses a float from 0 to 1
|
||||
percent = int(percent * 100)
|
||||
if munkicommon.munkistatusoutput:
|
||||
percent = float(msg[1:])
|
||||
if osvers < 10:
|
||||
# Leopard uses a float from 0 to 1
|
||||
percent = int(percent * 100)
|
||||
munkistatus.percent(percent)
|
||||
munkistatus.percent(percent)
|
||||
else:
|
||||
print "%s percent complete" % percent
|
||||
sys.stdout.flush()
|
||||
elif msg.startswith(" Error"):
|
||||
if munkicommon.munkistatusoutput:
|
||||
munkistatus.detail(msg)
|
||||
@@ -218,16 +228,7 @@ def copyAppFromDMG(dmgpath):
|
||||
return -1
|
||||
|
||||
|
||||
def getInstallCount(installList):
|
||||
count = 0
|
||||
for item in installList:
|
||||
if 'installed' in item:
|
||||
if not item['installed']:
|
||||
count +=1
|
||||
return count
|
||||
|
||||
|
||||
def installWithInfo(dirpath, installlist, appleupdates=False):
|
||||
def installWithInfo(dirpath, installlist):
|
||||
"""
|
||||
Uses the installlist to install items in the
|
||||
correct order.
|
||||
@@ -306,9 +307,13 @@ def installWithInfo(dirpath, installlist, appleupdates=False):
|
||||
|
||||
# record install success/failure
|
||||
if retcode == 0:
|
||||
munkicommon.log("Install of %s-%s: SUCCESS" % (display_name, version_to_install), "Install.log")
|
||||
success_msg = "Install of %s-%s: SUCCESS" % (display_name, version_to_install)
|
||||
munkicommon.log(success_msg, "Install.log")
|
||||
munkicommon.report['InstallResults'].append(success_msg)
|
||||
else:
|
||||
munkicommon.log("Install of %s-%s: FAILED with return code: %s" % (display_name, version_to_install, retcode), "Install.log")
|
||||
failure_msg = "Install of %s-%s: FAILED with return code: %s" % (display_name, version_to_install, retcode)
|
||||
munkicommon.log(failure_msg, "Install.log")
|
||||
munkicommon.report['InstallResults'].append(failure_msg)
|
||||
|
||||
# check to see if this installer item is needed by any additional items in installinfo
|
||||
# this might happen if there are mulitple things being installed with choicesXML files
|
||||
@@ -333,15 +338,6 @@ def installWithInfo(dirpath, installlist, appleupdates=False):
|
||||
return restartflag
|
||||
|
||||
|
||||
def getRemovalCount(removalList):
|
||||
count = 0
|
||||
for item in removalList:
|
||||
if 'installed' in item:
|
||||
if item['installed']:
|
||||
count +=1
|
||||
return count
|
||||
|
||||
|
||||
def processRemovals(removallist):
|
||||
restartFlag = False
|
||||
index = 0
|
||||
@@ -452,17 +448,19 @@ def processRemovals(removallist):
|
||||
|
||||
# record removal success/failure
|
||||
if retcode == 0:
|
||||
munkicommon.log("Removal of %s: SUCCESS" % name, "Install.log")
|
||||
success_msg = "Removal of %s: SUCCESS" % name
|
||||
munkicommon.log(success_msg, "Install.log")
|
||||
munkicommon.report['RemovalResults'].append(success_msg)
|
||||
else:
|
||||
munkicommon.log("Removal of %s: FAILED with return code: %s" % (name, retcode), "Install.log")
|
||||
failure_msg = "Removal of %s: FAILED with return code: %s" % (name, retcode)
|
||||
munkicommon.log(failure_msg, "Install.log")
|
||||
munkicommon.report['RemovalResults'].append(failure_msg)
|
||||
|
||||
|
||||
return restartFlag
|
||||
|
||||
|
||||
|
||||
def run():
|
||||
|
||||
managedinstallbase = munkicommon.pref('ManagedInstallDir')
|
||||
installdir = os.path.join(managedinstallbase , 'Cache')
|
||||
|
||||
@@ -484,6 +482,7 @@ def run():
|
||||
if "removals" in pl:
|
||||
# filter list to items that need to be removed
|
||||
removallist = [item for item in pl['removals'] if item.get('installed')]
|
||||
munkicommon.report['ItemsToRemove'] = removallist
|
||||
if removallist:
|
||||
if munkicommon.munkistatusoutput:
|
||||
if len(removallist) == 1:
|
||||
@@ -498,6 +497,7 @@ def run():
|
||||
if not munkicommon.stopRequested():
|
||||
# filter list to items that need to be installed
|
||||
installlist = [item for item in pl['managed_installs'] if item.get('installed') == False]
|
||||
munkicommon.report['ItemsToInstall'] = installlist
|
||||
if installlist:
|
||||
if munkicommon.munkistatusoutput:
|
||||
if len(installlist) == 1:
|
||||
@@ -513,6 +513,7 @@ def run():
|
||||
munkicommon.log("No %s found." % installinfo)
|
||||
|
||||
munkicommon.log("### End managed installer session ###")
|
||||
munkicommon.savereport()
|
||||
|
||||
return (removals_need_restart or installs_need_restart)
|
||||
|
||||
@@ -119,12 +119,13 @@ def display_detail(msg):
|
||||
These are usually logged only, but can be printed to
|
||||
stdout if verbose is set to 2 or higher
|
||||
"""
|
||||
log(msg)
|
||||
if munkistatusoutput:
|
||||
pass
|
||||
elif verbose > 1:
|
||||
print msg.encode('UTF-8')
|
||||
sys.stdout.flush()
|
||||
if logginglevel > 0:
|
||||
log(msg)
|
||||
|
||||
|
||||
def display_debug1(msg):
|
||||
@@ -138,7 +139,7 @@ def display_debug1(msg):
|
||||
print msg.encode('UTF-8')
|
||||
sys.stdout.flush()
|
||||
if logginglevel > 1:
|
||||
log(msg)
|
||||
log("DEBUG1: %s" % msg)
|
||||
|
||||
|
||||
def display_debug2(msg):
|
||||
@@ -151,7 +152,7 @@ def display_debug2(msg):
|
||||
elif verbose > 3:
|
||||
print msg.encode('UTF-8')
|
||||
if logginglevel > 2:
|
||||
log(msg)
|
||||
log("DEBUG2: %s" % msg)
|
||||
|
||||
|
||||
def reset_warnings():
|
||||
@@ -170,6 +171,8 @@ def display_warning(msg):
|
||||
log(warning)
|
||||
# append this warning to our warnings log
|
||||
log(warning, "warnings.log")
|
||||
# collect the warning for later reporting
|
||||
report['Warnings'].append(msg)
|
||||
|
||||
|
||||
def reset_errors():
|
||||
@@ -189,10 +192,12 @@ def display_error(msg):
|
||||
# append this error to our errors log
|
||||
log(errmsg, "errors.log")
|
||||
# collect the errors for later reporting
|
||||
errors = errors + msg + '\n'
|
||||
report['Errors'].append(msg)
|
||||
|
||||
|
||||
def log(msg, logname=''):
|
||||
# date/time format string
|
||||
formatstr = "%b %d %H:%M:%S"
|
||||
if not logname:
|
||||
# use our regular logfile
|
||||
logpath = logfile
|
||||
@@ -200,7 +205,7 @@ def log(msg, logname=''):
|
||||
logpath = os.path.join(os.path.dirname(logfile), logname)
|
||||
try:
|
||||
f = open(logpath, mode='a', buffering=1)
|
||||
print >>f, time.ctime(), msg.encode('UTF-8')
|
||||
print >>f, time.strftime(formatstr), msg.encode('UTF-8')
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
@@ -234,8 +239,89 @@ def rotate_main_log():
|
||||
rotatelog(logfile)
|
||||
|
||||
|
||||
def printreportitem(label, value, indent=0):
|
||||
indentspace = " "
|
||||
if type(value) == type(None):
|
||||
print indentspace*indent, "%s: !NONE!" % label
|
||||
elif type(value) == list or type(value).__name__ == 'NSCFArray':
|
||||
if label:
|
||||
print indentspace*indent, "%s:" % label
|
||||
index = 0
|
||||
for item in value:
|
||||
index += 1
|
||||
printreportitem(index, item, indent+1)
|
||||
elif type(value) == dict or type(value).__name__ == 'NSCFDictionary':
|
||||
if label:
|
||||
print indentspace*indent, "%s:" % label
|
||||
for subkey in value.keys():
|
||||
printreportitem(subkey, value[subkey], indent+1)
|
||||
else:
|
||||
print indentspace*indent, "%s: %s" % (label, value)
|
||||
|
||||
|
||||
def printreport(reportdict):
|
||||
"""Prints the report dictionary in a pretty(?) way"""
|
||||
for key in reportdict.keys():
|
||||
printreportitem(key, reportdict[key])
|
||||
|
||||
|
||||
def savereport():
|
||||
FoundationPlist.writePlist(report, os.path.join(pref('ManagedInstallDir'), "ManagedInstallReport.plist"))
|
||||
|
||||
|
||||
def archive_report():
|
||||
reportfile = os.path.join(pref('ManagedInstallDir'), "ManagedInstallReport.plist")
|
||||
if os.path.exists(reportfile):
|
||||
modtime = os.stat(reportfile).st_mtime
|
||||
formatstr = "%Y-%m-%d-%H%M%S"
|
||||
archivename = "ManagedInstallReport-" + time.strftime(formatstr,time.localtime(modtime)) + ".plist"
|
||||
archivepath = os.path.join(pref('ManagedInstallDir'), "Archives")
|
||||
if not os.path.exists(archivepath):
|
||||
try:
|
||||
os.mkdir(archivepath)
|
||||
except:
|
||||
display_warning("Could not create report archive path.")
|
||||
try:
|
||||
os.rename(reportfile, os.path.join(archivepath, archivename))
|
||||
# convert to binary format to compress
|
||||
#cmd = ['/usr/bin/plutil', '-convert', 'binary1', os.path.join(archivepath, archivename)]
|
||||
#p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
# stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
#(out, err) = p.communicate()
|
||||
except:
|
||||
display_warning("Could not archive report.")
|
||||
# now keep number of archived reports to 100 or fewer
|
||||
p = subprocess.Popen(['/bin/ls', '-t1', archivepath],
|
||||
bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, err) = p.communicate()
|
||||
if output:
|
||||
archiveitems = [item for item in output.splitlines() if item.startswith("ManagedInstallReport-")]
|
||||
if len(archiveitems) > 100:
|
||||
for item in archiveitems[100:]:
|
||||
itempath = os.path.join(archivepath, archiveitem)
|
||||
if os.path.isfile(itempath):
|
||||
try:
|
||||
os.unlink(itempath)
|
||||
except:
|
||||
display_warning("Could not remove archive item %s" % itempath)
|
||||
|
||||
|
||||
|
||||
# misc functions
|
||||
|
||||
def validPlist(path):
|
||||
'''Uses plutil to determine if path contains a valid plist.
|
||||
Returns True or False.'''
|
||||
cmd = ['/usr/bin/plutil', '-lint', '-s' , path]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
if p.returncode == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def stopRequested():
|
||||
"""Allows user to cancel operations when
|
||||
MunkiStatus is being used"""
|
||||
@@ -689,8 +775,7 @@ def getInstalledPackageVersion(pkgid):
|
||||
Returns the version string of the installed pkg
|
||||
if it exists, or an empty string if it does not
|
||||
"""
|
||||
|
||||
|
||||
|
||||
# First check (Leopard and later) package database
|
||||
try:
|
||||
p = subprocess.Popen(["/usr/sbin/pkgutil", "--pkg-info-plist", pkgid], bufsize=1,
|
||||
@@ -876,12 +961,12 @@ def cleanUpTmpDir():
|
||||
debug = False
|
||||
verbose = 1
|
||||
munkistatusoutput = False
|
||||
errors = ""
|
||||
warnings = ""
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
logfile = pref('LogFile')
|
||||
logginglevel = pref('LoggingLevel')
|
||||
|
||||
report = {}
|
||||
report['Errors'] = []
|
||||
report['Warnings'] = []
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
+121
-103
@@ -40,25 +40,6 @@ import munkicommon
|
||||
import munkistatus
|
||||
import FoundationPlist
|
||||
|
||||
def reporterrors():
|
||||
# just a placeholder right now;
|
||||
# this needs to be expanded to support error reporting
|
||||
# via email and HTTP CGI.
|
||||
# (and maybe moved to a library module so the installer
|
||||
# can use it, too.)
|
||||
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
clientidentifier = managedinstallprefs.get('ClientIdentifier','')
|
||||
#alternate_id = option_id
|
||||
hostname = os.uname()[1]
|
||||
|
||||
print "installcheck errors %s:" % datetime.datetime.now().ctime()
|
||||
print "Hostname: %s" % hostname
|
||||
print "Client identifier: %s" % clientidentifier
|
||||
#print "Alternate ID: %s" % alternate_id
|
||||
print "-----------------------------------------"
|
||||
print munkicommon.errors
|
||||
|
||||
|
||||
# global to hold our catalog DBs
|
||||
catalog = {}
|
||||
@@ -615,15 +596,13 @@ def download_installeritem(location):
|
||||
pkgname = os.path.basename(location)
|
||||
destinationpath = os.path.join(mycachedir, pkgname)
|
||||
|
||||
munkicommon.display_detail("Downloading %s from %s" % (pkgname, location))
|
||||
# bump up verboseness so we get download percentage done feedback.
|
||||
# this is kind of a hack...
|
||||
oldverbose = munkicommon.verbose
|
||||
munkicommon.verbose = oldverbose + 1
|
||||
|
||||
munkicommon.log("Downloading %s from %s" % (pkgname, location))
|
||||
dl_message = "Downloading %s..." % pkgname
|
||||
(path, err) = getHTTPfileIfNewerAtomically(pkgurl, destinationpath, message=dl_message)
|
||||
|
||||
# set verboseness back.
|
||||
munkicommon.verbose = oldverbose
|
||||
|
||||
@@ -632,8 +611,7 @@ def download_installeritem(location):
|
||||
else:
|
||||
munkicommon.display_error("Could not download %s from server." % pkgname)
|
||||
munkicommon.display_error(err)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def isItemInInstallInfo(manifestitem_pl, thelist, vers=''):
|
||||
@@ -1423,13 +1401,20 @@ def getCatalogs(cataloglist):
|
||||
if not catalogname in catalog:
|
||||
catalogurl = catalogbaseurl + urllib2.quote(catalogname)
|
||||
catalogpath = os.path.join(catalog_dir, catalogname)
|
||||
munkicommon.log("Getting catalog %s..." % catalogname)
|
||||
munkicommon.display_detail("Getting catalog %s..." % catalogname)
|
||||
message = "Retreiving catalog '%s'..." % catalogname
|
||||
(newcatalog, err) = getHTTPfileIfNewerAtomically(catalogurl, catalogpath, message=message)
|
||||
if newcatalog:
|
||||
catalog[catalogname] = makeCatalogDB(FoundationPlist.readPlist(newcatalog))
|
||||
if munkicommon.validPlist(newcatalog):
|
||||
catalog[catalogname] = makeCatalogDB(FoundationPlist.readPlist(newcatalog))
|
||||
else:
|
||||
munkicommon.display_error("Retreived catalog %s is invalid." % catalogname)
|
||||
try:
|
||||
os.unlink(newcatalog)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
munkicommon.display_error("Could not retreive catalog %s from server." % catalog)
|
||||
munkicommon.display_error("Could not retrieve catalog %s from server." % catalogname)
|
||||
munkicommon.display_error(err)
|
||||
|
||||
|
||||
@@ -1449,25 +1434,34 @@ def getmanifest(partialurl, suppress_errors=False):
|
||||
manifestname = "client_manifest.plist"
|
||||
else:
|
||||
# request for nested manifest
|
||||
munkicommon.log("Getting manifest %s..." % partialurl)
|
||||
munkicommon.display_detail("Getting manifest %s..." % partialurl)
|
||||
manifestname = os.path.split(partialurl)[1]
|
||||
manifesturl = manifestbaseurl + urllib2.quote(partialurl)
|
||||
|
||||
manifestpath = os.path.join(manifest_dir, manifestname)
|
||||
message = "Retreiving list of software for this machine..."
|
||||
(newmanifest, err) = getHTTPfileIfNewerAtomically(manifesturl, manifestpath, message=message)
|
||||
if not newmanifest and not suppress_errors:
|
||||
munkicommon.display_error("Could not retreive manifest %s from the server." % partialurl)
|
||||
munkicommon.display_error(err)
|
||||
|
||||
return newmanifest
|
||||
if not newmanifest:
|
||||
if not suppress_errors:
|
||||
munkicommon.display_error("Could not retrieve manifest %s from the server." % partialurl)
|
||||
munkicommon.display_error(err)
|
||||
return None
|
||||
|
||||
if munkicommon.validPlist(newmanifest):
|
||||
return newmanifest
|
||||
else:
|
||||
munkicommon.display_error("manifest returned for %s is invalid." % partialurl)
|
||||
try:
|
||||
os.unlink(newmanifest)
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def getPrimaryManifest(alternate_id):
|
||||
"""
|
||||
Gets the client manifest from the server
|
||||
"""
|
||||
global errors
|
||||
manifest = ""
|
||||
manifesturl = munkicommon.pref('ManifestURL') or munkicommon.pref('SoftwareRepoURL') + "/manifests/"
|
||||
if not manifesturl.endswith('?') and not manifesturl.endswith('/'):
|
||||
@@ -1489,43 +1483,31 @@ def getPrimaryManifest(alternate_id):
|
||||
if not manifest:
|
||||
# last resort - try for the site_default manifest
|
||||
clientidentifier = "site_default"
|
||||
munkicommon.display_detail("Request failed. Trying ..." % clientidentifier)
|
||||
munkicommon.display_detail("Request failed. Trying %s..." % clientidentifier)
|
||||
|
||||
if not manifest:
|
||||
manifest = getmanifest(manifesturl + urllib2.quote(clientidentifier))
|
||||
if manifest:
|
||||
# record this info for later
|
||||
munkicommon.report['ManifestName'] = clientidentifier
|
||||
munkicommon.display_detail("Using manifest: %s" % clientidentifier)
|
||||
# clear out any errors we got while trying to find
|
||||
# the primary manifest
|
||||
errors = ""
|
||||
|
||||
return manifest
|
||||
|
||||
|
||||
def getInstallCount(installinfo):
|
||||
count = 0
|
||||
for item in installinfo.get('managed_installs',[]):
|
||||
if 'installer_item' in item:
|
||||
count +=1
|
||||
return count
|
||||
|
||||
|
||||
def getRemovalCount(installinfo):
|
||||
count = 0
|
||||
for item in installinfo.get('removals',[]):
|
||||
if 'installed' in item:
|
||||
if item['installed']:
|
||||
count +=1
|
||||
return count
|
||||
|
||||
|
||||
def checkServer():
|
||||
'''in progress'''
|
||||
managedinstallprefs = munkicommon.prefs()
|
||||
manifesturl = managedinstallprefs['ManifestURL']
|
||||
def checkServer(url):
|
||||
'''A function we can call to check to see if the server is
|
||||
available before we kick off a full run. This can be fooled by
|
||||
ISPs that return results for non-existent web servers...'''
|
||||
# deconstruct URL so we can check availability
|
||||
port = 80
|
||||
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(manifesturl)
|
||||
(scheme, netloc, path, query, fragment) = urlparse.urlsplit(url)
|
||||
if scheme == "http":
|
||||
port = 80
|
||||
elif scheme == "https":
|
||||
port = 443
|
||||
else:
|
||||
return False
|
||||
|
||||
# get rid of any embedded username/password
|
||||
netlocparts = netloc.split("@")
|
||||
netloc = netlocparts[-1]
|
||||
@@ -1533,15 +1515,27 @@ def checkServer():
|
||||
netlocparts = netloc.split(":")
|
||||
host = netlocparts[0]
|
||||
if len(netlocparts) == 2:
|
||||
port = netlocparts[1]
|
||||
|
||||
port = int(netlocparts[1])
|
||||
s = socket.socket()
|
||||
#try:
|
||||
s.connect((host, port))
|
||||
s.close()
|
||||
return True
|
||||
#except:
|
||||
#return False
|
||||
# set timeout to 5 secs
|
||||
s.settimeout(5.0)
|
||||
try:
|
||||
s.connect((host, port))
|
||||
s.close()
|
||||
return (0, 'OK')
|
||||
except socket.error, err:
|
||||
if type(err) == str:
|
||||
return (-1, err)
|
||||
else:
|
||||
return err
|
||||
except socket.timeout, err:
|
||||
return (-1, err)
|
||||
except Exception, err:
|
||||
# common errors
|
||||
# (50, 'Network is down')
|
||||
# (8, 'nodename nor servname provided, or not known')
|
||||
# (61, 'Connection refused')
|
||||
return tuple(err)
|
||||
|
||||
|
||||
# HTTP download functions
|
||||
@@ -1554,8 +1548,8 @@ def checkServer():
|
||||
# one on the server.
|
||||
#
|
||||
# Possible failure mode: if client's main catalog gets pointed
|
||||
# to a different, older, catalog, we'll fail to retreive it.
|
||||
# Need to check content length as well, and if it changes, retreive
|
||||
# to a different, older, catalog, we'll fail to retrieve it.
|
||||
# Need to check content length as well, and if it changes, retrieve
|
||||
# it anyway.
|
||||
#
|
||||
# Should probably cleanup/unify
|
||||
@@ -1598,6 +1592,8 @@ def httpDownload(url, filename, headers={}, postData=None, reporthook=None, mess
|
||||
UseClientCertificate = munkicommon.pref('UseClientCertificate')
|
||||
cert = os.path.join(ManagedInstallDir, 'certs', pemfile)
|
||||
|
||||
# set default timeout so we don't hang if the server stops responding
|
||||
socket.setdefaulttimeout(30)
|
||||
reqObj = urllib2.Request(url, postData, headers)
|
||||
|
||||
if UseClientCertificate == True:
|
||||
@@ -1660,7 +1656,7 @@ def getfilefromhttpurl(url,filepath, ifmodifiedsince=None, message=None):
|
||||
"""
|
||||
gets a file from a url.
|
||||
If 'ifmodifiedsince' is specified, this header is set
|
||||
and the file is not retreived if it hasn't changed on the server.
|
||||
and the file is not retrieved if it hasn't changed on the server.
|
||||
Returns 0 if successful, or HTTP error code
|
||||
"""
|
||||
def reporthook(block_count, block_size, file_size):
|
||||
@@ -1732,7 +1728,7 @@ def getHTTPfileIfNewerAtomically(url,destinationpath, message=None):
|
||||
err = "SSL_CTX_use_certificate_chain_file error: Certificate Invalid or Missing"
|
||||
destinationpath = None
|
||||
else:
|
||||
err = "Error code: %s retreiving %s" % (result, url)
|
||||
err = "Error code: %s retrieving %s" % (result, url)
|
||||
destinationpath = None
|
||||
|
||||
if os.path.exists(mytemppath):
|
||||
@@ -1744,6 +1740,7 @@ def getHTTPfileIfNewerAtomically(url,destinationpath, message=None):
|
||||
def getMachineFacts():
|
||||
global machine
|
||||
|
||||
machine['hostname'] = os.uname()[1]
|
||||
machine['arch'] = os.uname()[4]
|
||||
cmd = ['/usr/bin/sw_vers', '-productVersion']
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
@@ -1755,13 +1752,12 @@ def getMachineFacts():
|
||||
|
||||
# some globals
|
||||
machine = {}
|
||||
|
||||
def check(id=''):
|
||||
'''Checks for available new or updated managed software, downloading installer items
|
||||
if needed. Returns 1 if there are available updates, 0 if there are no available updates,
|
||||
and -1 if there were errors.'''
|
||||
|
||||
getMachineFacts()
|
||||
munkicommon.report['MachineInfo'] = machine
|
||||
|
||||
ManagedInstallDir = munkicommon.pref('ManagedInstallDir')
|
||||
|
||||
@@ -1804,19 +1800,38 @@ def check(id=''):
|
||||
# this could happen if an item is downloaded on one
|
||||
# updatecheck run, but later removed from the manifest
|
||||
# before it is installed or removed
|
||||
cache_list = []
|
||||
for item in installinfo['managed_installs']:
|
||||
if "installer_item" in item:
|
||||
cache_list.append(item["installer_item"])
|
||||
for item in installinfo['removals']:
|
||||
if "uninstaller_item" in item:
|
||||
cache_list.append(item["uninstaller_item"])
|
||||
#cache_list = []
|
||||
#for item in installinfo['managed_installs']:
|
||||
# if "installer_item" in item:
|
||||
# cache_list.append(item["installer_item"])
|
||||
#for item in installinfo['removals']:
|
||||
# if "uninstaller_item" in item:
|
||||
# cache_list.append(item["uninstaller_item"])
|
||||
cache_list = [item["installer_item"] for item in installinfo['managed_installs'] if item.get("installer_item")]
|
||||
cache_list.extend([item["uninstaller_item"] for item in installinfo['removals'] if item.get("uninstaller_item")])
|
||||
cachedir = os.path.join(ManagedInstallDir, "Cache")
|
||||
for item in os.listdir(cachedir):
|
||||
if item not in cache_list:
|
||||
munkicommon.display_detail("Removing %s from cache" % item)
|
||||
os.unlink(os.path.join(cachedir, item))
|
||||
|
||||
|
||||
# filter managed_installs to get items already installed
|
||||
installed_items = [item for item in installinfo['managed_installs'] if item.get('installed')]
|
||||
# filter managed_installs to get problem items: not installed, but no installer item
|
||||
problem_items = [item for item in installinfo['managed_installs'] if item.get('installed') == False and not item.get('installer_item')]
|
||||
# filter removals to get items already removed (or never installed)
|
||||
removed_items = [item for item in installinfo['removals'] if item.get('installed') == False]
|
||||
|
||||
# filter managed_installs and removals lists so they have only items that need action
|
||||
installinfo['managed_installs'] = [item for item in installinfo['managed_installs'] if item.get('installer_item')]
|
||||
installinfo['removals'] = [item for item in installinfo['removals'] if item.get('installed')]
|
||||
|
||||
munkicommon.report['ManagedInstalls'] = installed_items
|
||||
munkicommon.report['ProblemInstalls'] = problem_items
|
||||
munkicommon.report['RemovedItems'] = removed_items
|
||||
munkicommon.report['ItemsToInstall'] = installinfo['managed_installs']
|
||||
munkicommon.report['ItemsToRemove'] = installinfo['removals']
|
||||
|
||||
# write out install list so our installer
|
||||
# can use it to install things in the right order
|
||||
installinfochanged = True
|
||||
@@ -1832,43 +1847,46 @@ def check(id=''):
|
||||
else:
|
||||
# couldn't get a primary manifest. Check to see if we have a valid InstallList from
|
||||
# an earlier run.
|
||||
munkicommon.display_error("Could not retreive managed install primary manifest.")
|
||||
munkicommon.display_error("Could not retrieve managed install primary manifest.")
|
||||
installinfopath = os.path.join(ManagedInstallDir, "InstallInfo.plist")
|
||||
if os.path.exists(installinfopath):
|
||||
try:
|
||||
installinfo = FoundationPlist.readPlist(installinfopath)
|
||||
munkicommon.report['ItemsToInstall'] = installinfo.get('managed_installs',[])
|
||||
munkicommon.report['ItemsToRemove'] = installinfo.get('removals',[])
|
||||
except:
|
||||
installinfo = {}
|
||||
|
||||
|
||||
installcount = getInstallCount(installinfo)
|
||||
removalcount = getRemovalCount(installinfo)
|
||||
installcount = len(installinfo.get("managed_installs",[]))
|
||||
removalcount = len(installinfo.get("removals",[]))
|
||||
|
||||
if installcount:
|
||||
munkicommon.log("")
|
||||
if installcount:
|
||||
munkicommon.display_info("The following items will be installed or upgraded:")
|
||||
for item in installinfo['managed_installs']:
|
||||
if item.get('installer_item'):
|
||||
munkicommon.display_info(" + %s-%s" % (item.get('name',''), item.get('version_to_install','')))
|
||||
if item.get('description'):
|
||||
munkicommon.display_info(" %s" % item['description'])
|
||||
if item.get('RestartAction') == 'RequireRestart':
|
||||
munkicommon.display_info(" *Restart required")
|
||||
for item in installinfo['managed_installs']:
|
||||
if item.get('installer_item'):
|
||||
munkicommon.display_info(" + %s-%s" % (item.get('name',''), item.get('version_to_install','')))
|
||||
if item.get('description'):
|
||||
munkicommon.display_info(" %s" % item['description'])
|
||||
if item.get('RestartAction') == 'RequireRestart':
|
||||
munkicommon.display_info(" *Restart required")
|
||||
munkicommon.report['RestartRequired'] = True
|
||||
if removalcount:
|
||||
munkicommon.display_info("The following items will be removed:")
|
||||
for item in installinfo['removals']:
|
||||
if item.get('installed'):
|
||||
munkicommon.display_info(" - %s" % item.get('name'))
|
||||
if item.get('RestartAction') == 'RequireRestart':
|
||||
munkicommon.display_info(" *Restart required")
|
||||
|
||||
for item in installinfo['removals']:
|
||||
if item.get('installed'):
|
||||
munkicommon.display_info(" - %s" % item.get('name'))
|
||||
if item.get('RestartAction') == 'RequireRestart':
|
||||
munkicommon.display_info(" *Restart required")
|
||||
munkicommon.report['RestartRequired'] = True
|
||||
|
||||
if installcount == 0 and removalcount == 0:
|
||||
munkicommon.display_info("No changes to managed software are available.")
|
||||
|
||||
munkicommon.savereport()
|
||||
munkicommon.log("### End managed software check ###")
|
||||
|
||||
if munkicommon.errors:
|
||||
reporterrors()
|
||||
|
||||
if installcount or removalcount:
|
||||
return 1
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user