diff --git a/src/GamCommands.txt b/src/GamCommands.txt index e8b902f6..b5372e24 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -614,11 +614,11 @@ gam calendar deleteevent (id|eventid )* (query|eventquer gam calendar wipe gam update cros (+)|(action deprovision_same_model_replace|deprovision_different_model_replace|deprovision_retiring_device|disable|reenable [acknowledge_device_touch_requirement]) -gam info cros [nolists] [listlimit ] +gam info cros [nolists] [listlimit ] [start ] [end ] [basic|full|allfields] * [fields ] gam print cros [todrive] [query ] - [orderby [ascending|descending]] [nolists] [listlimit ] + [orderby [ascending|descending]] [nolists] [listlimit ] [start ] [end ] [basic|full|allfields] * [fields ] gam print @@ -635,9 +635,22 @@ The full argument yields all column headers including two headers, recentUsers a that repeat with two subvalues each, yielding a large number of columns that make the output hard to process. The nolists argument suppresses these two headers; if you want these headers in a more manageable form use the following arguments. The listlimit argument limits the number of repetitions to . If equals zero, there is no limit. +The start and end arguments filter the time ranges. If recentusers is specified as a field, each pair of values for recentUsers is put on a separate row with all of the other headers. If timeranges is specified as a field, each pair of values for activeTimeRanges is put on a separate row with all of the other headers. +gam print crosactivity [todrive] [query ] + [recentusers] [timeranges] [both] [listlimit ] [start ] [end ] [delimiter ] + +The basic column headers are: deviceId,annotatedAssetId,annotatedLocation,serialNumber,orgUnitPath. +If recentusers is specified, all of the recent users email addresses, separated by the delimiter , +are put on a separate row, with header recentUsers.email, in addition to the basic headers. +If timeranges is specified, each pair of time range values, activeTimeRanges.date and activeTimeRanges.duration, +are put on a separate row with the basic headers. +The default is to include both recentusers and timeranges. +The listlimit argument limits the number of recent users and time ranges to . If equals zero, there is no limit. +The start and end arguments filter the time ranges. + gam update mobile + gam delete mobile gam info mobile diff --git a/src/gam.py b/src/gam.py index 20711d9a..f73d6d64 100755 --- a/src/gam.py +++ b/src/gam.py @@ -4909,7 +4909,7 @@ def addSmime(users): myarg = sys.argv[i].lower() if myarg == u'file': smimefile = sys.argv[i+1] - body[u'pkcs12'] = base64.urlsafe_b64encode(readFile(smimefile, mode =u'rb')) + body[u'pkcs12'] = base64.urlsafe_b64encode(readFile(smimefile, mode=u'rb')) i += 2 elif myarg == u'password': body[u'encryptedKeyPassword'] = sys.argv[i+1] @@ -8002,6 +8002,16 @@ def doGetResourceCalendarInfo(): continue print u'%s: %s' % (key, value) +def _filterTimeRanges(activeTimeRanges, startDate, endDate): + if startDate is None and endDate is None: + return activeTimeRanges + filteredTimeRanges = [] + for timeRange in activeTimeRanges: + activityDate = datetime.datetime.strptime(timeRange[u'date'], YYYYMMDD_FORMAT) + if ((startDate is None) or (activityDate >= startDate)) and ((endDate is None) or (activityDate <= endDate)): + filteredTimeRanges.append(timeRange) + return filteredTimeRanges + def doGetCrosInfo(): cd = buildGAPIObject(u'directory') deviceId = sys.argv[3] @@ -8017,6 +8027,7 @@ def doGetCrosInfo(): projection = None fieldsList = [] noLists = False + startDate = endDate = None listLimit = 0 i = 4 while i < len(sys.argv): @@ -8024,6 +8035,12 @@ def doGetCrosInfo(): if myarg == u'nolists': noLists = True i += 1 + elif myarg in CROS_START_ARGUMENTS: + startDate = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + i += 2 + elif myarg in CROS_END_ARGUMENTS: + endDate = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + i += 2 elif myarg == u'listlimit': listLimit = int(sys.argv[i+1]) i += 2 @@ -8050,7 +8067,7 @@ def doGetCrosInfo(): for field in fieldNameList.lower().replace(u',', u' ').split(): if field in CROS_ARGUMENT_TO_PROPERTY_MAP: fieldsList.extend(CROS_ARGUMENT_TO_PROPERTY_MAP[field]) - if field in [u'recentusers', u'timeranges', u'activetimeranges']: + if field in CROS_ACTIVE_TIME_RANGES_ARGUMENTS+CROS_RECENT_USERS_ARGUMENTS: projection = u'FULL' noLists = False else: @@ -8064,9 +8081,10 @@ def doGetCrosInfo(): fields = u','.join(set(fieldsList)).replace(u'.', u'/') else: fields = None - i = 1 + i = 0 device_count = len(devices) for deviceId in devices: + i += 1 cros = callGAPI(cd.chromeosdevices(), u'get', customerId=GC_Values[GC_CUSTOMER_ID], deviceId=deviceId, projection=projection, fields=fields) print u'CrOS Device: {0} ({1} of {2})'.format(deviceId, i, device_count) @@ -8076,22 +8094,21 @@ def doGetCrosInfo(): if up in cros: print u' {0}: {1}'.format(up, cros[up]) if not noLists: - activeTimeRanges = cros.get(u'activeTimeRanges', []) + activeTimeRanges = _filterTimeRanges(cros.get(u'activeTimeRanges', []), startDate, endDate) lenATR = len(activeTimeRanges) if lenATR: print u' activeTimeRanges' - for i in xrange(min(listLimit, lenATR) if listLimit else lenATR): - print u' date: {0}'.format(activeTimeRanges[i][u'date']) - print u' activeTime: {0}'.format(str(activeTimeRanges[i][u'activeTime'])) - print u' duration: {0}'.format(utils.formatMilliSeconds(activeTimeRanges[i][u'activeTime'])) + for activeTimeRange in activeTimeRanges[:min(lenATR, listLimit or lenATR)]: + print u' date: {0}'.format(activeTimeRange[u'date']) + print u' activeTime: {0}'.format(str(activeTimeRange[u'activeTime'])) + print u' duration: {0}'.format(utils.formatMilliSeconds(activeTimeRange[u'activeTime'])) recentUsers = cros.get(u'recentUsers', []) lenRU = len(recentUsers) if lenRU: print u' recentUsers' - for i in xrange(min(listLimit, lenRU) if listLimit else lenRU): - print u' type: {0}'.format(recentUsers[i][u'type']) - print u' email: {0}'.format(recentUsers[i].get(u'email', u'')) - i += 1 + for recentUser in recentUsers[:min(lenRU, listLimit or lenRU)]: + print u' type: {0}'.format(recentUser[u'type']) + print u' email: {0}'.format(recentUser.get(u'email', [u'Unknown', u'UnmanagedUser'][recentUser[u'type'] == u'USER_TYPE_UNMANAGED'])) def doGetMobileInfo(): cd = buildGAPIObject(u'directory') @@ -9472,10 +9489,12 @@ def doPrintMobileDevices(): def doPrintCrosActivity(): cd = buildGAPIObject(u'directory') todrive = False - titles = [u'deviceId', u'annotatedAssetId', u'serialNumber', u'orgUnitPath'] + titles = [u'deviceId', u'annotatedAssetId', u'annotatedLocation', u'serialNumber', u'orgUnitPath'] csvRows = [] - device_fields = [u'annotatedAssetId', u'deviceId', u'orgUnitPath', u'serialNumber'] - oldest_date = None + device_fields = [u'deviceId', u'annotatedAssetId', u'annotatedLocation', u'serialNumber', u'orgUnitPath'] + startDate = endDate = None + listLimit = 0 + delimiter = u',' query = None i = 3 while i < len(sys.argv): @@ -9486,66 +9505,67 @@ def doPrintCrosActivity(): elif myarg == u'todrive': todrive = True i += 1 - elif myarg == u'times': + elif myarg in CROS_ACTIVE_TIME_RANGES_ARGUMENTS: device_fields.append(u'activeTimeRanges') i += 1 - elif myarg == u'users': + elif myarg in CROS_RECENT_USERS_ARGUMENTS: device_fields.append(u'recentUsers') i += 1 elif myarg == u'both': - device_fields.append(u'recentUsers') - device_fields.append(u'activeTimeRanges') + device_fields.extend([u'recentUsers', u'activeTimeRanges']) i += 1 - elif myarg == u'oldestdate': - oldest_date = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + elif myarg in CROS_START_ARGUMENTS: + startDate = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + i += 2 + elif myarg in CROS_END_ARGUMENTS: + endDate = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + i += 2 + elif myarg == u'listlimit': + listLimit = int(sys.argv[i+1]) + i += 2 + elif myarg == u'delimiter': + delimiter = sys.argv[i+1] i += 2 else: print u'ERROR: %s is not a valid argument for "gam print crosactivity"' % sys.argv[i] sys.exit(2) if u'recentUsers' not in device_fields and u'activeTimeRanges' not in device_fields: - device_fields.append(u'recentUsers') - device_fields.append(u'activeTimeRanges') + device_fields.extend([u'recentUsers', u'activeTimeRanges']) if u'recentUsers' in device_fields: - titles.append(u'recent_users') + titles.append(u'recentUsers.email') if u'activeTimeRanges' in device_fields: - titles.append(u'activity_date') - titles.append(u'active_minutes') + titles.extend([u'activeTimeRanges.date', u'activeTimeRanges.duration']) fields = u'chromeosdevices(%s),nextPageToken' % u','.join(device_fields) sys.stderr.write(u'Retrieving All Chrome OS Devices for organization (may take some time for large accounts)...\n') page_message = u'Got %%num_items%% Chrome devices...\n' all_cros = callGAPIpages(cd.chromeosdevices(), u'list', u'chromeosdevices', page_message=page_message, query=query, customerId=GC_Values[GC_CUSTOMER_ID], projection=u'FULL', fields=fields, maxResults=GC_Values[GC_DEVICE_MAX_RESULTS]) - if all_cros: - for cros in all_cros: - if u'activeTimeRanges' in cros: - for time_range in cros[u'activeTimeRanges']: - row_date = time_range[u'date'] - if oldest_date: - row_time = datetime.datetime.strptime(row_date, u'%Y-%m-%d') - if row_time < oldest_date: - continue - row = {u'activity_date': row_date, u'active_minutes': time_range[u'activeTime']/1000/60} - for attrib in cros: - if attrib in [u'kind', u'etag', u'recentUsers', u'activeTimeRanges']: - continue - row[attrib] = cros[attrib] - csvRows.append(row) - if u'recentUsers' in cros: - recentusers = [] - for recentuser in cros[u'recentUsers']: - if u'email' in recentuser: - recentusers.append(recentuser[u'email']) - elif recentuser[u'type'] == u'USER_TYPE_UNMANAGED': - recentusers.append(u'UnmanagedUser') - else: - recentusers.append(u'Unknown') - row = {u'recent_users': u','.join(recentusers)} - for attrib in cros: - if attrib in [u'kind', u'etag', u'recentUsers', u'activeTimeRanges']: - continue - row[attrib] = cros[attrib] - csvRows.append(row) + for cros in all_cros: + row = {} + for attrib in cros: + if attrib not in [u'recentUsers', u'activeTimeRanges']: + row[attrib] = cros[attrib] + if u'activeTimeRanges' in cros: + activeTimeRanges = _filterTimeRanges(cros[u'activeTimeRanges'], startDate, endDate) + lenATR = len(activeTimeRanges) + for activeTimeRange in activeTimeRanges[:min(lenATR, listLimit or lenATR)]: + new_row = row.copy() + new_row[u'activeTimeRanges.date'] = activeTimeRange[u'date'] + new_row[u'activeTimeRanges.duration'] = utils.formatMilliSeconds(activeTimeRange[u'activeTime']) + csvRows.append(new_row) + if u'recentUsers' in cros: + recentUsers = [] + for recentUser in cros[u'recentUsers']: + if u'email' in recentUser: + recentUsers.append(recentUser[u'email']) + elif recentUser[u'type'] == u'USER_TYPE_UNMANAGED': + recentUsers.append(u'UnmanagedUser') + else: + recentUsers.append(u'Unknown') + lenRU = len(recentUsers) + row[u'recentUsers.email'] = delimiter.join(recentUsers[:min(lenRU, listLimit or lenRU)]) + csvRows.append(row) writeCSVfile(csvRows, titles, u'CrOS Activity', todrive) def doPrintCrosDevices(): @@ -9559,8 +9579,9 @@ def doPrintCrosDevices(): sortHeaders = False query = projection = orderBy = sortOrder = None noLists = False + selectActiveTimeRanges = selectRecentUsers = False + startDate = endDate = None listLimit = 0 - selectActiveTimeRanges = selectRecentUsers = None i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -9572,22 +9593,28 @@ def doPrintCrosDevices(): i += 1 elif myarg == u'nolists': noLists = True - selectActiveTimeRanges = selectRecentUsers = None + selectActiveTimeRanges = selectRecentUsers = False i += 1 - elif myarg == u'recentusers': + elif myarg in CROS_ACTIVE_TIME_RANGES_ARGUMENTS: projection = u'FULL' - selectRecentUsers = u'recentUsers' + selectActiveTimeRanges = True noLists = False if fieldsList: - fieldsList.append(selectRecentUsers) + fieldsList.append(u'activeTimeRanges') i += 1 - elif myarg in [u'timeranges', u'activetimeranges']: + elif myarg in CROS_RECENT_USERS_ARGUMENTS: projection = u'FULL' - selectActiveTimeRanges = u'activeTimeRanges' + selectRecentUsers = True noLists = False if fieldsList: - fieldsList.append(selectActiveTimeRanges) + fieldsList.append(u'recentUsers') i += 1 + elif myarg in CROS_START_ARGUMENTS: + startDate = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + i += 2 + elif myarg in CROS_END_ARGUMENTS: + endDate = datetime.datetime.strptime(sys.argv[i+1], u'%Y-%m-%d') + i += 2 elif myarg == u'listlimit': listLimit = int(sys.argv[i+1]) i += 2 @@ -9636,13 +9663,13 @@ def doPrintCrosDevices(): for field in fieldNameList.lower().replace(u',', u' ').split(): if field in CROS_ARGUMENT_TO_PROPERTY_MAP: addFieldToCSVfile(field, CROS_ARGUMENT_TO_PROPERTY_MAP, fieldsList, fieldsTitles, titles) - if field == u'recentusers': + if field in CROS_RECENT_USERS_ARGUMENTS: projection = u'FULL' - selectRecentUsers = u'recentUsers' + selectRecentUsers = True noLists = False - elif field in [u'timeranges', u'activetimeranges']: + elif field in CROS_ACTIVE_TIME_RANGES_ARGUMENTS: projection = u'FULL' - selectActiveTimeRanges = u'activeTimeRanges' + selectActiveTimeRanges = True noLists = False else: print u'ERROR: %s is not a valid argument for "gam print cros fields"' % field @@ -9660,46 +9687,43 @@ def doPrintCrosDevices(): all_cros = callGAPIpages(cd.chromeosdevices(), u'list', u'chromeosdevices', page_message=page_message, query=query, customerId=GC_Values[GC_CUSTOMER_ID], projection=projection, orderBy=orderBy, sortOrder=sortOrder, fields=fields, maxResults=GC_Values[GC_DEVICE_MAX_RESULTS]) - if all_cros: - if (not noLists) and (not selectActiveTimeRanges) and (not selectRecentUsers): - for cros in all_cros: - if u'notes' in cros: - cros[u'notes'] = cros[u'notes'].replace(u'\n', u'\\n') - addRowTitlesToCSVfile(flatten_json(cros, listLimit=listLimit), csvRows, titles) - else: - if not noLists: - if selectActiveTimeRanges: - for attrib in [u'activeTimeRanges.activeTime', u'activeTimeRanges.date']: - titles.append(attrib) - if selectRecentUsers: - for attrib in [u'recentUsers.email', u'recentUsers.type']: - titles.append(attrib) - for cros in all_cros: - if u'notes' in cros: - cros[u'notes'] = cros[u'notes'].replace(u'\n', u'\\n') - row = {} - for attrib in cros: - if attrib in [u'kind', u'etag', u'recentUsers', u'activeTimeRanges']: - continue + if (not noLists) and (not selectActiveTimeRanges) and (not selectRecentUsers): + for cros in all_cros: + if u'notes' in cros: + cros[u'notes'] = cros[u'notes'].replace(u'\n', u'\\n') + addRowTitlesToCSVfile(flatten_json(cros, listLimit=listLimit), csvRows, titles) + else: + if not noLists: + if selectActiveTimeRanges: + titles.extend([u'activeTimeRanges.date', u'activeTimeRanges.activeTime', u'activeTimeRanges.duration']) + if selectRecentUsers: + titles.extend([u'recentUsers.email', u'recentUsers.type']) + for cros in all_cros: + if u'notes' in cros: + cros[u'notes'] = cros[u'notes'].replace(u'\n', u'\\n') + row = {} + for attrib in cros: + if attrib not in [u'kind', u'etag', u'recentUsers', u'activeTimeRanges']: if attrib not in titles: titles.append(attrib) row[attrib] = cros[attrib] - activeTimeRanges = cros.get(selectActiveTimeRanges, []) if selectActiveTimeRanges else [] - recentUsers = cros.get(selectRecentUsers, []) if selectRecentUsers else [] - if noLists or (not activeTimeRanges and not recentUsers): - csvRows.append(row) - else: - lenATR = len(activeTimeRanges) - lenRU = len(recentUsers) - for i in xrange(min(listLimit, max(lenATR, lenRU)) if listLimit else max(lenATR, lenRU)): - new_row = row.copy() - if i < lenATR: - new_row[u'activeTimeRanges.activeTime'] = str(activeTimeRanges[i][u'activeTime']) - new_row[u'activeTimeRanges.date'] = activeTimeRanges[i][u'date'] - if i < lenRU: - new_row[u'recentUsers.email'] = recentUsers[i].get(u'email', u'') - new_row[u'recentUsers.type'] = recentUsers[i][u'type'] - csvRows.append(new_row) + activeTimeRanges = _filterTimeRanges(cros.get(u'activeTimeRanges', []), startDate, endDate) if selectActiveTimeRanges else [] + recentUsers = cros.get(u'recentUsers', []) if selectRecentUsers else [] + if noLists or (not activeTimeRanges and not recentUsers): + csvRows.append(row) + else: + lenATR = len(activeTimeRanges) + lenRU = len(recentUsers) + for i in xrange(min(listLimit, max(lenATR, lenRU)) if listLimit else max(lenATR, lenRU)): + new_row = row.copy() + if i < lenATR: + new_row[u'activeTimeRanges.date'] = activeTimeRanges[i][u'date'] + new_row[u'activeTimeRanges.activeTime'] = str(activeTimeRanges[i][u'activeTime']) + new_row[u'activeTimeRanges.duration'] = utils.formatMilliSeconds(activeTimeRanges[i][u'activeTime']) + if i < lenRU: + new_row[u'recentUsers.email'] = recentUsers[i].get(u'email', [u'Unknown', u'UnmanagedUser'][recentUsers[i][u'type'] == u'USER_TYPE_UNMANAGED']) + new_row[u'recentUsers.type'] = recentUsers[i][u'type'] + csvRows.append(new_row) if sortHeaders: sortCSVTitles([u'deviceId',], titles) writeCSVfile(csvRows, titles, u'CrOS', todrive) diff --git a/src/var.py b/src/var.py index 65aa410d..c845031c 100644 --- a/src/var.py +++ b/src/var.py @@ -442,7 +442,9 @@ CROS_ARGUMENT_TO_PROPERTY_MAP = { u'supportenddate': [u'supportEndDate',], u'tag': [u'annotatedAssetId',], u'timeranges': [u'activeTimeRanges.activeTime', u'activeTimeRanges.date'], + u'times': [u'activeTimeRanges.activeTime', u'activeTimeRanges.date'], u'user': [u'annotatedUser',], + u'users': [u'recentUsers.email', u'recentUsers.type'], u'willautorenew': [u'willAutoRenew',], } @@ -471,6 +473,11 @@ CROS_SCALAR_PROPERTY_PRINT_ORDER = [ u'willAutoRenew', ] +CROS_RECENT_USERS_ARGUMENTS = [u'recentusers', u'users'] +CROS_ACTIVE_TIME_RANGES_ARGUMENTS = [u'timeranges', u'activetimeranges', u'times'] +CROS_START_ARGUMENTS = [u'start', u'startdate', u'oldestdate'] +CROS_END_ARGUMENTS = [u'end', u'enddate'] + # # Global variables #