From 8617e9f57fa45b6c410e995d4153188a1956070c Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Sun, 21 Sep 2025 14:44:06 -0700 Subject: [PATCH] Expanded `` to allow specification of non-archived/archived users. --- src/GamCommands.txt | 106 ++++++------ src/GamUpdate.txt | 17 ++ src/gam/__init__.py | 327 ++++++++++++++++++++----------------- src/gam/gamlib/glclargs.py | 290 +++++++++++++++++++++++++++++++- src/gam/gamlib/glentity.py | 10 +- 5 files changed, 542 insertions(+), 208 deletions(-) diff --git a/src/GamCommands.txt b/src/GamCommands.txt index 4a080e1f..6dcc1ae5 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -428,6 +428,8 @@ If an item contains spaces, it should be surrounded by ". ::= draft|published|deleted ::= ::= + ::= + See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes ::= allmail| abridged|daily| @@ -663,11 +665,11 @@ If an item contains spaces, it should be surrounded by ". (tdreturnidonly [])| (tdshare commenter|reader|writer)*| (tdsheet (id:)|)| - (tdsheettimestamp [] [tdsheettimeformat ]) + (tdsheettimestamp [] [tdsheettimeformat ]) (tdsheettitle )| (tdsubject )| ([tdsheetdaysoffset ] [tdsheethoursoffset ])| - (tdtimestamp [] [tdtimeformat ] + (tdtimestamp [] [tdtimeformat ] [tddaysoffset ] [tdhoursoffset ])| (tdtimezone )| (tdtitle )| @@ -902,25 +904,25 @@ Specify a collection of ChromeOS devices by directly specifying them or by speci Specify a collection of Users by directly specifying them or by specifying items that will yield a list of users. ::= - (all users|users_ns|users_susp|users_ns_susp)| + (all users|users_na|users_arch|users_ns|users_susp|users_arch_or_susp|users_na_ns|users_ns_susp)| (user )| (users )| (oauthuser) - (domains|domains_ns|domains_susp )| - (group|group_ns|group_susp|group_inde )| - (groups|groups_ns|groups_susp|groups_inde )| + (domains|domains_na|domains_arch|domains_ns|domains_susp|domains_na_ns )| + (group|group_na|group_arch|group_ns|group_susp|group_na_ns|group_inde )| + (groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde )| (group_inde )|(groups_inde )| - (group_users|group_users_ns|group_users_susp + (group_users|group_users_na|group_users_arch|group_users_ns|group_users_susp|group_users_na_ns [members] [managers] [owners] [primarydomain] [domains ] [recursive|includederivedmembership] end)| (group_users_select [members] [managers] [owners] [notsuspended|suspended] [notarchived|archived] [primarydomain] [domains ] [recursive|includederivedmembership] end)| - (ou|ou_ns|ou_susp )| - (ou_and_children|ou_and_children_ns|ou_and_children_susp )| - (ous|ous_ns|ous_susp )| - (ous_and_children|ous_and_children_ns|ous_and_children_susp )| + (ou|ou_na|ou_arch|ou_ns|ou_susp|ou_na_ns )| + (ou_and_children|ou_and_children_na|ou_and_children_arch|ou_and_children_ns|ou_and_children_susp|ou_and_children_na_ns )| + (ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns )| + (ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns )| (courseparticipants )| (students )| (teachers )| @@ -940,20 +942,24 @@ Specify a collection of Users by directly specifying them or by specifying items (gcsdoc(:)+ )) [warnifnodata] [columndelimiter ] [noescapechar ] [quotechar ] [endcsv|(fields )] - (matchfield|skipfield )* + (matchfield|skipfield )* [delimiter ])| (datafile - users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp| - ous_and_children|ous_and_children_ns|ous_and_children_susp| - courseparticipants|students|teachers + users| + groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde| + ous|ous_na|ous_arch|ous_ns|ous_susps|ous_na_ns| + ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns| + courseparticipants|students|teachers (( [charset ])| (gdoc )| (gcsdoc )) [delimiter ])| (csvdatafile - users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp| - ous_and_children|ous_and_children_ns|ous_and_children_susp| - courseparticipants|students|teachers + users| + groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde| + ous|ous_na|ous_arch|ous_ns|ous_susps|ous_na_ns| + ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns| + courseparticipants|students|teachers (((:)+ [charset ] )| (gsheet(:)+ )| (gdoc(:)+ )| @@ -961,21 +967,23 @@ Specify a collection of Users by directly specifying them or by specifying items (gcsdoc(:)+ )) [warnifnodata] [columndelimiter ] [noescapechar ] [quotechar ] [endcsv|(fields )] - (matchfield|skipfield )* + (matchfield|skipfield )* [delimiter ])| (csvkmd - users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp| - ous_and_children|ous_and_children_ns|ous_and_children_susp| - courseparticipants|students|teachers + users| + groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde| + ous|ous_na|ous_arch|ous_ns|ous_susps|ous_na_ns| + ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns| + courseparticipants|students|teachers ((| (gsheet )| (gdoc )| (gcscsv )| (gcsdoc )) [charset ] [columndelimiter ] [noescapechar ] [quotechar ] [fields ]) - keyfield [keypattern ] [keyvalue ] [delimiter ] - subkeyfield [keypattern ] [keyvalue ] [delimiter ] - (matchfield|skipfield )* + keyfield [keypattern ] [keyvalue ] [delimiter ] + subkeyfield [keypattern ] [keyvalue ] [delimiter ] + (matchfield|skipfield )* [datafield (:)* [delimiter ]]) (csvdata (:*)) @@ -1007,23 +1015,25 @@ Specify a collection of items by directly specifying them; the item type is dete (gcsdoc(:)+ )) [warnifnodata] [columndelimiter ] [noescapechar ] [quotechar ] [endcsv|(fields )] - (matchfield|skipfield )* + (matchfield|skipfield )* [delimiter ] ::= csvkmd - users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp| - ous_and_children|ous_and_children_ns|ous_and_children_susp| - courseparticipants|students|teachers + users| + groups|groups_na|groups_arch|groups_ns|groups_susp|groups_inde| + ous|ous_na|ous_arch|ous_ns|ous_susp| + ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp| + courseparticipants|students|teachers ((| (gsheet )| (gdoc )| (gcscsv )| (gcsdoc )) [charset ] [columndelimiter ] [noescapechar ] [quotechar ] [fields ]) - keyfield [keypattern ] [keyvalue ] [delimiter ] - subkeyfield [keypattern ] [keyvalue ] [delimiter ] - (matchfield|skipfield )* + keyfield [keypattern ] [keyvalue ] [delimiter ] + subkeyfield [keypattern ] [keyvalue ] [delimiter ] + (matchfield|skipfield )* [datafield (:)* [delimiter ]] ::= csvsubkey ::= csvdata (:)* @@ -1379,13 +1389,13 @@ gam tbatch [showcmds []] gam csv [warnifnodata] [columndelimiter ] [noescapechar ] [quotechar ] [fields ] - (matchfield|skipfield )* [showcmds []] + (matchfield|skipfield )* [showcmds []] [skiprows ] [maxrows ] gam gam loop [warnifnodata] [columndelimiter ] [noescapechar ] [quotechar ] [fields ] - (matchfield|skipfield )* [showcmds []] + (matchfield|skipfield )* [showcmds []] [skiprows ] [maxrows ] gam @@ -1532,8 +1542,8 @@ gam print adminroles|roles [todrive *] [formatjson [quotechar ]] gam show adminroles|roles [role ] [privileges] - [nosystemroles] - [formatjson] + [nosystemroles] + [formatjson] gam create|add admin | customer|(org_unit ) [condition securitygroup|nonsecuritygroup] @@ -1582,7 +1592,7 @@ gam print alias|aliases [todrive *] [limittoou ]) [user|users ] [group|groups ] [select ] - [aliasmatchpattern ] + [issuspended ] [isarchived ] [aliasmatchpattern ] [shownoneditable] [nogroups] [nousers] [onerowpertarget] [delimiter ] [suppressnoaliasrows] @@ -1743,7 +1753,7 @@ gam calendar printacl [todrive *] (range )| (recurrence )| (reminder email|popup))| - (resource )| + (resource )| (selectattendees [] [] )| (sequence )| (sharedproperty )| @@ -4470,8 +4480,10 @@ gam update orgs|ous sync [removetoou sync [removetoou ] gam delete orgs|ous -gam info org|ou [nousers|notsuspended|suspended] [children|child] -gam info orgs|ous [nousers|notsuspended|suspended] [children|child] +gam info org|ou + [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child] +gam info orgs|ous + [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child] ::= cros_ou | cros_ou_and_children| @@ -5856,7 +5868,7 @@ gam print users [todrive *] [userview] [basic|full|allfields|(*|fields )] [delimiter ] [sortheaders []] [scalarsfirst []] [formatjson [quotechar ]] [quoteplusphonenumbers] - [issuspended ] [aliasmatchpattern ] + [issuspended ] [isarchived ] [aliasmatchpattern ] [showvalidcolumn] (addcsvdata )* [showitemcountonly] @@ -5873,7 +5885,7 @@ gam print users [todrive *] select [userview] [basic|full|allfields|(*|fields )] [delimiter ] [sortheaders []] [scalarsfirst []] [formatjson [quotechar ]] [quoteplusphonenumbers] - [issuspended ] [aliasmatchpattern ] + [issuspended ] [isarchived ] [aliasmatchpattern ] [showvalidcolumn] (addcsvdata )* [showitemcountonly] @@ -5888,7 +5900,7 @@ gam print users [todrive *] [userview] [basic|full|allfields|(*|fields )] [delimiter ] [sortheaders []] [scalarsfirst []] [formatjson [quotechar ]] [quoteplusphonenumbers] - [issuspended ] [aliasmatchpattern ] + [issuspended ] [isarchived ] [aliasmatchpattern ] [showvalidcolumn] (addcsvdata )* [showitemcountonly] @@ -5909,7 +5921,7 @@ gam print users [todrive *] ([domain|domains ] [(query )|(queries )] [limittoou ] [deleted_only|only_deleted]) [formatjson [quotechar ]] [countsonly|countonly] - [issuspended ] + [issuspended ] [isarchived ] Print user domain counts for specified users. @@ -6688,7 +6700,7 @@ gam create|add drivefile [(localfile |-)|(url )] [(drivefilename|newfilename ) | (replacefilename )*] [stripnameprefix ] [noduplicate] - [timestamp []] [timeformat ] + [timestamp []] [timeformat ] * [(csv [todrive *] (addcsvdata currenttime|)*) | (returnidonly|returnlinkonly|returneditlinkonly|showdetails)] @@ -6709,7 +6721,7 @@ gam update drivefile [copy] [returnidonly|ret [(localfile |-)|(url )] [retainname | (newfilename ) | (replacefilename )*] [stripnameprefix ] - [timestamp []] [timeformat ] + [timestamp []] [timeformat ] * [(gsheet|csvsheet [clearfilter])|(addsheet )] [charset ] [columndelimiter ] @@ -8591,7 +8603,7 @@ gam print shareddriveorganizers [todrive *] [shownorganizerdrives [false|true|only]] [includefileorganizers []] [delimiter ] - + # Users - Force Signout and Turn Off 2-Step Verification gam signout diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index 4a5d5a18..ac5f1f25 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -1,3 +1,20 @@ +7.22.00 + +Expanded `` to allow specification of non-archived/archived users. + * See [Collections of Users](Collections-of-Users) + +These commands have been updated: + * `gam print aliases` + * `gam update groups` + * `gam info orgs` + * `gam print orgs` + * `gam print users` + +Added `datetime ` command that can be embedded in Gam batch files. The current time is formatted with `` +and subsequent lines in `` will have `%datetime%` replaced with the formatted time value. + +See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes + 7.21.03 Added option `notifyrecoveryemail` to `gam create user` and `gam update user password ` diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 15614bea..2bf1c9ab 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki """ __author__ = 'GAM Team ' -__version__ = '7.21.03' +__version__ = '7.22.00' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' #pylint: disable=wrong-import-position @@ -969,14 +969,14 @@ SUSPENDED_CHOICE_MAP = {'notsuspended': False, 'suspended': True} def _getIsSuspended(myarg): if myarg in SUSPENDED_CHOICE_MAP: return SUSPENDED_CHOICE_MAP[myarg] - return getBoolean() + return getBoolean() #issuspended ARCHIVED_ARGUMENTS = {'notarchived', 'archived', 'isarchived'} ARCHIVED_CHOICE_MAP = {'notarchived': False, 'archived': True} def _getIsArchived(myarg): if myarg in ARCHIVED_CHOICE_MAP: return ARCHIVED_CHOICE_MAP[myarg] - return getBoolean() + return getBoolean() #isarchived def _getOptionalIsSuspendedIsArchived(): isSuspended = isArchived = None @@ -1549,10 +1549,12 @@ def getOrderBySortOrder(choiceMap, defaultSortOrderChoice='ASCENDING', mapSortOr return (getChoice(choiceMap, mapChoice=True), getChoice(SORTORDER_CHOICE_MAP, defaultChoice=defaultSortOrderChoice, mapChoice=mapSortOrderChoice)) -def orgUnitPathQuery(path, isSuspended): +def orgUnitPathQuery(path, isSuspended, isArchived): query = "orgUnitPath='{0}'".format(path.replace("'", "\\'")) if path != '/' else '' if isSuspended is not None: query += f' isSuspended={isSuspended}' + if isArchived is not None: + query += f' isArchived={isArchived}' return query def makeOrgUnitPathAbsolute(path): @@ -6247,63 +6249,77 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA _showInvalidEntity(Ent.USER, user) if GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]: return entityList - elif entityType in {Cmd.ENTITY_ALL_USERS, Cmd.ENTITY_ALL_USERS_NS, Cmd.ENTITY_ALL_USERS_NS_SUSP, Cmd.ENTITY_ALL_USERS_SUSP}: + elif entityType in Cmd.ALL_USER_ENTITY_TYPES: cd = buildGAPIObject(API.DIRECTORY) - if entityType == Cmd.ENTITY_ALL_USERS and isSuspended is not None: - query = f'isSuspended={isSuspended}' + if entityType == Cmd.ENTITY_ALL_USERS and ((isSuspended is not None) or (isArchived is not None)): + if isSuspended is not None: + query = f'isSuspended={isSuspended}' + if isArchived is not None: + query += f' isArchived={isArchived}' + else: + query = f'isArchived={isArchived}' else: query = Cmd.ALL_USERS_QUERY_MAP[entityType] - printGettingAllAccountEntities(Ent.USER) + printGettingAllAccountEntities(Ent.USER, query=query) try: result = callGAPIpages(cd.users(), 'list', 'users', pageMessage=getPageMessage(), throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - customer=GC.Values[GC.CUSTOMER_ID], - query=query, orderBy='email', fields='nextPageToken,users(primaryEmail,archived)', + customer=GC.Values[GC.CUSTOMER_ID], query=query, orderBy='email', + fields='nextPageToken,users(primaryEmail)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden): accessErrorExit(cd) - entityList = [user['primaryEmail'] for user in result if isArchived is None or isArchived == user['archived']] - printGotAccountEntities(len(entityList)) - elif entityType in {Cmd.ENTITY_DOMAINS, Cmd.ENTITY_DOMAINS_NS, Cmd.ENTITY_DOMAINS_SUSP}: - if entityType == Cmd.ENTITY_DOMAINS_NS: - query = 'isSuspended=False' - elif entityType == Cmd.ENTITY_DOMAINS_SUSP: - query = 'isSuspended=True' - elif isSuspended is not None: - query = f'isSuspended={isSuspended}' + entityList = [user['primaryEmail'] for user in result] + elif entityType == Cmd.ENTITY_ALL_USERS_ARCH_OR_SUSP: + cd = buildGAPIObject(API.DIRECTORY) + for query in ['isSuspended=True', 'isArchived=True']: + printGettingAllAccountEntities(Ent.USER, query) + try: + result = callGAPIpages(cd.users(), 'list', 'users', + pageMessage=getPageMessage(), + throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], + retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, + customer=GC.Values[GC.CUSTOMER_ID], query=query, orderBy='email', + fields='nextPageToken,users(primaryEmail)', + maxResults=GC.Values[GC.USER_MAX_RESULTS]) + except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden): + accessErrorExit(cd) + entitySet |= {user['primaryEmail'] for user in result} + entityList = sorted(list(entitySet)) + elif entityType in Cmd.DOMAIN_ENTITY_TYPES: + if entityType == Cmd.ENTITY_DOMAINS and ((isSuspended is not None) or (isArchived is not None)): + if isSuspended is not None: + query = f'isSuspended={isSuspended}' + if isArchived is not None: + query += f' isArchived={isArchived}' + else: + query = f'isArchived={isArchived}' else: - query = None + query = Cmd.DOMAINS_QUERY_MAP[entityType] cd = buildGAPIObject(API.DIRECTORY) domains = convertEntityToList(entity) for domain in domains: - printGettingAllEntityItemsForWhom(Ent.USER, domain, entityType=Ent.DOMAIN) + printGettingAllEntityItemsForWhom(Ent.USER, domain, query=query, entityType=Ent.DOMAIN) try: result = callGAPIpages(cd.users(), 'list', 'users', pageMessage=getPageMessageForWhom(), throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND, GAPI.FORBIDDEN], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - domain=domain, - query=query, orderBy='email', fields='nextPageToken,users(primaryEmail,archived)', + domain=domain, query=query, orderBy='email', + fields='nextPageToken,users(primaryEmail)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden): checkEntityDNEorAccessErrorExit(cd, Ent.DOMAIN, domain) _incrEntityDoesNotExist(Ent.DOMAIN) continue - entityList = [user['primaryEmail'] for user in result if isArchived is None or isArchived == user['archived']] - printGotAccountEntities(len(entityList)) - elif entityType in {Cmd.ENTITY_GROUP, Cmd.ENTITY_GROUPS, - Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUPS_NS, - Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUPS_SUSP, - Cmd.ENTITY_GROUP_INDE, Cmd.ENTITY_GROUPS_INDE}: - if entityType in {Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUPS_NS}: - isSuspended = False - elif entityType in {Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUPS_SUSP}: - isSuspended = True + entityList.extend([user['primaryEmail'] for user in result]) + elif entityType in Cmd.GROUP_ENTITY_TYPES or entityType in Cmd.GROUPS_ENTITY_TYPES: + isArchived, isSuspended = Cmd.GROUPS_QUERY_MAP.get(entityType, (isArchived, isSuspended)) includeDerivedMembership = entityType in {Cmd.ENTITY_GROUP_INDE, Cmd.ENTITY_GROUPS_INDE} cd = buildGAPIObject(API.DIRECTORY) - groups = convertEntityToList(entity, nonListEntityType=entityType in {Cmd.ENTITY_GROUP, Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUP_INDE}) + groups = convertEntityToList(entity, nonListEntityType=entityType in Cmd.GROUP_ENTITY_TYPES) for group in groups: if validateEmailAddressOrUID(group, checkPeople=False): group = normalizeEmailAddressOrUID(group) @@ -6329,11 +6345,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA entityList.append(email) else: _showInvalidEntity(Ent.GROUP, group) - elif entityType in {Cmd.ENTITY_GROUP_USERS, Cmd.ENTITY_GROUP_USERS_NS, Cmd.ENTITY_GROUP_USERS_SUSP, Cmd.ENTITY_GROUP_USERS_SELECT}: - if entityType == Cmd.ENTITY_GROUP_USERS_NS: - isSuspended = False - elif entityType == Cmd.ENTITY_GROUP_USERS_SUSP: - isSuspended = True + elif entityType in Cmd.GROUP_USERS_ENTITY_TYPES: + isArchived, isSuspended = Cmd.GROUP_USERS_QUERY_MAP.get(entityType, (isArchived, isSuspended)) cd = buildGAPIObject(API.DIRECTORY) groups = convertEntityToList(entity) includeDerivedMembership = False @@ -6424,21 +6437,13 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA _addCIGroupUsersToUsers(name, groupEmail, recursive) else: _showInvalidEntity(Ent.GROUP, group) - elif entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OUS, Cmd.ENTITY_OU_AND_CHILDREN, Cmd.ENTITY_OUS_AND_CHILDREN, - Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS, - Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP}: - if entityType in {Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS}: - isSuspended = False - elif entityType in {Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP}: - isSuspended = True + elif entityType in Cmd.OU_ENTITY_TYPES or entityType in Cmd.OUS_ENTITY_TYPES: + isArchived, isSuspended = Cmd.OU_QUERY_MAP.get(entityType, (isArchived, isSuspended)) cd = buildGAPIObject(API.DIRECTORY) - ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OU_AND_CHILDREN, - Cmd.ENTITY_OU_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS, - Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP}) - directlyInOU = entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OUS, Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP} + ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in Cmd.OU_ENTITY_TYPES) + directlyInOU = entityType in Cmd.OU_DIRECT_ENTITY_TYPES qualifier = Msg.DIRECTLY_IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT)) if directlyInOU else Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT)) - fields = 'nextPageToken,users(primaryEmail,orgUnitPath,archived)' if directlyInOU else 'nextPageToken,users(primaryEmail,archived)' - prevLen = 0 + fields = 'nextPageToken,users(primaryEmail,orgUnitPath)' if directlyInOU else 'nextPageToken,users(primaryEmail)' for ou in ous: ou = makeOrgUnitPathAbsolute(ou) if ou.startswith('id:'): @@ -6463,22 +6468,17 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(ou, isSuspended), orderBy='email', + customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(ou, isSuspended, isArchived), orderBy='email', fields=fields, maxResults=GC.Values[GC.USER_MAX_RESULTS]) for users in feed: if directlyInOU: for user in users: - if ouLower == user.get('orgUnitPath', '').lower() and (isArchived is None or isArchived == user['archived']): + if ouLower == user.get('orgUnitPath', '').lower(): usersInOU += 1 entityList.append(user['primaryEmail']) - elif isArchived is None: + else: entityList.extend([user['primaryEmail'] for user in users]) usersInOU += len(users) - else: - for user in users: - if isArchived == user['archived']: - usersInOU += 1 - entityList.append(user['primaryEmail']) setGettingAllEntityItemsForWhom(Ent.USER, ou, qualifier=qualifier) printGotEntityItemsForWhom(usersInOU) except (GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError, GAPI.badRequest, @@ -6488,7 +6488,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA elif entityType in {Cmd.ENTITY_QUERY, Cmd.ENTITY_QUERIES}: cd = buildGAPIObject(API.DIRECTORY) queries = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType == Cmd.ENTITY_QUERY) - prevLen = 0 for query in queries: printGettingAllAccountEntities(Ent.USER, query) try: @@ -6512,9 +6511,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA email not in entitySet): entitySet.add(email) entityList.append(email) - totalLen = len(entityList) - printGotAccountEntities(totalLen-prevLen) - prevLen = totalLen elif entityType == Cmd.ENTITY_LICENSES: skusList = [] for item in entity.split(','): @@ -6573,8 +6569,7 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA ClientAPIAccessDeniedExit(str(e)) elif entityType == Cmd.ENTITY_CROS: buildGAPIObject(API.DIRECTORY) - result = convertEntityToList(entity) - for deviceId in result: + for deviceId in convertEntityToList(entity): if deviceId not in entitySet: entitySet.add(deviceId) entityList.append(deviceId) @@ -6598,7 +6593,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA nonListEntityType=entityType == Cmd.ENTITY_CROS_QUERY) if entityType == Cmd.ENTITY_CROS_SN: queries = [f'id:{query}' for query in queries] - prevLen = 0 for query in queries: printGettingAllAccountEntities(Ent.CROS_DEVICE, query) try: @@ -6619,25 +6613,15 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA if deviceId not in entitySet: entitySet.add(deviceId) entityList.append(deviceId) - totalLen = len(entityList) - printGotAccountEntities(totalLen-prevLen) - prevLen = totalLen - elif entityType in {Cmd.ENTITY_CROS_OU, Cmd.ENTITY_CROS_OU_AND_CHILDREN, Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN, - Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY, - Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}: + elif entityType in Cmd.CROS_OU_ENTITY_TYPES or entityType in Cmd.CROS_OUS_ENTITY_TYPES: cd = buildGAPIObject(API.DIRECTORY) - ous = convertEntityToList(entity, shlexSplit=True, - nonListEntityType=entityType in {Cmd.ENTITY_CROS_OU, Cmd.ENTITY_CROS_OU_AND_CHILDREN, - Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, - Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES}) + ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in Cmd.CROS_OU_ENTITY_TYPES) numOus = len(ous) - includeChildOrgunits = entityType in {Cmd.ENTITY_CROS_OU_AND_CHILDREN, Cmd.ENTITY_CROS_OUS_AND_CHILDREN, - Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY, - Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES} + includeChildOrgunits = entityType in Cmd.CROS_OU_CHILDREN_ENTITY_TYPES allQualifier = Msg.DIRECTLY_IN_THE.format(Ent.Choose(Ent.ORGANIZATIONAL_UNIT, numOus)) if not includeChildOrgunits else Msg.IN_THE.format(Ent.Choose(Ent.ORGANIZATIONAL_UNIT, numOus)) - if entityType in {Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY}: + if entityType in Cmd.CROS_OU_QUERY_ENTITY_TYPES: queries = getQueries('query') - elif entityType in {Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}: + elif entityType in Cmd.CROS_OU_QUERIES_ENTITY_TYPES: queries = getQueries('queries') else: queries = [None] @@ -6710,7 +6694,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA customerId = _getCustomerIdNoC() queries = convertEntityToList(entity, shlexSplit=entityType == Cmd.ENTITY_BROWSER_QUERIES, nonListEntityType=entityType == Cmd.ENTITY_BROWSER_QUERY) - prevLen = 0 for query in queries: printGettingAllAccountEntities(Ent.CHROME_BROWSER, query) try: @@ -6730,9 +6713,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA if deviceId not in entitySet: entitySet.add(deviceId) entityList.append(deviceId) - totalLen = len(entityList) - printGotAccountEntities(totalLen-prevLen) - prevLen = totalLen else: systemErrorExit(UNKNOWN_ERROR_RC, 'getItemsToModify coding error') for errorType in [ENTITY_ERROR_DNE, ENTITY_ERROR_INVALID]: @@ -7045,10 +7025,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed= choices += Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES entityType = mapEntityType(getChoice(choices), typeMap) return (Cmd.ENTITY_USERS if entityType not in Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES else Cmd.ENTITY_CROS, - getItemsToModify(entityType, getEntitiesFromFile(shlexSplit=entityType in [Cmd.ENTITY_OUS, Cmd.ENTITY_OUS_AND_CHILDREN, - Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS, - Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP, - Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN]))) + getItemsToModify(entityType, getEntitiesFromFile(shlexSplit=entityType in Cmd.OUS_ENTITY_TYPES | {Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN}))) if entitySelector == Cmd.ENTITY_SELECTOR_CSVDATAFILE: if userAllowed: choices += Cmd.USER_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY] else [Cmd.ENTITY_USERS] @@ -7056,10 +7033,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed= choices += Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES entityType = mapEntityType(getChoice(choices), typeMap) return (Cmd.ENTITY_USERS if entityType not in Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES else Cmd.ENTITY_CROS, - getItemsToModify(entityType, getEntitiesFromCSVFile(shlexSplit=entityType in [Cmd.ENTITY_OUS, Cmd.ENTITY_OUS_AND_CHILDREN, - Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS, - Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP, - Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN]))) + getItemsToModify(entityType, getEntitiesFromCSVFile(shlexSplit=entityType in Cmd.OUS_ENTITY_TYPES | {Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN}))) if entitySelector == Cmd.ENTITY_SELECTOR_CSVKMD: if userAllowed: choices += Cmd.USER_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY] else [Cmd.ENTITY_USERS] @@ -7102,10 +7076,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed= if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]: buildGAPIObject(API.DIRECTORY) if entityClass == Cmd.ENTITY_USERS: - if entityType in [Cmd.ENTITY_GROUP_USERS, - Cmd.ENTITY_GROUP_USERS_NS, Cmd.ENTITY_GROUP_USERS_SUSP, - Cmd.ENTITY_GROUP_USERS_SELECT, - Cmd.ENTITY_CIGROUP_USERS]: + if entityType in Cmd.GROUP_USERS_ENTITY_TYPES | {Cmd.ENTITY_CIGROUP_USERS}: # Skip over sub-arguments while Cmd.ArgumentsRemaining(): myarg = getArgument() @@ -10149,8 +10120,8 @@ def threadBatchWorker(showCmds=False, numItems=0): batchWriteStderr(f'{currentISOformatTimeStamp()},{pid}/{numItems},Error,{str(e)},{logCmd}\n') GM.Globals[GM.TBATCH_QUEUE].task_done() -BATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD] -TBATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.EXECUTE_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD] +BATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD, Cmd.DATETIME_CMD, Cmd.SET_CMD, Cmd.CLEAR_CMD] +TBATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.EXECUTE_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD, Cmd.DATETIME_CMD, Cmd.SET_CMD, Cmd.CLEAR_CMD] def ThreadBatchGAMCommands(items, showCmds): if not items: @@ -10262,6 +10233,14 @@ def doBatch(threadBatch=False): continue if argv: cmd = argv[0].strip().lower() + if cmd == Cmd.DATETIME_CMD: + if len(argv) == 2: + kwValues['datetime'] = todaysTime().strftime(argv[1]) + else: + writeStderr(f'Command: >>>{Cmd.QuotedArgumentList([argv[0]])}<<< {Cmd.QuotedArgumentList(argv[1:])}\n') + writeStderr(f'{ERROR_PREFIX}{Cmd.ARGUMENT_ERROR_NAMES[Cmd.ARGUMENT_INVALID][1]}: {Msg.EXPECTED} <{Cmd.DATETIME_CMD} DateTimeFormat>)>\n') + errors += 1 + continue if cmd == Cmd.SET_CMD: if len(argv) == 3: kwValues[argv[1]] = argv[2] @@ -13353,7 +13332,7 @@ def getUserOrgUnits(cd, orgUnit, orgUnitId): throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnit, None), orderBy='email', + customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnit, None, None), orderBy='email', fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) userOrgUnits = {} for user in result: @@ -17848,10 +17827,29 @@ ORG_FIELD_INFO_ORDER = ['orgUnitId', 'name', 'description', 'parentOrgUnitPath', ORG_FIELDS_WITH_CRS_NLS = {'description'} def _doInfoOrgs(entityList): + def _printUsers(entityType, orgUnitPath, isSuspended, isArchived): + users = callGAPIpages(cd.users(), 'list', 'users', + throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], + retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, + customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, isSuspended, isArchived), orderBy='email', + fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) + printEntitiesCount(entityType, None) + usersInOU = 0 + Ind.Increment() + orgUnitPath = orgUnitPath.lower() + for user in users: + if orgUnitPath == user['orgUnitPath'].lower(): + printKeyValueList([user['primaryEmail']]) + usersInOU += 1 + elif showChildren: + printKeyValueList([f'{user["primaryEmail"]} (child)']) + usersInOU += 1 + Ind.Decrement() + printKeyValueList([Msg.TOTAL_ITEMS_IN_ENTITY.format(Ent.Plural(entityType), Ent.Singular(Ent.ORGANIZATIONAL_UNIT)), usersInOU]) + cd = buildGAPIObject(API.DIRECTORY) getUsers = True - isSuspended = None - entityType = Ent.USER + isSuspended = isArchived = None showChildren = False while Cmd.ArgumentsRemaining(): myarg = getArgument() @@ -17859,7 +17857,8 @@ def _doInfoOrgs(entityList): getUsers = False elif myarg in SUSPENDED_ARGUMENTS: isSuspended = _getIsSuspended(myarg) - entityType = Ent.USER_SUSPENDED if isSuspended else Ent.USER_NOT_SUSPENDED + elif myarg in ARCHIVED_ARGUMENTS: + isArchived = _getIsArchived(myarg) elif myarg in {'children', 'child'}: showChildren = True else: @@ -17890,38 +17889,29 @@ def _doInfoOrgs(entityList): printKeyValueWithCRsNLs(field, value) if getUsers: orgUnitPath = result['orgUnitPath'] - users = callGAPIpages(cd.users(), 'list', 'users', - throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], - retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, isSuspended), orderBy='email', - fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) - printEntitiesCount(entityType, None) - usersInOU = 0 - Ind.Increment() - orgUnitPath = orgUnitPath.lower() - for user in users: - if orgUnitPath == user['orgUnitPath'].lower(): - printKeyValueList([user['primaryEmail']]) - usersInOU += 1 - elif showChildren: - printKeyValueList([f'{user["primaryEmail"]} (child)']) - usersInOU += 1 - Ind.Decrement() - printKeyValueList([Msg.TOTAL_ITEMS_IN_ENTITY.format(Ent.Plural(entityType), Ent.Singular(Ent.ORGANIZATIONAL_UNIT)), usersInOU]) + if isArchived is None and isSuspended is None: + _printUsers(Ent.USER, orgUnitPath, None, None) + else: + if isArchived is not None: + _printUsers(Ent.USER_ARCHIVED if isArchived else Ent.USER_NOT_ARCHIVED, orgUnitPath, None, isArchived) + if isSuspended is not None: + _printUsers(Ent.USER_SUSPENDED if isSuspended else Ent.USER_NOT_SUSPENDED, orgUnitPath, isSuspended, None) Ind.Decrement() except (GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError): entityActionFailedWarning([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], Msg.DOES_NOT_EXIST, i, count) except (GAPI.badRequest, GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden): checkEntityAFDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath) -# gam info orgs|ous [nousers|notsuspended|suspended] [children|child] -def doInfoOrgs(): - _doInfoOrgs(getEntityList(Cmd.OB_ORGUNIT_ENTITY, shlexSplit=True)) - -# gam info org|ou [nousers|notsuspended|suspended] [children|child] +# gam info org|ou +# [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child] def doInfoOrg(): _doInfoOrgs([getOrgUnitItem()]) +# gam info orgs|ous +# [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child] +def doInfoOrgs(): + _doInfoOrgs(getEntityList(Cmd.OB_ORGUNIT_ENTITY, shlexSplit=True)) + ORG_ARGUMENT_TO_FIELD_MAP = { 'blockinheritance': 'blockInheritance', 'inheritanceblocked': 'blockInheritance', @@ -18151,7 +18141,7 @@ def doPrintOrgs(): return if showUserCounts: for orgUnit in orgUnits: - userCounts[orgUnit['orgUnitPath']] = [0, 0] + userCounts[orgUnit['orgUnitPath']] = {'suspended': [0, 0], 'archived': [0, 0], 'total': 0} qualifier = Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT)) printGettingAllEntityItemsForWhom(Ent.USER, orgUnitPath, qualifier=qualifier, entityType=Ent.ORGANIZATIONAL_UNIT) pageMessage = getPageMessageForWhom() @@ -18161,12 +18151,14 @@ def doPrintOrgs(): throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None), orderBy='email', - fields='nextPageToken,users(orgUnitPath,suspended)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) + customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None, None), orderBy='email', + fields='nextPageToken,users(orgUnitPath,suspended,archived)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) for users in feed: for user in users: if user['orgUnitPath'] in userCounts: - userCounts[user['orgUnitPath']][user['suspended']] += 1 + userCounts[user['orgUnitPath']]['suspended'][user['suspended']] += 1 + userCounts[user['orgUnitPath']]['archived'][user['archived']] += 1 + userCounts[user['orgUnitPath']]['total'] += 1 except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.invalidInput, GAPI.badRequest, GAPI.backendError, GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden): checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath) @@ -18233,15 +18225,34 @@ def doPrintOrgs(): (maxCrOSCounts != -1 and total > maxCrOSCounts)): continue if showUserCounts: - row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotSuspended'] = userCounts[orgUnitPath][0] - row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Suspended'] = userCounts[orgUnitPath][1] - row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Total'] = total = userCounts[orgUnitPath][0]+userCounts[orgUnitPath][1] + row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotArchived'] = userCounts[orgUnitPath]['archived'][0] + row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Archived'] = userCounts[orgUnitPath]['archived'][1] + row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotSuspended'] = userCounts[orgUnitPath]['suspended'][0] + row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Suspended'] = userCounts[orgUnitPath]['suspended'][1] + row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Total'] = total = userCounts[orgUnitPath]['total'] if ((minUserCounts != -1 and total < minUserCounts) or (maxUserCounts != -1 and total > maxUserCounts)): continue csvPF.WriteRowTitles(row) else: csvPF.WriteRow(row) + if showCrOSCounts or showUserCounts: + crosTitles = [] + userTitles = [] + allTitles = csvPF.titlesList[:] + for title in allTitles: + if showCrOSCounts and title.startswith(f'CrOS{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}'): + crosTitles.append(title) + csvPF.RemoveTitles(title) + if showUserCounts and title.startswith(f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}'): + userTitles.append(title) + csvPF.RemoveTitles(title) + if showCrOSCounts: + csvPF.AddTitles(sorted(crosTitles)) + if showUserCounts: + for title in ['NotArchived', 'Archived', 'NotSuspended', 'Suspended', 'Total']: + csvPF.AddTitles(f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{title}') + csvPF.SetSortTitles([]) csvPF.writeCSVfile('Orgs') # gam show orgtree [fromparent ] [batchsuborgs [Boolean>]] @@ -18451,7 +18462,7 @@ def doCheckOrgUnit(): throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, - customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None), + customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None, None), fields='nextPageToken,users(orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS]) for users in feed: for user in users: @@ -18872,7 +18883,7 @@ def makeUserGroupDomainQueryFilters(kwargsDict): kwargsQueries.append((kwargs, query)) return kwargsQueries -def userFilters(kwargs, query, orgUnitPath, isSuspended): +def userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived): queryTitle = '' if kwargs.get('domain'): queryTitle += f'domain={kwargs["domain"]}, ' @@ -18891,6 +18902,12 @@ def userFilters(kwargs, query, orgUnitPath, isSuspended): else: query += ' ' query += f'isSuspended={isSuspended}' + if isArchived is not None: + if query is None: + query = '' + else: + query += ' ' + query += f'isArchived={isArchived}' if query is not None: queryTitle += f'query="{query}", ' if queryTitle: @@ -18902,7 +18919,7 @@ def userFilters(kwargs, query, orgUnitPath, isSuspended): # [limittoou ]) # [user|users ] [group|groups ] # [select ] -# [issuspended ] [aliasmatchpattern ] +# [issuspended ] [isarchived ] [aliasmatchpattern ] # [shownoneditable] [nogroups] [nousers] # [onerowpertarget] [delimiter ] # [suppressnoaliasrows] @@ -18948,7 +18965,7 @@ def doPrintAliases(): groups = [] users = [] aliasMatchPattern = re.compile(r'^.*$') - isSuspended = orgUnitPath = None + isArchived = isSuspended = orgUnitPath = None addCSVData = {} delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER] while Cmd.ArgumentsRemaining(): @@ -18972,8 +18989,10 @@ def doPrintAliases(): pass elif myarg == 'select': _, users = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS) - elif myarg == 'issuspended': - isSuspended = getBoolean() + elif myarg in SUSPENDED_ARGUMENTS: + isSuspended = _getIsSuspended(myarg) + elif myarg in ARCHIVED_ARGUMENTS: + isArchived = _getIsArchived(myarg) elif myarg in {'user','users'}: users.extend(convertEntityToList(getString(Cmd.OB_EMAIL_ADDRESS_LIST, minLen=0))) elif myarg in {'group', 'groups'}: @@ -19008,7 +19027,7 @@ def doPrintAliases(): for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict): kwargs = kwargsQuery[0] query = kwargsQuery[1] - query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended) + query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived) printGettingAllAccountEntities(Ent.USER, pquery) try: entityList = callGAPIpages(cd.users(), 'list', 'users', @@ -45690,7 +45709,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails', # [userview] [basic|full|allfields | * | fields ] # [delimiter ] [sortheaders] [formatjson [quotechar ]] [quoteplusphonenumbers] # [convertcrnl] -# [issuspended ] [aliasmatchpattern ] +# [issuspended ] [isarchived ] [aliasmatchpattern ] # [showitemcountonly] # [showvalidcolumn] (addcsvdata )* # @@ -45703,7 +45722,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails', # [userview] [basic|full|allfields | * | fields ] # [delimiter ] [sortheaders] [formatjson [quotechar ]] [quoteplusphonenumbers] # [convertcrnl] -# [issuspended ] [aliasmatchpattern ] +# [issuspended ] [isarchived ] [aliasmatchpattern ] # [showitemcountonly] # [showvalidcolumn] (addcsvdata )* # @@ -45711,13 +45730,13 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails', # ([domain ] [(query )|(queries )] # [limittoou ] [deleted_only|only_deleted])|[select ] # [formatjson [quotechar ]] [countonly] -# [issuspended ] [aliasmatchpattern ] +# [issuspended ] [isarchived ] [aliasmatchpattern ] # [showitemcountonly] # [showvalidcolumn] (addcsvdata )* # # gam print users [todrive *] # [formatjson [quotechar ]] [countonly] -# [issuspended ] +# [issuspended ] [isarchived ] # [showitemcountonly] def doPrintUsers(entityList=None): def _writeUserEntity(userEntity): @@ -45873,7 +45892,7 @@ def doPrintUsers(entityList=None): schemaParms = _initSchemaParms('basic') projectionSet = False oneLicensePerRow = quotePlusPhoneNumbers = showDeleted = False - aliasMatchPattern = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None + aliasMatchPattern = isArchived = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None viewType = 'admin_view' delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER] showValidColumn = '' @@ -45892,8 +45911,10 @@ def doPrintUsers(entityList=None): showDeleted = True elif entityList is None and myarg == 'select': _, entityList = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS) - elif myarg == 'issuspended': - isSuspended = getBoolean() + elif myarg in SUSPENDED_ARGUMENTS: + isSuspended = _getIsSuspended(myarg) + elif myarg in ARCHIVED_ARGUMENTS: + isArchived = _getIsArchived(myarg) elif myarg == 'orderby': orderBy, sortOrder = getOrderBySortOrder(USERS_ORDERBY_CHOICE_MAP) elif myarg == 'userview': @@ -46006,7 +46027,7 @@ def doPrintUsers(entityList=None): for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict): kwargs = kwargsQuery[0] query = kwargsQuery[1] - query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended) + query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived) printGettingAllAccountEntities(Ent.USER, pquery) pageMessage = getPageMessage(showFirstLastItems=True) try: @@ -46016,9 +46037,9 @@ def doPrintUsers(entityList=None): GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.UNKNOWN_ERROR, GAPI.FAILED_PRECONDITION], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR, GAPI.FAILED_PRECONDITION], - query=query, fields=fields, showDeleted=showDeleted, orderBy=orderBy, sortOrder=sortOrder, viewType=viewType, projection=schemaParms['projection'], customFieldMask=schemaParms['customFieldMask'], + query=query, fields=fields, maxResults=maxResults, **kwargs) for users in feed: if orgUnitPath is None: @@ -54970,7 +54991,7 @@ def getDriveFileAttribute(myarg, body, parameters, updateCmd): elif myarg == 'timestamp': parameters[DFA_TIMESTAMP] = getBoolean() elif myarg == 'timeformat': - parameters[DFA_TIMEFORMAT] = getString(Cmd.OB_STRING, minLen=0) + parameters[DFA_TIMEFORMAT] = getString(Cmd.OB_DATETIME_FORMAT, minLen=0) elif getDriveFileCopyAttribute(myarg, body, parameters): pass else: @@ -59557,7 +59578,7 @@ createReturnItemMap = { # [(localfile |-)|(url )] # [(drivefilename|newfilename ) | (replacefilename )*] # [stripnameprefix ] -# [timestamp ]] [timeformat ] +# [timestamp ]] [timeformat ] # * [noduplicate] # [(csv [todrive *] (addcsvdata )*)) | # (returnidonly|returnlinkonly|returneditlinkonly|showdetails)] @@ -60084,7 +60105,7 @@ def checkDriveFileShortcut(users): # [(localfile |-)|(url )] # [retainname | (newfilename ) | (replacefilename )*] # [stripnameprefix ] -# [timestamp ]] [timeformat ] +# [timestamp ]] [timeformat ] # * # [(gsheet|csvsheet [clearfilter])|(addsheet )] # [charset ] [columndelimiter ] diff --git a/src/gam/gamlib/glclargs.py b/src/gam/gamlib/glclargs.py index b1fbf824..48bd2a7e 100644 --- a/src/gam/gamlib/glclargs.py +++ b/src/gam/gamlib/glclargs.py @@ -49,43 +49,71 @@ class GamCLArgs(): ENTITY_CROS_OUS_AND_CHILDREN_QUERIES = 'cros_ous_and_children_queries' ENTITY_CROS_SN = 'cros_sn' ENTITY_DOMAINS = 'domains' + ENTITY_DOMAINS_NA = 'domains_na' + ENTITY_DOMAINS_ARCH = 'domains_arch' ENTITY_DOMAINS_NS = 'domains_ns' ENTITY_DOMAINS_SUSP = 'domains_susp' + ENTITY_DOMAINS_NA_NS = 'domains_na_ns' ENTITY_GROUP = 'group' ENTITY_GROUP_INDE = 'group_inde' + ENTITY_GROUP_NA = 'group_na' + ENTITY_GROUP_ARCH = 'group_arch' ENTITY_GROUP_NS = 'group_ns' ENTITY_GROUP_SUSP = 'group_susp' + ENTITY_GROUP_NA_NS = 'group_na_ns' ENTITY_GROUPS = 'groups' ENTITY_GROUPS_INDE = 'groups_inde' + ENTITY_GROUPS_NA = 'groups_na' + ENTITY_GROUPS_ARCH = 'groups_arch' ENTITY_GROUPS_NS = 'groups_ns' ENTITY_GROUPS_SUSP = 'groups_susp' + ENTITY_GROUPS_NA_NS = 'groups_na_ns' ENTITY_GROUP_USERS = 'group_users' + ENTITY_GROUP_USERS_NA = 'group_users_na' + ENTITY_GROUP_USERS_ARCH = 'group_users_arch' ENTITY_GROUP_USERS_NS = 'group_users_ns' ENTITY_GROUP_USERS_SUSP = 'group_users_susp' + ENTITY_GROUP_USERS_NA_NS = 'group_users_na_ns' ENTITY_GROUP_USERS_SELECT = 'group_users_select' ENTITY_LICENSES = 'licenses' ENTITY_OAUTHUSER = 'oauthuser' ENTITY_OU = 'ou' + ENTITY_OU_NA = 'ou_na' + ENTITY_OU_ARCH = 'ou_arch' ENTITY_OU_NS = 'ou_ns' ENTITY_OU_SUSP = 'ou_susp' + ENTITY_OU_NA_NS = 'ou_na_ns' ENTITY_OU_AND_CHILDREN = 'ou_and_children' + ENTITY_OU_AND_CHILDREN_NA = 'ou_and_children_na' + ENTITY_OU_AND_CHILDREN_ARCH = 'ou_and_children_arch' ENTITY_OU_AND_CHILDREN_NS = 'ou_and_children_ns' ENTITY_OU_AND_CHILDREN_SUSP = 'ou_and_children_susp' + ENTITY_OU_AND_CHILDREN_NA_NS = 'ou_and_children_na_ns' ENTITY_OUS = 'ous' + ENTITY_OUS_NA = 'ous_na' + ENTITY_OUS_ARCH = 'ous_arch' ENTITY_OUS_NS = 'ous_ns' ENTITY_OUS_SUSP = 'ous_susp' + ENTITY_OUS_NA_NS = 'ous_na_ns' ENTITY_OUS_AND_CHILDREN = 'ous_and_children' + ENTITY_OUS_AND_CHILDREN_NA = 'ous_and_children_na' + ENTITY_OUS_AND_CHILDREN_ARCH = 'ous_and_children_arch' ENTITY_OUS_AND_CHILDREN_NS = 'ous_and_children_ns' ENTITY_OUS_AND_CHILDREN_SUSP = 'ous_and_children_susp' + ENTITY_OUS_AND_CHILDREN_NA_NS = 'ous_and_children_na_ns' ENTITY_QUERIES = 'queries' ENTITY_QUERY = 'query' ENTITY_STUDENTS = 'students' ENTITY_TEACHERS = 'teachers' ENTITY_USER = 'user' ENTITY_USERS = 'users' + ENTITY_USERS_NA = 'users_na' + ENTITY_USERS_ARCH = 'users_arch' ENTITY_USERS_NS = 'users_ns' - ENTITY_USERS_NS_SUSP = 'users_ns_susp' ENTITY_USERS_SUSP = 'users_susp' + ENTITY_USERS_NA_NS = 'users_na_ns' + ENTITY_USERS_ARCH_OR_SUSP = 'users_arch_or_susp' + ENTITY_USERS_NS_SUSP = 'users_ns_susp' # BROWSER_ENTITIES = [ ENTITY_BROWSER, @@ -118,34 +146,58 @@ class GamCLArgs(): ENTITY_CIGROUP_USERS, ENTITY_COURSEPARTICIPANTS, ENTITY_DOMAINS, + ENTITY_DOMAINS_NA, + ENTITY_DOMAINS_ARCH, ENTITY_DOMAINS_NS, ENTITY_DOMAINS_SUSP, + ENTITY_DOMAINS_NA_NS, ENTITY_GROUP, ENTITY_GROUP_INDE, + ENTITY_GROUP_NA, + ENTITY_GROUP_ARCH, ENTITY_GROUP_NS, ENTITY_GROUP_SUSP, + ENTITY_GROUP_NA_NS, ENTITY_GROUPS, ENTITY_GROUPS_INDE, + ENTITY_GROUPS_NA, + ENTITY_GROUPS_ARCH, ENTITY_GROUPS_NS, ENTITY_GROUPS_SUSP, + ENTITY_GROUPS_NA_NS, ENTITY_GROUP_USERS, + ENTITY_GROUP_USERS_NA, + ENTITY_GROUP_USERS_ARCH, ENTITY_GROUP_USERS_NS, ENTITY_GROUP_USERS_SUSP, + ENTITY_GROUP_USERS_NA_NS, ENTITY_GROUP_USERS_SELECT, ENTITY_LICENSES, ENTITY_OAUTHUSER, ENTITY_OU, + ENTITY_OU_NA, + ENTITY_OU_ARCH, ENTITY_OU_NS, ENTITY_OU_SUSP, + ENTITY_OU_NA_NS, ENTITY_OU_AND_CHILDREN, + ENTITY_OU_AND_CHILDREN_NA, + ENTITY_OU_AND_CHILDREN_ARCH, ENTITY_OU_AND_CHILDREN_NS, ENTITY_OU_AND_CHILDREN_SUSP, + ENTITY_OU_AND_CHILDREN_NA_NS, ENTITY_OUS, + ENTITY_OUS_NA, + ENTITY_OUS_ARCH, ENTITY_OUS_NS, ENTITY_OUS_SUSP, + ENTITY_OUS_NA_NS, ENTITY_OUS_AND_CHILDREN, + ENTITY_OUS_AND_CHILDREN_NA, + ENTITY_OUS_AND_CHILDREN_ARCH, ENTITY_OUS_AND_CHILDREN_NS, ENTITY_OUS_AND_CHILDREN_SUSP, + ENTITY_OUS_AND_CHILDREN_NA_NS, ENTITY_QUERIES, ENTITY_QUERY, ENTITY_STUDENTS, @@ -222,29 +274,53 @@ class GamCLArgs(): 'licence': ENTITY_LICENSES, 'licences': ENTITY_LICENSES, 'org': ENTITY_OU, + 'org_na': ENTITY_OU_NA, + 'org_arch': ENTITY_OU_ARCH, 'org_ns': ENTITY_OU_NS, 'org_susp': ENTITY_OU_SUSP, + 'org_na_ns': ENTITY_OU_NA_NS, 'org_and_child': ENTITY_OU_AND_CHILDREN, + 'org_and_child_na': ENTITY_OU_AND_CHILDREN_NA, + 'org_and_child_arch': ENTITY_OU_AND_CHILDREN_ARCH, 'org_and_child_ns': ENTITY_OU_AND_CHILDREN_NS, 'org_and_child_susp': ENTITY_OU_AND_CHILDREN_SUSP, + 'org_and_child_na_ns': ENTITY_OU_AND_CHILDREN_NA_NS, 'org_and_children': ENTITY_OU_AND_CHILDREN, + 'org_and_children_na': ENTITY_OU_AND_CHILDREN_NA, + 'org_and_children_arch': ENTITY_OU_AND_CHILDREN_ARCH, 'org_and_children_ns': ENTITY_OU_AND_CHILDREN_NS, 'org_and_children_susp': ENTITY_OU_AND_CHILDREN_SUSP, + 'org_and_children_na_ns': ENTITY_OU_AND_CHILDREN_NA_NS, 'orgs': ENTITY_OUS, + 'orgs_na': ENTITY_OUS_NA, + 'orgs_arch': ENTITY_OUS_ARCH, 'orgs_ns': ENTITY_OUS_NS, 'orgs_susp': ENTITY_OUS_SUSP, + 'orgs_na_ns': ENTITY_OUS_NA_NS, 'orgs_and_child': ENTITY_OUS_AND_CHILDREN, + 'orgs_and_child_na': ENTITY_OUS_AND_CHILDREN_NA, + 'orgs_and_child_arch': ENTITY_OUS_AND_CHILDREN_ARCH, 'orgs_and_child_ns': ENTITY_OUS_AND_CHILDREN_NS, 'orgs_and_child_susp': ENTITY_OUS_AND_CHILDREN_SUSP, + 'orgs_and_child_na_ns': ENTITY_OUS_AND_CHILDREN_NA_NS, 'orgs_and_children': ENTITY_OUS_AND_CHILDREN, + 'orgs_and_children_na': ENTITY_OUS_AND_CHILDREN_NA, + 'orgs_and_children_arch': ENTITY_OUS_AND_CHILDREN_ARCH, 'orgs_and_children_ns': ENTITY_OUS_AND_CHILDREN_NS, 'orgs_and_children_susp': ENTITY_OUS_AND_CHILDREN_SUSP, + 'orgs_and_children_na_ns': ENTITY_OUS_AND_CHILDREN_NA_NS, 'ou_and_child': ENTITY_OU_AND_CHILDREN, + 'ou_and_child_na': ENTITY_OU_AND_CHILDREN_NA, + 'ou_and_child_arch': ENTITY_OU_AND_CHILDREN_ARCH, 'ou_and_child_ns': ENTITY_OU_AND_CHILDREN_NS, 'ou_and_child_susp': ENTITY_OU_AND_CHILDREN_SUSP, + 'ou_and_child_na_ns': ENTITY_OU_AND_CHILDREN_NA_NS, 'ous_and_child': ENTITY_OUS_AND_CHILDREN, + 'ous_and_child_na': ENTITY_OUS_AND_CHILDREN_NA, + 'ous_and_child_arch': ENTITY_OUS_AND_CHILDREN_ARCH, 'ous_and_child_ns': ENTITY_OUS_AND_CHILDREN_NS, 'ous_and_child_susp': ENTITY_OUS_AND_CHILDREN_SUSP, + 'ous_and_child_na_ns': ENTITY_OUS_AND_CHILDREN_NA_NS, } # CL entity source selectors ENTITY_SELECTOR_ALL = 'all' @@ -315,30 +391,217 @@ class GamCLArgs(): ] USER_ENTITY_SELECTOR_ALL_SUBTYPES = [ ENTITY_USERS, + ENTITY_USERS_NA, + ENTITY_USERS_ARCH, ENTITY_USERS_NS, - ENTITY_USERS_NS_SUSP, ENTITY_USERS_SUSP, + ENTITY_USERS_ARCH_OR_SUSP, + ENTITY_USERS_NA_NS, + ENTITY_USERS_NS_SUSP, ] # ENTITY_ALL_CROS = ENTITY_SELECTOR_ALL+' '+ENTITY_CROS ENTITY_ALL_USERS = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS + ENTITY_ALL_USERS_NA = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NA + ENTITY_ALL_USERS_ARCH = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_ARCH ENTITY_ALL_USERS_NS = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NS - ENTITY_ALL_USERS_NS_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NS_SUSP ENTITY_ALL_USERS_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_SUSP + ENTITY_ALL_USERS_NA_NS = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NA_NS + ENTITY_ALL_USERS_ARCH_OR_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_ARCH_OR_SUSP + ENTITY_ALL_USERS_NS_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NS_SUSP +# + ALL_USER_ENTITY_TYPES = { + ENTITY_ALL_USERS, + ENTITY_ALL_USERS_NA, + ENTITY_ALL_USERS_ARCH, + ENTITY_ALL_USERS_NS, + ENTITY_ALL_USERS_SUSP, + ENTITY_ALL_USERS_NA_NS, + ENTITY_ALL_USERS_NS_SUSP, + } + DOMAIN_ENTITY_TYPES = { + ENTITY_DOMAINS, + ENTITY_DOMAINS_NA, + ENTITY_DOMAINS_ARCH, + ENTITY_DOMAINS_NS, + ENTITY_DOMAINS_SUSP, + ENTITY_DOMAINS_NA_NS, + } + GROUP_ENTITY_TYPES = { + ENTITY_GROUP, + ENTITY_GROUP_NA, + ENTITY_GROUP_ARCH, + ENTITY_GROUP_NS, + ENTITY_GROUP_SUSP, + ENTITY_GROUP_NA_NS, + ENTITY_GROUP_INDE, + } + GROUPS_ENTITY_TYPES = { + ENTITY_GROUPS, + ENTITY_GROUPS_NA, + ENTITY_GROUPS_ARCH, + ENTITY_GROUPS_NS, + ENTITY_GROUPS_SUSP, + ENTITY_GROUPS_NA_NS, + ENTITY_GROUPS_INDE, + } + GROUP_USERS_ENTITY_TYPES = { + ENTITY_GROUP_USERS, + ENTITY_GROUP_USERS_NA, + ENTITY_GROUP_USERS_ARCH, + ENTITY_GROUP_USERS_NS, + ENTITY_GROUP_USERS_SUSP, + ENTITY_GROUP_USERS_NA_NS, + ENTITY_GROUP_USERS_SELECT, + } + OU_ENTITY_TYPES = { + ENTITY_OU, + ENTITY_OU_AND_CHILDREN, + ENTITY_OU_NA, + ENTITY_OU_AND_CHILDREN_NA, + ENTITY_OU_ARCH, + ENTITY_OU_AND_CHILDREN_ARCH, + ENTITY_OU_NS, + ENTITY_OU_AND_CHILDREN_NS, + ENTITY_OU_SUSP, + ENTITY_OU_AND_CHILDREN_SUSP, + ENTITY_OU_NA_NS, + ENTITY_OU_AND_CHILDREN_NA_NS, + } + OUS_ENTITY_TYPES = { + ENTITY_OUS, + ENTITY_OUS_AND_CHILDREN, + ENTITY_OUS_NA, + ENTITY_OUS_AND_CHILDREN_NA, + ENTITY_OUS_ARCH, + ENTITY_OUS_AND_CHILDREN_ARCH, + ENTITY_OUS_NS, + ENTITY_OUS_AND_CHILDREN_NS, + ENTITY_OUS_SUSP, + ENTITY_OUS_AND_CHILDREN_SUSP, + ENTITY_OUS_NA_NS, + ENTITY_OUS_AND_CHILDREN_NA_NS, + } + OU_DIRECT_ENTITY_TYPES = { + ENTITY_OU, + ENTITY_OUS, + ENTITY_OU_NA, + ENTITY_OUS_NA, + ENTITY_OU_ARCH, + ENTITY_OUS_ARCH, + ENTITY_OU_NS, + ENTITY_OUS_NS, + ENTITY_OU_SUSP, + ENTITY_OUS_SUSP, + ENTITY_OU_NA_NS, + ENTITY_OUS_NA_NS, + } + CROS_OU_ENTITY_TYPES = { + ENTITY_CROS_OU, + ENTITY_CROS_OU_AND_CHILDREN, + ENTITY_CROS_OU_QUERY, + ENTITY_CROS_OU_AND_CHILDREN_QUERY, + ENTITY_CROS_OU_QUERIES, + ENTITY_CROS_OU_AND_CHILDREN_QUERIES, + } + CROS_OUS_ENTITY_TYPES = { + ENTITY_CROS_OUS, + ENTITY_CROS_OUS_AND_CHILDREN, + ENTITY_CROS_OUS_QUERY, + ENTITY_CROS_OUS_AND_CHILDREN_QUERY, + ENTITY_CROS_OUS_QUERIES, + ENTITY_CROS_OUS_AND_CHILDREN_QUERIES, + } + CROS_OU_CHILDREN_ENTITY_TYPES = { + ENTITY_CROS_OU_AND_CHILDREN, + ENTITY_CROS_OU_AND_CHILDREN_QUERY, + ENTITY_CROS_OU_AND_CHILDREN_QUERIES, + ENTITY_CROS_OUS_AND_CHILDREN, + ENTITY_CROS_OUS_AND_CHILDREN_QUERY, + ENTITY_CROS_OUS_AND_CHILDREN_QUERIES, + } + CROS_OU_QUERY_ENTITY_TYPES = { + ENTITY_CROS_OU_QUERY, + ENTITY_CROS_OU_AND_CHILDREN_QUERY, + ENTITY_CROS_OUS_QUERY, + ENTITY_CROS_OUS_AND_CHILDREN_QUERY, + } + CROS_OU_QUERIES_ENTITY_TYPES = { + ENTITY_CROS_OU_QUERIES, + ENTITY_CROS_OU_AND_CHILDREN_QUERIES, + ENTITY_CROS_OUS_QUERIES, + ENTITY_CROS_OUS_AND_CHILDREN_QUERIES, + } # ALL_USERS_QUERY_MAP = { ENTITY_ALL_USERS: 'isSuspended=False', + ENTITY_ALL_USERS_NA: 'isArchived=False', + ENTITY_ALL_USERS_ARCH: 'isArchived=True', ENTITY_ALL_USERS_NS: 'isSuspended=False', - ENTITY_ALL_USERS_NS_SUSP: None, ENTITY_ALL_USERS_SUSP: 'isSuspended=True', + ENTITY_ALL_USERS_NA_NS: 'isArchived=False isSuspended=False', + ENTITY_ALL_USERS_NS_SUSP: None, + } + DOMAINS_QUERY_MAP = { + ENTITY_DOMAINS: None, + ENTITY_DOMAINS_NA: 'isArchived=False', + ENTITY_DOMAINS_ARCH: 'isArchived=True', + ENTITY_DOMAINS_NS: 'isSuspended=False', + ENTITY_DOMAINS_SUSP: 'isSuspended=True', + ENTITY_DOMAINS_NA_NS: 'isArchived=False isSuspended=False', + } + GROUPS_QUERY_MAP = { #(isArchived, isSuspended) + ENTITY_GROUP_NA: (False, None), + ENTITY_GROUPS_NA: (False, None), + ENTITY_GROUP_ARCH: (True, None), + ENTITY_GROUPS_ARCH: (True, None), + ENTITY_GROUP_NS: (None, False), + ENTITY_GROUPS_NS: (None, False), + ENTITY_GROUP_SUSP: (None, True), + ENTITY_GROUPS_SUSP: (None, True), + ENTITY_GROUP_NA_NS: (False, False), + ENTITY_GROUPS_NA_NS: (False, False), + } + GROUP_USERS_QUERY_MAP = { #(isArchived, isSuspended) + ENTITY_GROUP_USERS_NA: (False, None), + ENTITY_GROUP_USERS_ARCH: (True, None), + ENTITY_GROUP_USERS_NS: (None, False), + ENTITY_GROUP_USERS_SUSP: (None, True), + ENTITY_GROUP_USERS_NA_NS: (False, False), + } + OU_QUERY_MAP = { #(isArchived, isSuspended) + ENTITY_OU_NA: (False, None), + ENTITY_OUS_NA: (False, None), + ENTITY_OU_AND_CHILDREN_NA: (False, None), + ENTITY_OUS_AND_CHILDREN_NA: (False, None), + ENTITY_OU_ARCH: (True, None), + ENTITY_OUS_ARCH: (True, None), + ENTITY_OU_AND_CHILDREN_ARCH: (True, None), + ENTITY_OUS_AND_CHILDREN_ARCH: (True, None), + ENTITY_OU_NS: (None, False), + ENTITY_OUS_NS: (None, False), + ENTITY_OU_AND_CHILDREN_NS: (None, False), + ENTITY_OUS_AND_CHILDREN_NS: (None, False), + ENTITY_OU_SUSP: (None, True), + ENTITY_OUS_SUSP: (None, True), + ENTITY_OU_AND_CHILDREN_SUSP: (None, True), + ENTITY_OUS_AND_CHILDREN_SUSP: (None, True), + ENTITY_OU_NA_NS: (False, False), + ENTITY_OUS_NA_NS: (False, False), + ENTITY_OU_AND_CHILDREN_NA_NS: (False, False), + ENTITY_OUS_AND_CHILDREN_NA_NS: (False, False), } # ENTITY_SELECTOR_ALL_SUBTYPES_MAP = { ENTITY_CROS: ENTITY_ALL_CROS, ENTITY_USERS: ENTITY_ALL_USERS, + ENTITY_USERS_NA: ENTITY_ALL_USERS_NA, + ENTITY_USERS_ARCH: ENTITY_ALL_USERS_ARCH, ENTITY_USERS_NS: ENTITY_ALL_USERS_NS, - ENTITY_USERS_NS_SUSP: ENTITY_ALL_USERS_NS_SUSP, ENTITY_USERS_SUSP: ENTITY_ALL_USERS_SUSP, + ENTITY_USERS_NA_NS: ENTITY_ALL_USERS_NA_NS, + ENTITY_USERS_ARCH_OR_SUSP: ENTITY_ALL_USERS_ARCH_OR_SUSP, + ENTITY_USERS_NS_SUSP: ENTITY_ALL_USERS_NS_SUSP, } # Allowed values for CL source selector datafile, csvkmd CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES = [ @@ -352,22 +615,37 @@ class GamCLArgs(): ENTITY_CIGROUPS, ENTITY_CIGROUP_USERS, ENTITY_DOMAINS, + ENTITY_DOMAINS_NA, + ENTITY_DOMAINS_ARCH, ENTITY_DOMAINS_NS, ENTITY_DOMAINS_SUSP, + ENTITY_DOMAINS_NA_NS, ENTITY_GROUPS, ENTITY_GROUPS_INDE, + ENTITY_GROUPS_NA, + ENTITY_GROUPS_ARCH, ENTITY_GROUPS_NS, ENTITY_GROUPS_SUSP, + ENTITY_GROUPS_NA_NS, ENTITY_GROUP_USERS, + ENTITY_GROUP_USERS_NA, + ENTITY_GROUP_USERS_ARCH, ENTITY_GROUP_USERS_NS, ENTITY_GROUP_USERS_SUSP, + ENTITY_GROUP_USERS_NA_NS, ENTITY_GROUP_USERS_SELECT, ENTITY_OUS, + ENTITY_OUS_NA, + ENTITY_OUS_ARCH, ENTITY_OUS_NS, ENTITY_OUS_SUSP, + ENTITY_OUS_NA_NS, ENTITY_OUS_AND_CHILDREN, + ENTITY_OUS_AND_CHILDREN_NA, + ENTITY_OUS_AND_CHILDREN_ARCH, ENTITY_OUS_AND_CHILDREN_NS, ENTITY_OUS_AND_CHILDREN_SUSP, + ENTITY_OUS_AND_CHILDREN_NA_NS, ENTITY_COURSEPARTICIPANTS, ENTITY_STUDENTS, ENTITY_TEACHERS, @@ -377,6 +655,7 @@ class GamCLArgs(): GAM_CMD = 'gam' COMMIT_BATCH_CMD = 'commit-batch' PRINT_CMD = 'print' + DATETIME_CMD = 'datetime' SET_CMD = 'set' CLEAR_CMD = 'clear' SLEEP_CMD = 'sleep' @@ -924,6 +1203,7 @@ class GamCLArgs(): OB_CSE_KEYPAIR_ID = 'CSEKeyPairID' OB_CUSTOMER_ID = 'CustomerID' OB_CUSTOMER_AUTH_TOKEN = 'CustomerAuthToken' + OB_DATETIME_FORMAT = 'DateTimeFormat' OB_DEVICE_FILE_ENTITY = 'DeviceFileEntity' OB_DEVICE_ENTITY = 'DeviceEntity' OB_DEVICE_ID = 'DeviceID' diff --git a/src/gam/gamlib/glentity.py b/src/gam/gamlib/glentity.py index 1f06ea9b..d15c54e0 100644 --- a/src/gam/gamlib/glentity.py +++ b/src/gam/gamlib/glentity.py @@ -384,11 +384,13 @@ class GamEntity(): URL = 'url ' USER = 'user' USER_ALIAS = 'uali' + USER_NOT_ARCHIVED = 'usna' + USER_ARCHIVED = 'usar' USER_EMAIL = 'uema' USER_INVITATION = 'uinv' - USER_NOT_SUSPENDED = 'uns' - USER_SCHEMA = 'usch' + USER_NOT_SUSPENDED = 'usns' USER_SUSPENDED = 'usup' + USER_SCHEMA = 'usch' VACATION = 'vaca' VACATION_ENABLED = 'vace' VALUE = 'val' @@ -748,11 +750,13 @@ class GamEntity(): URL: ['URLs', 'URL'], USER: ['Users', 'User'], USER_ALIAS: ['User Aliases', 'User Alias'], + USER_NOT_ARCHIVED: ['Users (Not archived)', 'User (Not archived)'], + USER_ARCHIVED: ['Users (Archived)', 'User (Archived)'], USER_EMAIL: ['User Emails', 'User Email'], USER_INVITATION: ['User Invitations', 'User Invitation'], USER_NOT_SUSPENDED: ['Users (Not suspended)', 'User (Not suspended)'], - USER_SCHEMA: ['Schemas', 'Schema'], USER_SUSPENDED: ['Users (Suspended)', 'User (Suspended)'], + USER_SCHEMA: ['Schemas', 'Schema'], VACATION: ['Vacation', 'Vacation'], VACATION_ENABLED: ['Vacation Enabled', 'Vacation Enabled'], VALUE: ['Values', 'Value'],