Skip to content

Commit 4702013

Browse files
authored
Merge pull request #52 from salopensource/bugfix-apple-install-times
Bugfix apple install times
2 parents 890b263 + 16776d0 commit 4702013

File tree

1 file changed

+82
-85
lines changed

1 file changed

+82
-85
lines changed

payload/usr/local/sal/checkin_modules/apple_sus_checkin.py

Lines changed: 82 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,29 @@
22

33

44
import datetime
5+
import platform
6+
import plistlib
57
import re
68
import subprocess
79
import sys
10+
import xml.parsers.expat
811
from distutils.version import StrictVersion
912

1013
sys.path.insert(0, '/usr/local/sal')
1114
import utils
1215

1316

14-
__version__ = '1.0.0'
17+
__version__ = '1.0.1'
1518

1619

1720
def main():
1821
sus_submission = {}
19-
sus_report = get_sus_install_report()
20-
2122
sus_submission['facts'] = get_sus_facts()
2223

2324
# Process managed items and update histories.
24-
sus_submission['managed_items'] = {}
25+
sus_submission['managed_items'] = get_sus_install_report()
2526
sus_submission['update_history'] = []
2627

27-
for item in sus_report:
28-
name, version, date = item
29-
submission_item = {}
30-
submission_item['date_managed'] = date
31-
submission_item['status'] = 'PRESENT'
32-
submission_item['data'] = {'type': 'Apple SUS Install', 'version': version}
33-
sus_submission['managed_items'][name] = submission_item
34-
3528
pending = get_pending()
3629
sus_submission['managed_items'].update(pending)
3730

@@ -40,60 +33,19 @@ def main():
4033

4134
def get_sus_install_report():
4235
"""Return installed apple updates from softwareupdate"""
43-
cmd = ['softwareupdate', '--history']
4436
try:
45-
output = subprocess.check_output(cmd)
46-
except subprocess.CalledProcessError:
47-
# This is a new argument and not supported on all OS versions
48-
return []
49-
50-
# Example output:
51-
# macOS Mojave 10.14.1 11/06/2018, 08:41:49
52-
# macOS 10.14.1 Update 11/02/2018, 13:24:17
53-
# Command Line Tools (macOS High Sierra version 10.13) for Xcode 10.1 11/02/2018, 12:36:15
54-
55-
# Line one is a "normal" line, name, version, and date are separated
56-
# by 2+ spaces.
57-
# Line two has no version number.
58-
# Line three has such a long name that they decided to output only
59-
# one space between the name and the version.
60-
61-
# Drop the header and do an initial split on 2 or more whitespace
62-
mostly_parsed = [re.split(r' {2,}', l.strip()) for l in output.splitlines()[2:]]
63-
results = []
64-
for line in mostly_parsed:
65-
# If we have three lines, everything is fine.
66-
if len(line) == 2:
67-
# Some long update names are displayed without a minimum 2
68-
# space delimiter, so we have to split them again.
69-
# This time, we split on a single space and then see if the
70-
# second item can be cast to a StrictVersion.
71-
attempt = line[0].rsplit(' ', 1)
72-
if len(attempt) == 2:
73-
try:
74-
# If we got a StrictVersion, then use our split
75-
# results
76-
StrictVersion(attempt[1])
77-
name = attempt[0]
78-
version = attempt[1]
79-
except ValueError:
80-
# Otherwise, there's no versionm, just a name.
81-
name = line[0]
82-
version = None
83-
84-
else:
85-
# I haven't seen examples of this (name with no spaces
86-
# and a date), but it's here just in case.
87-
name = line[0]
88-
version = None
89-
else:
90-
name = line[0]
91-
version = line[1]
92-
93-
installed = datetime.datetime.strptime(line[-1], '%m/%d/%Y, %H:%M:%S')
94-
results.append([name, version, installed])
95-
96-
return results
37+
history = plistlib.readPlist('/Library/Receipts/InstallHistory.plist')
38+
except (IOError, xml.parsers.expat.ExpatError):
39+
history = []
40+
return {
41+
i['displayName']: {
42+
'date_managed': i['date'],
43+
'status': 'PRESENT',
44+
'data': {
45+
'type': 'Apple SUS Install',
46+
'version': i['displayVersion'].strip()
47+
}
48+
} for i in history if i['processName'] == 'softwareupdated'}
9749

9850

9951
def get_sus_facts():
@@ -162,26 +114,71 @@ def get_pending():
162114
except subprocess.CalledProcessError:
163115
return pending_items
164116

