Managed Software Update/munki.py:trimVersionString() enhancement.

Instead of stripping x parts off the tuple, simply trim all lone trailing 0's from the version.

    Examples:
      10.0.0.0 -> 10.0
      10.0.0.1 -> 10.0.0.1
      10.0.0.1.0.0 -> 10.0.0.1
      10.0.0-abc1 -> 10.0.0-abc1
      10.0.0-abc1.0 -> 10.0.0-abc1



git-svn-id: http://munki.googlecode.com/svn/trunk@793 a4e17f2e-e282-11dd-95e1-755cbddbdd66
This commit is contained in:
Justin McWilliams
2010-10-14 17:39:57 +00:00
parent 2330ddfc70
commit 12447c8e74
2 changed files with 92 additions and 83 deletions
+45 -45
View File
@@ -8,9 +8,9 @@
# 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.
@@ -28,10 +28,10 @@ class MSUAppDelegate(NSObject):
munkiStatusController = objc.IBOutlet()
mainWindowController = objc.IBOutlet()
update_view_controller = objc.IBOutlet()
update_array_controller = objc.IBOutlet()
optional_view_controller = objc.IBOutlet()
optional_array_controller = objc.IBOutlet()
@@ -40,7 +40,7 @@ class MSUAppDelegate(NSObject):
_logoutImage = NSImage.imageNamed_("LogOutReq.tif")
_listofupdates = []
_optionalInstalls = []
restart_required = False
logout_required = False
runmode = "Normal"
@@ -48,13 +48,13 @@ class MSUAppDelegate(NSObject):
def applicationDidFinishLaunching_(self, sender):
NSLog(u"Managed Software Update finished launching.")
runmode = NSUserDefaults.standardUserDefaults().stringForKey_("mode") or \
os.environ.get("ManagedSoftwareUpdateMode")
if runmode:
self.runmode = runmode
NSLog("Runmode: %s" % runmode)
consoleuser = munki.getconsoleuser()
if consoleuser == None or consoleuser == u"loginwindow":
# Status Window only
@@ -76,35 +76,35 @@ class MSUAppDelegate(NSObject):
else:
# no updates available. Should we check for some?
self.checkForUpdates()
def munkiStatusSessionEnded_(self, socketSessionResult):
consoleuser = munki.getconsoleuser()
if self.runmode == "MunkiStatus" or consoleuser == None or consoleuser == u"loginwindow":
# Status Window only, so we should just quit
NSApp.terminate_(self)
alertMessageText = "Update check failed"
if self.managedsoftwareupdate_task == "installwithnologout":
alertMessageText = "Install session failed"
if socketSessionResult == -1:
# connection was dropped unexpectedly
self.mainWindowController.theWindow.makeKeyAndOrderFront_(self)
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(alertMessageText, u"Quit", objc.nil, objc.nil, "There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
return
elif socketSessionResult == -2:
# socket timed out before connection
self.mainWindowController.theWindow.makeKeyAndOrderFront_(self)
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(alertMessageText, u"Quit", objc.nil, objc.nil, "There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
return
if self.managedsoftwareupdate_task == "installwithnologout":
# we're done.
NSApp.terminate_(self)
elif self.managedsoftwareupdate_task == "manualcheck":
self.managedsoftwareupdate_task = None
self._listofupdates = []
@@ -131,15 +131,15 @@ class MSUAppDelegate(NSObject):
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Cannot check for updates", u"Quit", objc.nil, objc.nil, "Managed Software Update failed its preflight check.\nTry again later.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
def noUpdatesAlert(self):
def noUpdatesAlert(self):
if self._optionalInstalls:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Your software is up to date.", u"Quit", u"Optional software...", objc.nil, "There is no new software for your computer at this time.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
else:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Your software is up to date.", u"Quit", objc.nil, objc.nil, "There is no new software for your computer at this time.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
def checkForUpdates(self):
# kick off an update check
self.mainWindowController.theWindow.orderOut_(self)
@@ -152,11 +152,11 @@ class MSUAppDelegate(NSObject):
self.mainWindowController.theWindow.makeKeyAndOrderFront_(self)
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Update check failed", u"Quit", objc.nil, objc.nil, "There is a configuration problem with the managed software installer. Could not start the update check process. Contact your systems administrator.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
def applicationDidBecomeActive_(self, sender):
pass
def getOptionalInstalls(self):
optionalInstalls = []
installinfo = munki.getInstallInfo()
@@ -168,7 +168,7 @@ class MSUAppDelegate(NSObject):
else:
self.update_view_controller.optionalSoftwareBtn.setHidden_(YES)
def getAvailableUpdates(self):
updatelist = []
installinfo = munki.getInstallInfo()
@@ -195,7 +195,7 @@ class MSUAppDelegate(NSObject):
if restartNeeded:
row["RestartAction"] = "RequireRestart"
updatelist.append(row)
if updatelist:
self._listofupdates = updatelist
self.update_view_controller.updateNowBtn.setEnabled_(YES)
@@ -209,14 +209,14 @@ class MSUAppDelegate(NSObject):
else:
self.update_view_controller.updateNowBtn.setEnabled_(NO)
self.getOptionalInstalls()
def buildOptionalInstallsData(self):
table = []
selfservedata = munki.readSelfServiceManifest()
selfserve_installs = selfservedata.get('managed_installs',[])
selfserve_uninstalls = selfservedata.get('managed_uninstalls',[])
for item in self._optionalInstalls:
row = {}
row['enabled'] = objc.YES
@@ -232,7 +232,7 @@ class MSUAppDelegate(NSObject):
row['original_managed'] = (item['name'] in selfserve_installs)
row['itemname'] = item['name']
row['name'] = item.get("display_name") or item['name']
row['version'] = munki.trimVersionString(item.get("version_to_install"),3)
row['version'] = munki.trimVersionString(item.get("version_to_install"))
row['description'] = item.get("description","")
if item.get("installer_item_size"):
row['size'] = munki.humanReadable(item.get("installer_item_size"))
@@ -264,11 +264,11 @@ class MSUAppDelegate(NSObject):
row['original_status'] = status
row_dict = NSMutableDictionary.dictionaryWithDictionary_(row)
table.append(row_dict)
self.optional_view_controller.setOptionallist_(table)
self.optional_view_controller.tableView.deselectAll_(self)
def addOrRemoveOptionalSoftware(self):
# record any requested changes in installed/removal state
# then kick off an update check
@@ -285,7 +285,7 @@ class MSUAppDelegate(NSObject):
munki.writeSelfServiceManifest(optional_install_choices)
self.checkForUpdates()
def buildUpdateTableData(self):
table = []
self.restart_required = False
@@ -301,7 +301,7 @@ class MSUAppDelegate(NSObject):
else:
row['image'] = self._emptyImage
row['name'] = item.get("display_name") or item.get("name","")
row['version'] = munki.trimVersionString(item.get("version_to_install"),3)
row['version'] = munki.trimVersionString(item.get("version_to_install"))
if item.get("installer_item_size"):
row['size'] = munki.humanReadable(item.get("installer_item_size"))
elif item.get("installed_size"):
@@ -311,7 +311,7 @@ class MSUAppDelegate(NSObject):
row['description'] = item.get("description","")
row_dict = NSDictionary.dictionaryWithDictionary_(row)
table.append(row_dict)
self.update_view_controller.setUpdatelist_(table)
self.update_view_controller.tableView.deselectAll_(self)
if self.restart_required:
@@ -320,30 +320,30 @@ class MSUAppDelegate(NSObject):
elif self.logout_required:
self.update_view_controller.restartInfoFld.setStringValue_(u"Logout will be required.")
self.update_view_controller.restartImageFld.setImage_(self._logoutImage)
def confirmInstallUpdates(self):
if self.mainWindowController.theWindow.isVisible() == objc.NO:
return
if len(munki.currentGUIusers()) > 1:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Other users logged in", u"Cancel", objc.nil, objc.nil, "There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.multipleUserAlertDidEnd_returnCode_contextInfo_, objc.nil)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.multipleUserAlertDidEnd_returnCode_contextInfo_, objc.nil)
elif self.restart_required:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Restart Required", u"Log out and update", u"Cancel", objc.nil, "A restart is required after updating. Log out and update now?")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
elif self.logout_required or munki.installRequiresLogout():
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Logout Required", u"Log out and update", u"Cancel", objc.nil, "A logout is required before updating. Log out and update now?")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
else:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Logout Recommended", u"Log out and update", u"Cancel", u"Update without logging out", "A logout is recommended before updating. Log out and update now?")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.logoutAlertDidEnd_returnCode_contextInfo_, objc.nil)
def installSessionErrorAlert(self):
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(u"Cannot start installation session", u"Quit", objc.nil, objc.nil, "There is a configuration problem with the managed software installer. Could not start the install session. Contact your systems administrator.")
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(self.mainWindowController.theWindow, self, self.quitAlertDidEnd_returnCode_contextInfo_, objc.nil)
@PyObjCTools.AppHelper.endSheetMethod
def logoutAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
if returncode == 0:
@@ -357,18 +357,18 @@ class MSUAppDelegate(NSObject):
NSLog("User chose to update without logging out")
result = munki.justUpdate()
if result:
self.installSessionErrorAlert()
self.installSessionErrorAlert()
else:
self.managedsoftwareupdate_task = "installwithnologout"
self.mainWindowController.theWindow.orderOut_(self)
self.munkiStatusController.window.makeKeyAndOrderFront_(self)
self.munkiStatusController.startMunkiStatusSession()
@PyObjCTools.AppHelper.endSheetMethod
def multipleUserAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
pass
@PyObjCTools.AppHelper.endSheetMethod
def quitAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo):
if returncode == 1:
@@ -377,6 +377,6 @@ class MSUAppDelegate(NSObject):
self.update_view_controller.optionalSoftwareBtn.setHidden_(NO)
self.buildOptionalInstallsData()
self.mainWindowController.theTabView.selectNextTabViewItem_(self)
+47 -38
View File
@@ -9,9 +9,9 @@
# 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.
@@ -30,22 +30,22 @@ UPDATECHECKLAUNCHFILE = \
def call(cmd):
'''Convenience function; works around an issue with subprocess.call
in PyObjC in Snow Leopard'''
proc = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE,
proc = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(output, err) = proc.communicate()
return proc.returncode
def getManagedInstallsPrefs():
'''Define default preference values;
'''Define default preference values;
Read preference values from ManagedInstalls.plist if it exists.'''
prefs = {}
prefs['ManagedInstallDir'] = "/Library/Managed Installs"
prefs['InstallAppleSoftwareUpdates'] = False
prefs['ShowRemovalDetail'] = False
prefs['InstallRequiresLogout'] = False
prefsfile = "/Library/Preferences/ManagedInstalls.plist"
if os.path.exists(prefsfile):
try:
@@ -61,10 +61,10 @@ def getManagedInstallsPrefs():
prefs[key] = plist[key]
except AttributeError:
pass
return prefs
def readSelfServiceManifest():
'''Read the SelfServeManifest if it exists'''
# read our working copy if it exists
@@ -82,21 +82,21 @@ def readSelfServiceManifest():
return {}
else:
return {}
def writeSelfServiceManifest(optional_install_choices):
'''Write out our self-serve manifest
'''Write out our self-serve manifest
so managedsoftwareupdate can use it'''
usermanifest = "/Users/Shared/.SelfServeManifest"
try:
FoundationPlist.writePlist(optional_install_choices, usermanifest)
except FoundationPlist.FoundationPlistException:
pass
def getRemovalDetailPrefs():
'''Returns preference to control display of removal detail'''
return getManagedInstallsPrefs().get('ShowRemovalDetail', False)
def installRequiresLogout():
'''Returns preference to force logout for all installs'''
return getManagedInstallsPrefs().get('InstallRequiresLogout', False)
@@ -114,13 +114,13 @@ def getInstallInfo():
except FoundationPlist.NSPropertyListSerializationException:
pass
return plist
def startUpdateCheck():
'''Does launchd magic to run managedsoftwareupdate as root.'''
result = call(["/usr/bin/touch", UPDATECHECKLAUNCHFILE])
return result
def getAppleUpdates():
'''Returns any available Apple updates'''
@@ -128,33 +128,42 @@ def getAppleUpdates():
managedinstallbase = prefs['ManagedInstallDir']
plist = {}
appleUpdatesFile = os.path.join(managedinstallbase, 'AppleUpdates.plist')
if (os.path.exists(appleUpdatesFile) and
if (os.path.exists(appleUpdatesFile) and
prefs['InstallAppleSoftwareUpdates']):
try:
plist = FoundationPlist.readPlist(appleUpdatesFile)
except FoundationPlist.NSPropertyListSerializationException:
pass
return plist
def humanReadable(kbytes):
"""Returns sizes in human-readable units."""
units = [(" KB", 2**10), (" MB", 2**20), (" GB", 2**30), (" TB", 2**40)]
units = [(" KB", 2**10), (" MB", 2**20), (" GB", 2**30), (" TB", 2**40)]
for suffix, limit in units:
if kbytes > limit:
continue
else:
return str(round(kbytes/float(limit/2**10), 1)) + suffix
def trimVersionString(versString, tupleCount):
'''Trims the version string to no more than tupleCount parts'''
if versString == None or versString == "":
return ""
components = str(versString).split(".")
if len(components) > tupleCount:
components = components[0:tupleCount]
return ".".join(components)
def trimVersionString(version_string):
"""Trims all lone trailing zeros in the version string after major/minor.
Examples:
10.0.0.0 -> 10.0
10.0.0.1 -> 10.0.0.1
10.0.0-abc1 -> 10.0.0-abc1
10.0.0-abc1.0 -> 10.0.0-abc1
"""
if version_string == None or version_string == '':
return ''
version_parts = version_string.split('.')
# strip off all trailing 0's in the version, while over 2 parts.
while len(version_parts) > 2 and version_parts[-1] == '0':
del(version_parts[-1])
return '.'.join(version_parts)
def getconsoleuser():
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
cfuser = SCDynamicStoreCopyConsoleUser( None, None, None )
@@ -164,8 +173,8 @@ def getconsoleuser():
def currentGUIusers():
'''Gets a list of GUI users by parsing the output of /usr/bin/who'''
gui_users = []
proc = subprocess.Popen("/usr/bin/who", shell=False,
stdin=subprocess.PIPE,
proc = subprocess.Popen("/usr/bin/who", shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(output, err) = proc.communicate()
lines = str(output).splitlines()
@@ -175,12 +184,12 @@ def currentGUIusers():
gui_users.append(parts[0])
return gui_users
def logoutNow():
'''Uses oscascript to run an AppleScript
to tell loginwindow to logout.
Ugly, but it works.'''
Ugly, but it works.'''
script = """
ignoring application responses
tell application "loginwindow"
@@ -200,7 +209,7 @@ end ignoring
def logoutAndUpdate():
'''Touch a flag so the process that runs after
logout knows it's OK to install everything'''
cmd = ["/usr/bin/touch",
cmd = ["/usr/bin/touch",
"/private/tmp/com.googlecode.munki.installatlogout"]
result = call(cmd)
if result == 0:
@@ -212,12 +221,12 @@ def logoutAndUpdate():
def justUpdate():
'''Trigger managedinstaller via launchd KeepAlive path trigger
We touch a file that launchd is is watching
launchd, in turn,
launchd, in turn,
launches managedsoftwareupdate --installwithnologout as root'''
cmd = ["/usr/bin/touch",
cmd = ["/usr/bin/touch",
"/private/tmp/.com.googlecode.munki.managedinstall.launchd"]
return call(cmd)