Significant changes to how Apple update items are matched to metadata; new function in updatecheck specifically created to handle collection of catalog data by appleupdates; new function created for applying metadata

Of note, 'copyUpdateMetadata' function uses a list of whitelisted
metadata keys as well as appropriate logic to determine if a particular
'RestartAction' should be applied.
This commit is contained in:
Heig Gregorian
2013-02-13 13:22:07 -08:00
parent 9f8cd3a975
commit e2005a696b
2 changed files with 70 additions and 89 deletions
+46 -57
View File
@@ -830,33 +830,22 @@ class AppleUpdates(object):
Returns:
Boolean. True if apple updates was updated, False otherwise.
"""
metadata_exclusions = ['catalogs',
'installed_size',
'installer_type',
'name',
'version',
'version_to_install']
apple_updates = self.GetSoftwareUpdateInfo()
if apple_updates:
# Process update metadata only if AppleSoftwareUpdatesOnly is false
if not munkicommon.pref('AppleSoftwareUpdatesOnly'):
self.apple_md = updatecheck.check(
self.client_id, apple_update_md_only=True)
for update in apple_updates:
matching_items = self.getAllItemsWithProductKey(
update['productKey'])
if matching_items:
update_metadata = matching_items[0]
for key in update_metadata:
# Don't overwrite items in exclusions list
if key in metadata_exclusions:
continue
else:
# Apply non-empty metadata
if update_metadata[key]:
munkicommon.display_debug2(
'Applying %s to %s...' % (key, update['display_name']))
update[key] = update_metadata[key]
# Gather available apple_update_metadata
cataloglist, self.apple_md = \
updatecheck.getAppleUpdateMetaData(self.client_id)
for item in apple_updates:
# Find matching metadata item
metadata_item = updatecheck.getItemDetail(
item['productKey'], cataloglist,
apple_update_metadata=True)
if metadata_item:
munkicommon.display_debug1(
'Processing metadata for %s, %s...'
% (item['productKey'], item['display_name']))
self.copyUpdateMetadata(item, metadata_item)
plist = {'AppleUpdates': apple_updates}
FoundationPlist.writePlist(plist, self.apple_updates_plist)
return True
@@ -1327,43 +1316,43 @@ class AppleUpdates(object):
return updates
def getAllItemsWithProductKey(self, productKey):
"""Searches apple_md for all items matching a given Apple productKey
Returns:
list of pkginfo items; sorted with newest version first. No precedence
is given to catalog order.
def copyUpdateMetadata(self, item, metadata):
"""Applies metadata to Apple update item restricted
to keys contained in 'metadata_to_copy'.
"""
def compare_item_versions(a, b):
"""Internal comparison function for use with sorting"""
return cmp(munkicommon.MunkiLooseVersion(b['version']),
munkicommon.MunkiLooseVersion(a['version']))
metadata_to_copy = ['blocking_applications',
'description',
'display_name',
'force_install_after_date',
'unattended_install',
'RestartAction']
itemlist = []
# Mapping of supported RestartActions to
# equal or greater auxiliary actions
RestartActions = {
'RequireRestart' : ['RequireRestart', 'RecommendRestart'],
'RecommendRestart': ['RequireRestart', 'RecommendRestart'],
'RequireLogout' : ['RequireRestart', 'RecommendRestart',
'RequireLogout'],
'None' : ['RequireRestart', 'RecommendRestart',
'RequireLogout']
}
munkicommon.display_debug1(
'Looking for metadata matching: %s...' % productKey)
# is productKey in the catalog name table?
for catalogname in self.apple_md.keys():
if productKey in self.apple_md[catalogname]['named']:
versionsmatchingproductKey = \
self.apple_md[catalogname]['named'][productKey]
for vers in versionsmatchingproductKey.keys():
if vers != 'latest':
indexlist = \
self.apple_md[catalogname]['named'][productKey][vers]
for index in indexlist:
thisitem = self.apple_md[catalogname]['items'][index]
if not thisitem in itemlist:
munkicommon.display_debug1(
'Adding item %s, version %s from catalog %s...' %
(productKey, thisitem['version'], catalogname))
itemlist.append(thisitem)
if itemlist:
# sort so latest version is first
itemlist.sort(compare_item_versions)
return itemlist
for key in metadata:
# Apply 'white-listed', non-empty metadata keys
if key in metadata_to_copy and metadata[key]:
if key == 'RestartAction':
# Ensure that a heavier weighted 'RestartAction' is not
# overriden by one supplied in metadata
if metadata[key] not in RestartActions.get(item.get(key, 'None')):
munkicommon.display_debug2(
'\tSkipping %s \'%s\', \'%s\' is preferred.'
% (key, metadata[key], item[key]))
continue
munkicommon.display_debug2('\tApplying %s...' % key)
item[key] = metadata[key]
return item
+24 -32
View File
@@ -44,8 +44,6 @@ from Foundation import NSDate, NSPredicate, NSTimeZone
# This many hours before a force install deadline, start notifying the user.
FORCE_INSTALL_WARNING_HOURS = 4
# Flag denoting a check from appleupdates for apple_update_metadata
APPLE_UPDATE_MD_ONLY = False
def makeCatalogDB(catalogitems):
"""Takes an array of catalog items and builds some indexes so we can
@@ -53,12 +51,6 @@ def makeCatalogDB(catalogitems):
name_table = {}
pkgid_table = {}
# Filter catalogitems so that they only contain apple_update_metadata items
if APPLE_UPDATE_MD_ONLY:
catalogitems = [item for item in catalogitems
if item.get('installer_type') ==
'apple_update_metadata']
itemindex = -1
for item in catalogitems:
itemindex = itemindex + 1
@@ -952,7 +944,7 @@ def trimVersionString(version_string):
return '.'.join(version_parts)
def getItemDetail(name, cataloglist, vers=''):
def getItemDetail(name, cataloglist, vers='', apple_update_metadata=False):
"""Searches the catalogs in list for an item matching the given name.
If no version is supplied, but the version is appended to the name
@@ -965,14 +957,17 @@ def getItemDetail(name, cataloglist, vers=''):
return cmp(munkicommon.MunkiLooseVersion(b),
munkicommon.MunkiLooseVersion(a))
(name, includedversion) = nameAndVersion(name)
if vers == '':
if includedversion:
vers = includedversion
if vers:
vers = trimVersionString(vers)
else:
if apple_update_metadata:
vers = 'latest'
else:
(name, includedversion) = nameAndVersion(name)
if vers == '':
if includedversion:
vers = includedversion
if vers:
vers = trimVersionString(vers)
else:
vers = 'latest'
munkicommon.display_debug1('Looking for detail for: %s, version %s...' %
(name, vers))
@@ -2616,16 +2611,14 @@ def getDownloadCachePath(destinationpathprefix, url):
return os.path.join(
destinationpathprefix, getInstallerItemBasename(url))
MACHINE = {}
CONDITIONS = {}
def check(client_id='', localmanifestpath=None, apple_update_md_only=False):
def check(client_id='', localmanifestpath=None):
"""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."""
global APPLE_UPDATE_MD_ONLY
APPLE_UPDATE_MD_ONLY = apple_update_md_only
global MACHINE
munkicommon.getMachineFacts()
MACHINE = munkicommon.getMachineFacts()
@@ -2639,6 +2632,9 @@ def check(client_id='', localmanifestpath=None, apple_update_md_only=False):
if munkicommon.munkistatusoutput:
munkistatus.activate()
munkicommon.log('### Beginning managed software check ###')
munkicommon.display_status_major('Checking for available updates...')
if localmanifestpath:
mainmanifestpath = localmanifestpath
else:
@@ -2648,14 +2644,6 @@ def check(client_id='', localmanifestpath=None, apple_update_md_only=False):
installinfo = {}
if APPLE_UPDATE_MD_ONLY:
munkicommon.display_detail('**Checking for Apple Update Metadata**')
print munkicommon.pref('AppleSoftwareUpdatesOnly')
return getAppleUpdateMetaData(mainmanifestpath)
munkicommon.log('### Beginning managed software check ###')
munkicommon.display_status_major('Checking for available updates...')
if mainmanifestpath:
# initialize our installinfo record
installinfo['processed_installs'] = []
@@ -3140,15 +3128,19 @@ def getResourceIfChangedAtomically(url,
verify=verify)
def getAppleUpdateMetaData(manifest):
global CATALOG
CATALOG = {}
def getAppleUpdateMetaData(client_id=''):
"""Used by appleupdates in determining
metadata to apply to available Apple updates"""
cataloglist = []
munkicommon.display_detail('**Checking for Apple Update Metadata**')
manifest = getPrimaryManifest(client_id)
if manifest:
manifestdata = getManifestData(manifest)
cataloglist = manifestdata.get('catalogs')
if cataloglist:
getCatalogs(cataloglist)
return CATALOG
return cataloglist, CATALOG
def main():