165-
# Example output
166-
167-
# Software Update Tool
168-
169-
# Software Update found the following new or updated software:
170-
# * macOS High Sierra 10.13.6 Update-
171-
# macOS High Sierra 10.13.6 Update ( ), 1931648K [recommended] [restart]
172-
# * iTunesX-12.8.2
173-
# iTunes (12.8.2), 273564K [recommended]
174-
175-
for line in output.splitlines():
176-
if line.strip().startswith('*'):
177-
item = {'date_managed': datetime.datetime.utcnow().isoformat() + 'Z'}
178-
item['status'] = 'PENDING'
179-
pending_items[line.strip()[2:]] = item
180-
181-
return pending_items
182-
183-
184-
117+
# The following regex code is from Shea Craig's work on the Salt
118+
# mac_softwareupdate module. Reference that for future updates.
119+
if StrictVersion(platform.mac_ver()[0]) >= StrictVersion('10.15'):
120+
# Example output:
121+
# Software Update Tool
122+
#
123+
# Finding available software
124+
# Software Update found the following new or updated software:
125+
# * Label: Command Line Tools beta 5 for Xcode-11.0
126+
# Title: Command Line Tools beta 5 for Xcode, Version: 11.0, Size: 224804K, Recommended: YES,
127+
# * Label: macOS Catalina Developer Beta-6
128+
# Title: macOS Catalina Public Beta, Version: 5, Size: 3084292K, Recommended: YES, Action: restart,
129+
# * Label: BridgeOSUpdateCustomer
130+
# Title: BridgeOSUpdateCustomer, Version: 10.15.0.1.1.1560926689, Size: 390674K, Recommended: YES, Action: shut down,
131+
# - Label: iCal-1.0.2
132+
# Title: iCal, Version: 1.0.2, Size: 6520K,
133+
rexp = re.compile(
134+
r'(?m)' # Turn on multiline matching
135+
r'^\s*[*-] Label: ' # Name lines start with * or - and "Label: "
136+
r'(?P<name>[^ ].*)[\r\n]' # Capture the rest of that line; this is the update name.
137+
r'.*Version: (?P<version>[^,]*), ' # Grab the version number.
138+
r'Size: (?P<size>[^,]*),\s*' # Grab the size; unused at this time.
139+
r'(?P<recommended>Recommended: YES,)?\s*' # Optionally grab the recommended flag.
140+
r'(?P<action>Action: (?:restart|shut down),)?' # Optionally grab an action.
141+
)
142+
else:
143+
# Example output:
144+
# Software Update Tool
145+
#
146+
# Finding available software
147+
# Software Update found the following new or updated software:
148+
# * Command Line Tools (macOS Mojave version 10.14) for Xcode-10.3
149+
# Command Line Tools (macOS Mojave version 10.14) for Xcode (10.3), 199140K [recommended]
150+
# * macOS 10.14.1 Update
151+
# macOS 10.14.1 Update (10.14.1), 199140K [recommended] [restart]
152+
# * BridgeOSUpdateCustomer
153+
# BridgeOSUpdateCustomer (10.14.4.1.1.1555388607), 328394K, [recommended] [shut down]
154+
# - iCal-1.0.2
155+
# iCal, (1.0.2), 6520K
156+
rexp = re.compile(
157+
r'(?m)' # Turn on multiline matching
158+
r'^\s+[*-] ' # Name lines start with 3 spaces and either a * or a -.
159+
r'(?P<name>.*)[\r\n]' # The rest of that line is the name.
160+
r'.*\((?P<version>[^ \)]*)' # Capture the last parenthesized value on the next line.
161+
r'[^\r\n\[]*(?P<recommended>\[recommended\])?\s?' # Capture [recommended] if there.
162+
r'(?P<action>\[(?:restart|shut down)\])?' # Capture an action if present.
163+
)
164+
165+
now = datetime.datetime.utcnow().isoformat() + 'Z'
166+
return {
167+
m.group('name'): {
168+
'date_managed': now,
169+
'status': 'PENDING',
170+
'data': {
171+
'version': m.group('version'),
172+
'recommended': 'TRUE' if 'recommended' in m.group('recommended') else 'FALSE',
173+
'action': _bracket_cleanup(m, 'action')
174+
}
175+
} for m in rexp.finditer(output)
176+
}
177+
178+
179+
def _bracket_cleanup(match, key):
180+
"""Strip out [ and ] and uppercase SUS output"""
181+
return re.sub(r'[\[\]]', '', match.group(key) or '').upper()
185182

186183

187184
if __name__ == "__main__":

0 commit comments

Comments
 (0)