Initial commit for conditional items framework extension

This commit is contained in:
Heig Gregorian
2012-03-02 14:10:34 -08:00
parent ba1238b452
commit afce440698
3 changed files with 141 additions and 2 deletions

View File

@@ -0,0 +1,53 @@
#!/usr/bin/python
'''This is a basic example of a conditional script which outputs 2 key/value pairs:
Examples:
if_name,en0
ip_address,192.168.1.128
NOTE: Information gathered is ONLY for the primary interface'''
from SystemConfiguration import * # from pyObjC
import socket
import collections
import os
NETWORK_INFO = {}
def getIPAddress(service_uuid):
# print service_uuid
ds = SCDynamicStoreCreate(None, 'GetIPv4Addresses', None, None)
newpattern = SCDynamicStoreKeyCreateNetworkServiceEntity(None,
kSCDynamicStoreDomainState,
service_uuid,
kSCEntNetIPv4)
newpatterns = CFArrayCreate(None, (newpattern, ), 1, kCFTypeArrayCallBacks)
ipaddressDict = SCDynamicStoreCopyMultiple(ds, None, newpatterns)
for ipaddress in ipaddressDict.values():
ipaddy = ipaddress['Addresses'][0]
return ipaddy
def getNetworkInfo():
ds = SCDynamicStoreCreate(None, 'GetIPv4Addresses', None, None)
pattern = SCDynamicStoreKeyCreateNetworkGlobalEntity(None,
kSCDynamicStoreDomainState,
kSCEntNetIPv4);
patterns = CFArrayCreate(None, (pattern, ), 1, kCFTypeArrayCallBacks)
valueDict = SCDynamicStoreCopyMultiple(ds, None, patterns)
ipv4info = collections.namedtuple('ipv4info', 'ifname ip router service')
for serviceDict in valueDict.values():
ifname = serviceDict[u'PrimaryInterface']
NETWORK_INFO['interface'] = serviceDict[u'PrimaryInterface']
NETWORK_INFO['service_uuid'] = serviceDict[u'PrimaryService']
NETWORK_INFO['router'] = serviceDict[u'Router']
NETWORK_INFO['ip_address'] = getIPAddress(serviceDict[u'PrimaryService'])
print "if_name,%s" % ifname
print "ip_address,%s" % NETWORK_INFO['ip_address']
getNetworkInfo()

View File

@@ -43,7 +43,7 @@ from distutils import version
from types import StringType
from xml.dom import minidom
from Foundation import NSDate, NSMetadataQuery, NSPredicate, NSRunLoop
from Foundation import NSArray, NSDate, NSMetadataQuery, NSPredicate, NSRunLoop
from Foundation import CFPreferencesCopyAppValue
from Foundation import CFPreferencesSetValue
from Foundation import CFPreferencesAppSynchronize
@@ -1941,6 +1941,59 @@ def getMachineFacts():
return MACHINE
CONDITIONS = {}
def getConditions():
"""Fetches key/value pairs from condition scripts
which can be placed into /usr/local/munki/conditions"""
if not CONDITIONS:
# define path to conditions directory which would contain admin created scripts
scriptdir = os.path.realpath(os.path.dirname(sys.argv[0]))
conditionsdir = os.path.join(scriptdir, "conditions")
if os.path.exists(conditionsdir):
from munkilib import utils
for condition_script in listdir(conditionsdir):
# grab path to each condition script
condition_script_path = os.path.join(conditionsdir, condition_script)
try:
# attempt to execute condition script
result, stdout, stderr = utils.runExternalScript(condition_script_path)
# condition scripts may contain multi-line output,
# each representing a key/value pair
condition_stdout = stdout.splitlines()
for condition in condition_stdout:
# format and prepare each line for inclusion into the CONDITIONS dict
condition = str(condition)
key_value_pair = filter(len,[x.strip() for x in condition.split(',')])
key_value_length = len(key_value_pair)
if key_value_length == 2:
# traditional key/value pairing
condition_key = key_value_pair[0]
condition_value = key_value_pair[1]
elif key_value_length > 2:
# 'complex' key/value pairing - value is a list of multiple values
# keys with multiple values are cast as an NSArray.
# This allows for a slightly different predicate evaluation
condition_key = key_value_pair[0]
condition_value = key_value_pair[1:]
condition_value = NSArray.arrayWithArray_(condition_value)
else:
# key/value pair is invalid
pass
try:
# Build dict of condition key/value pairs
CONDITIONS[condition_key] = condition_value
except:
display_warning('No valid key/value pairs: %s', condition_script)
except utils.ScriptNotFoundError:
pass # script is not required, so pass
except utils.RunExternalScriptError, e:
print >> sys.stderr, str(e)
else:
# /usr/local/munki/conditions does not exist
pass
return CONDITIONS
def isAppRunning(appname):
"""Tries to determine if the application in appname is currently
running"""

View File

@@ -1630,11 +1630,28 @@ def makePredicateInfoObject():
INFO_OBJECT['machine_type'] = 'laptop'
else:
INFO_OBJECT['machine_type'] = 'desktop'
for key in CONDITIONS.keys():
INFO_OBJECT[key] = CONDITIONS[key]
def predicateEvaluatesAsTrue(predicate_string):
'''Evaluates predicate against our info object'''
munkicommon.display_debug1('Evaluating predicate: %s' % predicate_string)
# Parse condition item key from the predicate string
condition_key = predicate_string.split()[0]
if not condition_key in INFO_OBJECT:
# Stop processing a predicate if it's conditional item key has not been defined
munkicommon.display_warning('Condition "%s" is undefined', condition_key)
return False
valueIsArray = False
if "array" in str(type(INFO_OBJECT[condition_key])).lower():
# Test if the key's value is an array and prepare a new predicate string
predicate_string_orig = predicate_string
# Manipulate predicate_string in prep for different evaluation
predicate_string = predicate_string.replace(condition_key,'SELF')
valueIsArray = True
try:
p = NSPredicate.predicateWithFormat_(predicate_string)
except Exception, e:
@@ -1642,7 +1659,18 @@ def predicateEvaluatesAsTrue(predicate_string):
# can't parse predicate, so return False
return False
result = p.evaluateWithObject_(INFO_OBJECT)
if not valueIsArray:
# Traditional key/value pair evaluation
result = p.evaluateWithObject_(INFO_OBJECT)
else:
# Complex key/value pair evaluation
if INFO_OBJECT[condition_key].filteredArrayUsingPredicate_(p):
result = True
else:
result = False
# Set predicate string back to it's original form for easier comprehension
predicate_string = predicate_string_orig
munkicommon.display_debug1(
'Predicate %s is %s' % (predicate_string, result))
return result
@@ -2308,6 +2336,7 @@ def getDownloadCachePath(destinationpathprefix, url):
destinationpathprefix, getInstallerItemBasename(url))
MACHINE = {}
CONDITIONS = {}
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,
@@ -2318,6 +2347,10 @@ def check(client_id='', localmanifestpath=None):
MACHINE = munkicommon.getMachineFacts()
munkicommon.report['MachineInfo'] = MACHINE
global CONDITIONS
munkicommon.getConditions()
CONDITIONS = munkicommon.getConditions()
ManagedInstallDir = munkicommon.pref('ManagedInstallDir')
if munkicommon.munkistatusoutput:
munkistatus.activate()