Initial support for checking for available licensed seats for uninstalled optional_installs

This commit is contained in:
Greg Neagle
2013-07-16 15:21:29 -07:00
parent 709d6f8b1d
commit 97d0d34f9b
3 changed files with 111 additions and 6 deletions

View File

@@ -484,6 +484,11 @@ class MSUAppDelegate(NSObject):
status = "Not installed"
if item.get("will_be_installed"):
status = NSLocalizedString(u"Will be installed", None)
elif 'licensed_seats_available' in item:
if not item['licensed_seats_available']:
status = NSLocalizedString(
u"No available license seats", None)
row['enabled'] = objc.NO
elif item.get("note"):
# some reason we can't install
status = item.get("note")

View File

@@ -366,11 +366,11 @@ def curl(url, destinationpath,
else:
temp_download_exists = os.path.isfile(tempdownloadpath)
http_result = header.get('http_result_code')
if http_result.startswith('2') and \
temp_download_exists:
if http_result.startswith('2') and temp_download_exists:
downloadedsize = os.path.getsize(tempdownloadpath)
if downloadedsize >= targetsize:
if not downloadedpercent == 100:
if targetsize and not downloadedpercent == 100:
# need to display a percent done of 100%
munkicommon.display_percent_done(100, 100)
os.rename(tempdownloadpath, destinationpath)
if (resume and not header.get('etag')
@@ -439,8 +439,8 @@ def getResourceIfChangedAtomically(url,
if xattr_hash == expected_hash:
#File is already current, no change.
return False
elif munkicommon.pref('PackageVerificationMode').lower() in \
['hash_strict', 'hash']:
elif munkicommon.pref(
'PackageVerificationMode').lower() in ['hash_strict', 'hash']:
try:
os.unlink(destinationpath)
except OSError:

View File

@@ -28,6 +28,7 @@ import subprocess
import socket
import urllib2
import urlparse
from urllib import quote_plus
from OpenSSL.crypto import load_certificate, FILETYPE_PEM
# our libs
@@ -1589,6 +1590,74 @@ def processOptionalInstall(manifestitem, cataloglist, installinfo):
installinfo['optional_installs'].append(iteminfo)
def updateAvailableLicenseSeats(installinfo):
'''Records # of available seats for each optional install'''
license_info_url = munkicommon.pref('LicenseInfoURL')
if not license_info_url:
# nothing to do!
return
if not installinfo.get('optional_installs'):
# nothing to do!
return
license_info = {}
items_to_check = [item['name']
for item in installinfo['optional_installs']
if not item['installed']]
# complicated logic here to 'batch' process our GET requests but
# keep them under 256 characters each
start_index = 0
while start_index < len(items_to_check):
end_index = len(items_to_check)
while True:
query_items = ['name=' + quote_plus(item)
for item in items_to_check[start_index:end_index]]
querystring = '?' + '&'.join(query_items)
url = license_info_url + querystring
if len(url) < 256:
break
# drop an item and see if we're under 256 characters
end_index = end_index - 1
munkicommon.display_debug1('Fetching licensed seat data from %s' % url)
license_data = getDataFromURL(url)
munkicommon.display_debug1('Got: %s' % license_data)
try:
license_dict = FoundationPlist.readPlistFromString(
license_data)
except FoundationPlist.FoundationPlistException:
munkicommon.display_warning(
'Bad license data from %s: %s'
% (url, license_data))
# should we act as all are zero?
continue
else:
# merge data from license_dict into license_info
license_info.update(license_dict)
start_index = end_index
# use license_info to update our remaining seats
for item in installinfo['optional_installs']:
munkicommon.display_debug2(
'Looking for license info for %s' % item['name'])
if item['name'] in license_info.keys():
# record available seats for this item
munkicommon.display_debug1(
'Recording available seats for %s: %s'
% (item['name'], license_info[item['name']]))
try:
seats_available = int(license_info[item['name']]) > 0
munkicommon.display_debug2(
'Seats available: %s' % seats_available)
item['licensed_seats_available'] = seats_available
except ValueError:
munkicommon.display_warning(
'Bad license data for %s: %s'
% (item['name'], license_info[item['name']]))
item['licensed_seats_available'] = False
def processInstall(manifestitem, cataloglist, installinfo):
"""Processes a manifest item. Determines if it needs to be
installed, and if so, if any items it is dependent on need to
@@ -2706,6 +2775,10 @@ def check(client_id='', localmanifestpath=None):
installinfo)
if munkicommon.stopRequested():
return 0
# verify available license seats for optional installs
if installinfo.get('optional_installs'):
updateAvailableLicenseSeats(installinfo)
# now process any self-serve choices
usermanifest = '/Users/Shared/.SelfServeManifest'
@@ -2740,8 +2813,13 @@ def check(client_id='', localmanifestpath=None):
'**Processing self-serve choices**')
selfserveinstalls = getManifestValueForKey(selfservemanifest,
'managed_installs')
# build list of items in the optional_installs list
# that have not exceeded available seats
available_optional_installs = [item['name']
for item in installinfo.get('optional_installs', [])]
for item in installinfo.get('optional_installs', [])
if (not 'licensed_seats_available' in item
or item['licensed_seats_available'])]
if selfserveinstalls:
# filter the list, removing any items not in the current list
# of available self-serve installs
@@ -3079,6 +3157,28 @@ def checkForceInstallPackages():
return result
def getDataFromURL(url):
'''Returns data from url as string. We use the existing
getResourceIfChangedAtomically function so any custom
authentication/authorization headers are reused'''
urldata = os.path.join(munkicommon.tmpdir, 'urldata')
if os.path.exists(urldata):
try:
os.unlink(urldata)
except (IOError, OSError), err:
munkicommon.display_warning('Error in getDataFromURL' % err)
unused_result = getResourceIfChangedAtomically(url, urldata)
try:
fdesc = open(urldata)
data = fdesc.read()
fdesc.close()
os.unlink(urldata)
return data
except (IOError, OSError), err:
munkicommon.display_warning('Error in getDataFromURL' % err)
return ''
def getResourceIfChangedAtomically(url,
destinationpath,
message=None,