Compare commits

...

31 Commits
v5.10 ... v5.11

Author SHA1 Message Date
Jay Lee
73677544a3 GAM 5.11 2020-06-17 14:21:49 -04:00
Jay Lee
7c46d8548e servicemanagement API to serviceusage API 2020-06-17 13:30:08 -04:00
Ross Scroggs
186381426a Update GamCommands.txt (#1211) 2020-06-06 09:35:24 -04:00
Ross Scroggs
af1e695661 Clean up csvtest (#1209)
Let your formatter redo 13382/13386, I don't understand its rules
2020-06-05 13:37:44 -04:00
Jay Lee
4ccd51269a Support base64-sha1 and base64-md5 user passwords
This allows pulling md5 and sha-1 passwords stored in OpenLDAP format into G Suite. This example commands set user password to "helloworld".

gam update user user@example.com password "{SHA}at+xg6SiyUovktq1redipHiJpaE=" base64-sha1
2020-06-04 15:59:06 -04:00
Jay Lee
560cfe225f wMerge branch 'master' of https://github.com/jay0lee/GAM 2020-06-03 13:30:56 -04:00
Jay Lee
e9e4c3d333 Add a 'gam csvtest' command to help users understand CSV batch processing 2020-06-03 13:30:50 -04:00
Ross Scroggs
dbca6e3b88 Handle 0 users in update group (#1207) 2020-06-01 16:05:26 -04:00
Jay Lee
ad465ed20c MacOS XCode 11.5 2020-06-01 15:35:08 -04:00
Jay Lee
9370f7ce15 Merge branch 'master' of https://github.com/jay0lee/GAM 2020-06-01 13:45:13 -04:00
Jay Lee
d9151a866b 3.9 dev, allow Python 3.9 to fail, Focal is now passing 2020-06-01 13:44:12 -04:00
Ross Scroggs
7937fd00d4 Clean up group_inde (#1203) 2020-05-26 15:09:15 -04:00
Eduardo
d2199a5b9c include includeDerivedMembership option for group sync (#1198)
* include includeDerivedMembership option

in command gam course xxx sync students|teachers group yyyy
I add alternative option group_inde (INclude DErived)

* Avoid interference of new includeDerivedMembership

taers232c suggestion to avoid unexpected interference
2020-05-26 14:30:11 -04:00
Ross Scroggs
6e765325c1 Fix bug where user all was not properly recognized (#1202) 2020-05-26 14:29:10 -04:00
Jay Lee
18119b3d64 disable secrets for focal 2020-05-26 09:56:41 -04:00
Jay Lee
378a7c2d6c allow focal to fail 2020-05-24 09:09:18 -04:00
Jay Lee
1270a315b2 it wasn't the unsafe flags... 2020-05-21 10:18:36 -04:00
Jay Lee
931b2cc700 temp disable Python optimizations 2020-05-21 10:00:51 -04:00
Jay Lee
e145ac0ad1 hold on ssh-server 2020-05-21 09:19:52 -04:00
Jay Lee
ab8e882e94 focal 2020-05-21 09:03:16 -04:00
Jay Lee
b66d671b74 try upgrading pyinstaller commit 2020-05-21 08:29:55 -04:00
Jay Lee
f662a13778 another attempt to fix focal 2020-05-20 11:29:17 -04:00
Jay Lee
845aa122e1 unmute apt-get upgrade so it doesn't timeout 2020-05-20 11:07:59 -04:00
Jay Lee
bb19336d06 silence apt-get again 2020-05-20 10:52:53 -04:00
Jay Lee
774948cf9d try apt-get upgrade instead of dist-upgrade 2020-05-20 10:27:18 -04:00
Jay Lee
e26e077c83 no apt-get dist-upgrade for focal 2020-05-20 10:17:02 -04:00
Jay Lee
f264ffd040 Merge branch 'master' of https://github.com/jay0lee/GAM 2020-05-20 10:02:07 -04:00
Jay Lee
7e16e4880b Update .travis.yml 2020-05-20 09:53:49 -04:00
Ross Scroggs
dd1ee6ff44 Fix bug (#1196)
```
$ gam calendar testsimple@rdschool.org addevent start 2020-05-17T08:00:00-07:00 end 2020-05-17T09:00:00-07:00  recurrence "RRULE:FREQ=WEEKLY;WKST=SU;COUNT=13;BYDAY=MO" summary "Monday Morning Meeting"
Traceback (most recent call last):
  File "/Users/Ross/Documents/GoogleApps/GAMO/gam.py", line 11, in <module>
    main(sys.argv)
  File "/Users/Ross/Documents/GoogleApps/GAMO/gam/__main__.py", line 45, in main
    sys.exit(gam.ProcessGAMCommand(sys.argv))
  File "/Users/Ross/Documents/GoogleApps/GAMO/gam/__init__.py", line 14413, in ProcessGAMCommand
    gapi_calendar.addOrUpdateEvent('add')
  File "/Users/Ross/Documents/GoogleApps/GAMO/gam/gapi/calendar.py", line 350, in addOrUpdateEvent
    sendUpdates, body = getEventAttributes(i, calendarId, cal, body, action)
  File "/Users/Ross/Documents/GoogleApps/GAMO/gam/gapi/calendar.py", line 537, in getEventAttributes
    if not timeZone:
UnboundLocalError: local variable 'timeZone' referenced before assignment
```
2020-05-16 09:56:48 -04:00
Jay Lee
90d628cc75 Remove deprecated CloudPrint commands 2020-05-15 18:35:48 -04:00
Jay Lee
d5a0b33f04 add MacOS 10.15 check 2020-05-15 08:16:21 -04:00
10 changed files with 97 additions and 1269 deletions

View File

@@ -39,7 +39,14 @@ cache:
- $HOME/ssl
jobs:
allow_failures:
- python: nightly
fast_finish: true
include:
- os: linux
name: "Linux 64-bit Focal"
dist: focal
language: shell
- os: linux
name: "Linux 64-bit Bionic"
dist: bionic
@@ -69,7 +76,11 @@ jobs:
language: python
python: 3.7
- os: linux
name: "Python nightly Source Testing"
name: "Python 3.9 dev Source Testing"
language: python
python: 3.9-dev
- os: linux
name: "Python trunk nightly Source Testing"
language: python
python: nightly
- os: linux
@@ -87,7 +98,7 @@ jobs:
- os: osx
name: "MacOS 10.15"
language: generic
osx_image: xcode11.4
osx_image: xcode11.5
- os: windows
name: "Windows 64-bit"
language: shell

View File

@@ -780,7 +780,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
<UserBasicAttribute> ::=
(agreed2terms|agreedtoterms <Boolean>)|
(changepassword|changepasswordatnextlogin <Boolean>)|
(crypt|sha|sha1|sha-1|md5|nohash)|
(base64-md5|base64-sha1|crypt|sha|sha1|sha-1|md5|nohash)|
(customerid <String>)|
(email|primaryemail|username <EmailAddress>)|
(firstname|givenname <String>)|
@@ -822,6 +822,7 @@ gam help
gam batch <FileName>|- [charset <Charset>]
gam csv <FileName>|- [charset <Charset>] gam <GAM argument list>
gam csvtest <FileName>|- [charset <Charset>] gam <GAM argument list>
You can make substitutions in <GAMArgumentList> with values from the CSV file.
An argument containing exactly ~xxx is replaced by the value of field xxx from the CSV file

View File

@@ -1,486 +0,0 @@
{
"kind": "discovery#restDescription",
"discoveryVersion": "v1",
"id": "cloudprint:v2",
"name": "cloudprint",
"version": "v2",
"revision": "20150605",
"title": "Cloud Print API",
"description": "Lets you access Cloud Print Printers",
"ownerDomain": "google.com",
"ownerName": "Google",
"icons": {
"x16": "http://www.google.com/images/icons/product/search-16.gif",
"x32": "http://www.google.com/images/icons/product/search-32.gif"
},
"documentationLink": "https://developers.google.com/cloud-print",
"protocol": "rest",
"baseUrl": "https://www.google.com/",
"basePath": "/cloudprint/",
"rootUrl": "https://www.google.com/",
"servicePath": "/cloudprint/",
"parameters": {
"prettyPrint": {
"type": "boolean",
"description": "Returns response with indentations and line breaks.",
"default": "true",
"location": "query"
}
},
"auth": {
"oauth2": {
"scopes": {
"https://www.googleapis.com/auth/cloudprint": {
"description": "Manage Cloud Print"
}
}
}
},
"schemas": {
"Job": {
"id": "Job",
"type": "object",
"description": "Job Object",
"properties": {
"title": {
"type": "string",
"description": "Job Title"
},
"id": {
"type": "string",
"description": "Unique ID"
}
}
},
"Jobs": {
"id": "Jobs",
"type": "object",
"description": "List of Jobs.",
"properties": {
"jobs": {
"type": "array",
"description": "List of job objects.",
"items": {
"$ref": "Job"
}
}
}
},
"Printer": {
"id": "Printer",
"type": "object",
"description": "Printer Object",
"properties": {
"displayName": {
"type": "string",
"description": "Display Name"
},
"id": {
"type": "string",
"description": "Unique ID"
}
}
},
"Printers": {
"id": "Printers",
"type": "object",
"description": "List of Printers.",
"properties": {
"printers": {
"type": "array",
"description": "List of printer objects.",
"items": {
"$ref": "Printer"
}
}
}
}
},
"resources": {
"jobs": {
"methods": {
"delete": {
"id": "cloudprint.jobs.delete",
"path": "deletejob",
"httpMethod": "GET",
"parameters": {
"jobid": {
"type": "string",
"location": "query",
"required": "true"
}
}
},
"fetch": {
"id": "cloudprint.jobs.fetch",
"path": "fetch",
"httpMethod": "GET",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
}
},
"response": {
"$ref": "Jobs"
}
},
"getticket": {
"id": "cloudprint.jobs.getticket",
"path": "ticket",
"httpMethod": "GET",
"parameters": {
"jobid": {
"type": "string",
"required": "true",
"location": "query"
},
"use_cjt": {
"type": "boolean",
"required": "true",
"location": "query"
}
}
},
"list": {
"id": "cloudprint.jobs.list",
"path": "jobs",
"httpMethod": "GET",
"parameters": {
"printerid": {
"type": "string",
"location": "query"
},
"owner": {
"type": "string",
"location": "query"
},
"status": {
"type": "string",
"location": "query"
},
"q": {
"type": "string",
"location": "query"
},
"offset": {
"type": "string",
"location": "query"
},
"limit": {
"type": "string",
"location": "query"
},
"sortorder": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Jobs"
}
},
"update": {
"id": "cloudprint.jobs.update",
"path": "control",
"httpMethod": "GET",
"parameters": {
"jobid": {
"type": "string",
"required": "true",
"location": "query"
},
"semantic_state_diff": {
"type": "string",
"required": "true",
"location": "query"
}
},
"response": {
"$ref": "Jobs"
}
},
"resubmit": {
"id": "cloudprint.jobs.resubmit",
"path": "resubmit",
"httpMethod": "POST",
"description": "resubmit a job to new printer.",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"jobid": {
"type": "string",
"required": "true",
"location": "query"
},
"ticket": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Job"
}
},
"submit": {
"id": "cloudprint.jobs.submit",
"path": "submit",
"httpMethod": "POST",
"description": "Send a print job to cloud print.",
"request": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"title": {
"type": "string",
"location": "query"
},
"ticket": {
"type": "string",
"location": "query"
},
"content": {
"type": "string",
"location": "query"
},
"contentType": {
"type": "string",
"location": "query"
},
"tag": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Job"
}
}
}
},
"printers": {
"methods": {
"get": {
"id": "cloudprint.printers.get",
"path": "printer",
"httpMethod": "GET",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"extra_fields": {
"type": "string",
"location": "query"
}
},
"response": {
"$ref": "Printer"
}
},
"list": {
"id": "cloudprint.printers.list",
"path": "search",
"httpMethod": "GET",
"description": "List all printers",
"parameters": {
"q": {
"type": "string",
"description": "Query list of printers",
"location": "query"
},
"type": {
"type": "string",
"description": "limit results to printers of type",
"location": "query"
},
"connection_status": {
"type": "string",
"description": "limit results to printers with this status",
"location": "query"
},
"extra_fields": {
"type": "string",
"description": "include extra fields",
"location": "query"
}
},
"response": {
"$ref": "Printers"
}
},
"share": {
"id": "cloudprint.printers.share",
"path": "share",
"httpMethod": "GET",
"description": "Share printer with user, group or domain",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"scope": {
"type": "string",
"location": "query"
},
"role": {
"type": "string",
"location": "query"
},
"type": {
"type": "string",
"location": "query"
},
"skip_notification": {
"type": "boolean",
"location": "query"
},
"public": {
"type": "boolean",
"location": "query"
}
}
},
"unshare": {
"id": "cloudprint.printers.unshare",
"path": "unshare",
"httpMethod": "GET",
"description": "unshare printer with user, group or domain",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"scope": {
"type": "string",
"location": "query"
},
"public": {
"type": "string",
"location": "query"
}
}
},
"delete": {
"id": "cloudprint.printers.delete",
"path": "delete",
"httpMethod": "GET",
"description": "delete a printer",
"parameters": {
"printerid": {
"type": "string",
"required": "true",
"location": "query"
}
}
},
"update": {
"id": "cloudprint.printers.update",
"path": "update",
"httpMethod": "GET",
"description": "update a printer",
"parameters": {
"isTosAccepted": {
"type": "boolean",
"location": "query"
},
"gcpVersion": {
"type": "string",
"location": "query"
},
"setupUrl": {
"type": "string",
"location": "query"
},
"supportUrl": {
"type": "string",
"location": "query"
},
"firmware": {
"type": "string",
"location": "query"
},
"currentQuota": {
"type": "string",
"location": "query"
},
"type": {
"type": "string",
"location": "query"
},
"public": {
"type": "boolean",
"location": "query"
},
"status": {
"type": "string",
"location": "query"
},
"proxy": {
"type": "string",
"location": "query"
},
"manufacturer": {
"type": "string",
"location": "query"
},
"defaultDisplayName": {
"type": "string",
"location": "query"
},
"displayName": {
"type": "string",
"location": "query"
},
"name": {
"type": "string",
"location": "query"
},
"uuid": {
"type": "string",
"location": "query"
},
"updateUrl": {
"type": "string",
"location": "query"
},
"ownerId": {
"type": "string",
"location": "query"
},
"model": {
"type": "string",
"location": "query"
},
"description": {
"type": "string",
"location": "query"
},
"printerid": {
"type": "string",
"required": "true",
"location": "query"
},
"quotaEnabled": {
"type": "boolean",
"location": "query"
},
"dailyQuota": {
"type": "string",
"location": "query"
}
}
}
}
}
}
}

View File

@@ -29,7 +29,7 @@ gamversion="latest"
adminuser=""
regularuser=""
gam_glibc_vers="2.27 2.23"
gam_macos_vers="10.14.6 10.13.6"
gam_macos_vers="10.15.4 10.14.6 10.13.6"
while getopts "hd:a:o:b:lp:u:r:v:" OPTION
do

View File

@@ -7,11 +7,9 @@ from PyInstaller.utils.hooks import copy_metadata
sys.modules['FixTk'] = None
extra_files = [('cloudprint-v2.json', 'cloudprint-v2.json')]
# dynamically determine where httplib2/cacerts.txt lives
proot = os.path.dirname(importlib.import_module('httplib2').__file__)
extra_files += [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
extra_files = [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
extra_files += copy_metadata('google-api-python-client')

View File

@@ -2783,695 +2783,6 @@ def doPrintCourseParticipants():
display.write_csv_file(csvRows, titles, 'Course Participants', todrive)
def doPrintPrintJobs():
cp = buildGAPIObject('cloudprint')
todrive = False
titles = ['printerid', 'id']
csvRows = []
printerid = None
owner = None
status = None
sortorder = None
descending = False
query = None
age = None
older_or_newer = None
jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'todrive':
todrive = True
i += 1
elif myarg in ['olderthan', 'newerthan']:
if myarg == 'olderthan':
older_or_newer = 'older'
else:
older_or_newer = 'newer'
age_number = sys.argv[i + 1][:-1]
if not age_number.isdigit():
controlflow.system_error_exit(
2, f'expected a number; got {age_number}')
age_unit = sys.argv[i + 1][-1].lower()
if age_unit == 'm':
age = int(time.time()) - (int(age_number) * 60)
elif age_unit == 'h':
age = int(time.time()) - (int(age_number) * 60 * 60)
elif age_unit == 'd':
age = int(time.time()) - (int(age_number) * 60 * 60 * 24)
else:
controlflow.system_error_exit(
2,
f'expected m (minutes), h (hours) or d (days); got {age_unit}'
)
i += 2
elif myarg == 'query':
query = sys.argv[i + 1]
i += 2
elif myarg == 'status':
status = sys.argv[i + 1]
i += 2
elif myarg == 'ascending':
descending = False
i += 1
elif myarg == 'descending':
descending = True
i += 1
elif myarg == 'orderby':
sortorder = sys.argv[i + 1].lower().replace('_', '')
if sortorder not in PRINTJOB_ASCENDINGORDER_MAP:
controlflow.expected_argument_exit(
'orderby', ', '.join(PRINTJOB_ASCENDINGORDER_MAP),
sortorder)
sortorder = PRINTJOB_ASCENDINGORDER_MAP[sortorder]
i += 2
elif myarg in ['printer', 'printerid']:
printerid = sys.argv[i + 1]
i += 2
elif myarg in ['owner', 'user']:
owner = sys.argv[i + 1]
i += 2
elif myarg == 'limit':
jobLimit = getInteger(sys.argv[i + 1], myarg, minVal=0)
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i],
'gam print printjobs')
if sortorder and descending:
sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder]
if printerid:
result = gapi.call(cp.printers(), 'get', printerid=printerid)
checkCloudPrintResult(result)
if ((not sortorder) or
(sortorder == 'CREATE_TIME_DESC')) and (older_or_newer == 'newer'):
timeExit = True
elif (sortorder == 'CREATE_TIME') and (older_or_newer == 'older'):
timeExit = True
else:
timeExit = False
jobCount = offset = 0
while True:
if jobLimit == 0:
limit = PRINTJOBS_DEFAULT_MAX_RESULTS
else:
limit = min(PRINTJOBS_DEFAULT_MAX_RESULTS, jobLimit - jobCount)
if limit == 0:
break
result = gapi.call(cp.jobs(),
'list',
printerid=printerid,
q=query,
status=status,
sortorder=sortorder,
owner=owner,
offset=offset,
limit=limit)
checkCloudPrintResult(result)
newJobs = result['range']['jobsCount']
totalJobs = int(result['range']['jobsTotal'])
if GC_Values[GC_DEBUG_LEVEL] > 0:
sys.stderr.write(
f'Debug: jobCount: {jobCount}, jobLimit: {jobLimit}, jobsCount: {newJobs}, jobsTotal: {totalJobs}\n'
)
if newJobs == 0:
break
jobCount += newJobs
offset += newJobs
for job in result['jobs']:
createTime = int(job['createTime']) / 1000
if older_or_newer:
if older_or_newer == 'older' and createTime > age:
if timeExit:
jobCount = totalJobs
break
continue
if older_or_newer == 'newer' and createTime < age:
if timeExit:
jobCount = totalJobs
break
continue
job['createTime'] = utils.formatTimestampYMDHMS(job['createTime'])
job['updateTime'] = utils.formatTimestampYMDHMS(job['updateTime'])
job['tags'] = ' '.join(job['tags'])
display.add_row_titles_to_csv_file(utils.flatten_json(job), csvRows,
titles)
if jobCount >= totalJobs:
break
display.write_csv_file(csvRows, titles, 'Print Jobs', todrive)
def doPrintPrinters():
cp = buildGAPIObject('cloudprint')
todrive = False
titles = [
'id',
]
csvRows = []
queries = [None]
printer_type = None
connection_status = None
extra_fields = None
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg in ['query', 'queries']:
queries = getQueries(myarg, sys.argv[i + 1])
i += 2
elif myarg == 'type':
printer_type = sys.argv[i + 1]
i += 2
elif myarg == 'status':
connection_status = sys.argv[i + 1]
i += 2
elif myarg == 'extrafields':
extra_fields = sys.argv[i + 1]
i += 2
elif myarg == 'todrive':
todrive = True
i += 1
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam print printers')
for query in queries:
printers = gapi.call(cp.printers(),
'list',
q=query,
type=printer_type,
connection_status=connection_status,
extra_fields=extra_fields)
checkCloudPrintResult(printers)
for printer in printers['printers']:
printer['createTime'] = utils.formatTimestampYMDHMS(
printer['createTime'])
printer['accessTime'] = utils.formatTimestampYMDHMS(
printer['accessTime'])
printer['updateTime'] = utils.formatTimestampYMDHMS(
printer['updateTime'])
printer['tags'] = ' '.join(printer['tags'])
display.add_row_titles_to_csv_file(utils.flatten_json(printer),
csvRows, titles)
display.write_csv_file(csvRows, titles, 'Printers', todrive)
def doPrinterShowACL():
cp = buildGAPIObject('cloudprint')
show_printer = sys.argv[2]
printer_info = gapi.call(cp.printers(), 'get', printerid=show_printer)
checkCloudPrintResult(printer_info)
for acl in printer_info['printers'][0]['access']:
if 'key' in acl:
acl['accessURL'] = f'https://www.google.com/cloudprint/addpublicprinter.html?printerid={show_printer}&key={acl["key"]}'
display.print_json(acl)
print()
def doPrinterAddACL():
cp = buildGAPIObject('cloudprint')
printer = sys.argv[2]
role = sys.argv[4].upper()
scope = sys.argv[5]
notify = bool(len(sys.argv) > 6 and sys.argv[6].lower() == 'notify')
public = None
skip_notification = True
if scope.lower() == 'public':
public = True
scope = None
role = None
skip_notification = None
elif scope.find('@') == -1:
scope = f'/hd/domain/{scope}'
else:
skip_notification = not notify
result = gapi.call(cp.printers(),
'share',
printerid=printer,
role=role,
scope=scope,
public=public,
skip_notification=skip_notification)
checkCloudPrintResult(result)
who = scope
if who is None:
who = 'public'
role = 'user'
print(f'Added {role} {who}')
def doPrinterDelACL():
cp = buildGAPIObject('cloudprint')
printer = sys.argv[2]
scope = sys.argv[4]
public = None
if scope.lower() == 'public':
public = True
scope = None
elif scope.find('@') == -1:
scope = f'/hd/domain/{scope}'
result = gapi.call(cp.printers(),
'unshare',
printerid=printer,
scope=scope,
public=public)
checkCloudPrintResult(result)
who = scope
if who is None:
who = 'public'
print(f'Removed {who}')
def encode_multipart(fields, files, boundary=None):
def escape_quote(s):
return s.replace('"', '\\"')
def getFormDataLine(name, value, boundary):
return f'--{boundary}', f'Content-Disposition: form-data; name="{escape_quote(name)}"', '', str(
value)
if boundary is None:
boundary = ''.join(random.choice(ALPHANUMERIC_CHARS) for _ in range(30))
lines = []
for name, value in list(fields.items()):
if name == 'tags':
for tag in value:
lines.extend(getFormDataLine('tag', tag, boundary))
else:
lines.extend(getFormDataLine(name, value, boundary))
for name, value in list(files.items()):
filename = value['filename']
mimetype = value['mimetype']
lines.extend((
f'--{boundary}',
f'Content-Disposition: form-data; name="{escape_quote(name)}"; filename="{escape_quote(filename)}"',
f'Content-Type: {mimetype}',
'',
value['content'],
))
lines.extend((
f'--{boundary}--',
'',
))
body = '\r\n'.join(lines)
headers = {
'Content-Type': f'multipart/form-data; boundary={boundary}',
'Content-Length': str(len(body)),
}
return (body, headers)
def doPrintJobFetch():
cp = buildGAPIObject('cloudprint')
printerid = sys.argv[2]
if printerid == 'any':
printerid = None
owner = None
status = None
sortorder = None
descending = False
query = None
age = None
older_or_newer = None
jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT
targetFolder = os.getcwd()
i = 4
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg in ['olderthan', 'newerthan']:
if myarg == 'olderthan':
older_or_newer = 'older'
else:
older_or_newer = 'newer'
age_number = sys.argv[i + 1][:-1]
if not age_number.isdigit():
controlflow.system_error_exit(
2, f'expected a number; got {age_number}')
age_unit = sys.argv[i + 1][-1].lower()
if age_unit == 'm':
age = int(time.time()) - (int(age_number) * 60)
elif age_unit == 'h':
age = int(time.time()) - (int(age_number) * 60 * 60)
elif age_unit == 'd':
age = int(time.time()) - (int(age_number) * 60 * 60 * 24)
else:
controlflow.system_error_exit(
2,
f'expected m (minutes), h (hours) or d (days); got {age_unit}'
)
i += 2
elif myarg == 'query':
query = sys.argv[i + 1]
i += 2
elif myarg == 'status':
status = sys.argv[i + 1]
i += 2
elif myarg == 'ascending':
descending = False
i += 1
elif myarg == 'descending':
descending = True
i += 1
elif myarg == 'orderby':
sortorder = sys.argv[i + 1].lower().replace('_', '')
if sortorder not in PRINTJOB_ASCENDINGORDER_MAP:
controlflow.expected_argument_exit(
'orderby', ', '.join(PRINTJOB_ASCENDINGORDER_MAP),
sortorder)
sortorder = PRINTJOB_ASCENDINGORDER_MAP[sortorder]
i += 2
elif myarg in ['owner', 'user']:
owner = sys.argv[i + 1]
i += 2
elif myarg == 'limit':
jobLimit = getInteger(sys.argv[i + 1], myarg, minVal=0)
i += 2
elif myarg == 'drivedir':
targetFolder = GC_Values[GC_DRIVE_DIR]
i += 1
elif myarg == 'targetfolder':
targetFolder = os.path.expanduser(sys.argv[i + 1])
if not os.path.isdir(targetFolder):
os.makedirs(targetFolder)
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i],
'gam printjobs fetch')
if sortorder and descending:
sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder]
if printerid:
result = gapi.call(cp.printers(), 'get', printerid=printerid)
checkCloudPrintResult(result)
ssd = '{"state": {"type": "DONE"}}'
if ((not sortorder) or
(sortorder == 'CREATE_TIME_DESC')) and (older_or_newer == 'newer'):
timeExit = True
elif (sortorder == 'CREATE_TIME') and (older_or_newer == 'older'):
timeExit = True
else:
timeExit = False
jobCount = offset = 0
while True:
if jobLimit == 0:
limit = PRINTJOBS_DEFAULT_MAX_RESULTS
else:
limit = min(PRINTJOBS_DEFAULT_MAX_RESULTS, jobLimit - jobCount)
if limit == 0:
break
result = gapi.call(cp.jobs(),
'list',
printerid=printerid,
q=query,
status=status,
sortorder=sortorder,
owner=owner,
offset=offset,
limit=limit)
checkCloudPrintResult(result)
newJobs = result['range']['jobsCount']
totalJobs = int(result['range']['jobsTotal'])
if newJobs == 0:
break
jobCount += newJobs
offset += newJobs
for job in result['jobs']:
createTime = int(job['createTime']) / 1000
if older_or_newer:
if older_or_newer == 'older' and createTime > age:
if timeExit:
jobCount = totalJobs
break
continue
if older_or_newer == 'newer' and createTime < age:
if timeExit:
jobCount = totalJobs
break
continue
fileUrl = job['fileUrl']
jobid = job['id']
fileName = os.path.join(
targetFolder,
f'{"".join(c if c in FILENAME_SAFE_CHARS else "_" for c in job["title"])}-{jobid}'
)
_, content = cp._http.request(uri=fileUrl, method='GET')
if fileutils.write_file(fileName,
content,
mode='wb',
continue_on_error=True):
# ticket = gapi.call(cp.jobs(), u'getticket', jobid=jobid, use_cjt=True)
result = gapi.call(cp.jobs(),
'update',
jobid=jobid,
semantic_state_diff=ssd)
checkCloudPrintResult(result)
print(f'Printed job {jobid} to {fileName}')
if jobCount >= totalJobs:
break
if jobCount == 0:
print('No print jobs.')
def doDelPrinter():
cp = buildGAPIObject('cloudprint')
printerid = sys.argv[3]
result = gapi.call(cp.printers(), 'delete', printerid=printerid)
checkCloudPrintResult(result)
def doGetPrinterInfo():
cp = buildGAPIObject('cloudprint')
printerid = sys.argv[3]
everything = False
i = 4
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'everything':
everything = True
i += 1
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam info printer')
result = gapi.call(cp.printers(), 'get', printerid=printerid)
checkCloudPrintResult(result)
printer_info = result['printers'][0]
printer_info['createTime'] = utils.formatTimestampYMDHMS(
printer_info['createTime'])
printer_info['accessTime'] = utils.formatTimestampYMDHMS(
printer_info['accessTime'])
printer_info['updateTime'] = utils.formatTimestampYMDHMS(
printer_info['updateTime'])
printer_info['tags'] = ' '.join(printer_info['tags'])
if not everything:
del printer_info['capabilities']
del printer_info['access']
display.print_json(printer_info)
def doUpdatePrinter():
cp = buildGAPIObject('cloudprint')
printerid = sys.argv[3]
kwargs = {}
i = 4
update_items = [
'isTosAccepted', 'gcpVersion', 'setupUrl', 'quotaEnabled', 'id',
'supportUrl', 'firmware', 'currentQuota', 'type', 'public', 'status',
'description', 'defaultDisplayName', 'proxy', 'dailyQuota',
'manufacturer', 'displayName', 'name', 'uuid', 'updateUrl', 'ownerId',
'model'
]
while i < len(sys.argv):
arg_in_item = False
for item in update_items:
if item.lower() == sys.argv[i].lower():
kwargs[item] = sys.argv[i + 1]
i += 2
arg_in_item = True
break
if not arg_in_item:
controlflow.invalid_argument_exit(sys.argv[i], 'gam update printer')
result = gapi.call(cp.printers(), 'update', printerid=printerid, **kwargs)
checkCloudPrintResult(result)
print(f'Updated printer {printerid}')
def doPrinterRegister():
cp = buildGAPIObject('cloudprint')
form_fields = {
'name':
'GAM',
'proxy':
'GAM',
'uuid':
_getValueFromOAuth('sub'),
'manufacturer':
GAM_AUTHOR,
'model':
'cp1',
'gcp_version':
'2.0',
'setup_url':
GAM_URL,
'support_url':
'https://groups.google.com/forum/#!forum/google-apps-manager',
'update_url':
GAM_RELEASES,
'firmware':
GAM_VERSION,
'semantic_state': {
'version': '1.0',
'printer': {
'state': 'IDLE',
}
},
'use_cdd':
True,
'capabilities': {
'version': '1.0',
'printer': {
'supported_content_type': [{
'content_type': 'application/pdf',
'min_version': '1.5'
}, {
'content_type': 'image/jpeg'
}, {
'content_type': 'text/plain'
}],
'copies': {
'default': 1,
'max': 100
},
'media_size': {
'option': [{
'name': 'ISO_A4',
'width_microns': 210000,
'height_microns': 297000
}, {
'name': 'NA_LEGAL',
'width_microns': 215900,
'height_microns': 355600
}, {
'name': 'NA_LETTER',
'width_microns': 215900,
'height_microns': 279400,
'is_default': True
}],
},
},
},
'tags': ['GAM', GAM_URL],
}
body, headers = encode_multipart(form_fields, {})
#Get the printer first to make sure our OAuth access token is fresh
gapi.call(cp.printers(), 'list')
_, result = cp._http.request(
uri='https://www.google.com/cloudprint/register',
method='POST',
body=body,
headers=headers)
result = json.loads(result.decode(UTF8))
checkCloudPrintResult(result)
print(f'Created printer {result["printers"][0]["id"]}')
def doPrintJobResubmit():
cp = buildGAPIObject('cloudprint')
jobid = sys.argv[2]
printerid = sys.argv[4]
ssd = '{"state": {"type": "HELD"}}'
result = gapi.call(cp.jobs(),
'update',
jobid=jobid,
semantic_state_diff=ssd)
checkCloudPrintResult(result)
ticket = gapi.call(cp.jobs(), 'getticket', jobid=jobid, use_cjt=True)
result = gapi.call(cp.jobs(),
'resubmit',
printerid=printerid,
jobid=jobid,
ticket=ticket)
checkCloudPrintResult(result)
print(
f'Success resubmitting {jobid} as job {result["job"]["id"]} to printer {printerid}'
)
def doPrintJobSubmit():
cp = buildGAPIObject('cloudprint')
printer = sys.argv[2]
content = sys.argv[4]
form_fields = {
'printerid': printer,
'title': content,
'ticket': '{"version": "1.0"}',
'tags': ['GAM', GAM_URL]
}
i = 5
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg == 'tag':
form_fields['tags'].append(sys.argv[i + 1])
i += 2
elif myarg in ['name', 'title']:
form_fields['title'] = sys.argv[i + 1]
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i],
'gam printer ... print')
form_files = {}
if content[:4] == 'http':
form_fields['content'] = content
form_fields['contentType'] = 'url'
else:
filepath = content
content = os.path.basename(content)
mimetype = mimetypes.guess_type(filepath)[0]
if mimetype is None:
mimetype = 'application/octet-stream'
filecontent = fileutils.read_file(filepath, mode='rb')
form_files['content'] = {
'filename': content,
'content': filecontent,
'mimetype': mimetype
}
#result = gapi.call(cp.printers(), u'submit', body=body)
body, headers = encode_multipart(form_fields, form_files)
#Get the printer first to make sure our OAuth access token is fresh
gapi.call(cp.printers(), 'get', printerid=printer)
_, result = cp._http.request(uri='https://www.google.com/cloudprint/submit',
method='POST',
body=body,
headers=headers)
result = json.loads(result.decode(UTF8))
checkCloudPrintResult(result)
print(f'Submitted print job {result["job"]["id"]}')
def doDeletePrintJob():
cp = buildGAPIObject('cloudprint')
job = sys.argv[2]
result = gapi.call(cp.jobs(), 'delete', jobid=job)
checkCloudPrintResult(result)
print(f'Print Job {job} deleted')
def doCancelPrintJob():
cp = buildGAPIObject('cloudprint')
job = sys.argv[2]
ssd = '{"state": {"type": "ABORTED", "user_action_cause": {"action_code": "CANCELLED"}}}'
result = gapi.call(cp.jobs(), 'update', jobid=job, semantic_state_diff=ssd)
checkCloudPrintResult(result)
print(f'Print Job {job} cancelled')
def checkCloudPrintResult(result):
if isinstance(result, bytes):
result = result.decode(UTF8)
if isinstance(result, str):
try:
result = json.loads(result)
except ValueError:
controlflow.system_error_exit(3, f'unexpected response: {result}')
if not result['success']:
controlflow.system_error_exit(
result['errorCode'], f'{result["errorCode"]}: {result["message"]}')
def doProfile(users):
cd = buildGAPIObject('directory')
myarg = sys.argv[4].lower()
@@ -7326,6 +6637,7 @@ def getUserAttributes(i, cd, updateCmd):
i += 1
need_password = True
need_to_hash_password = True
need_to_b64_decrypt_password = False
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg in ['firstname', 'givenname']:
@@ -7366,13 +6678,17 @@ def getUserAttributes(i, cd, updateCmd):
body['includeInGlobalAddressList'] = getBoolean(
sys.argv[i + 1], myarg)
i += 2
elif myarg in ['sha', 'sha1', 'sha-1']:
elif myarg in ['sha', 'sha1', 'sha-1', 'base64-sha1']:
body['hashFunction'] = 'SHA-1'
need_to_hash_password = False
if myarg == 'base64-sha1':
need_to_b64_decrypt_password = True
i += 1
elif myarg == 'md5':
elif myarg in ['md5', 'base64-md5']:
body['hashFunction'] = 'MD5'
need_to_hash_password = False
if myarg == 'base64-md5':
need_to_b64_decrypt_password = True
i += 1
elif myarg == 'crypt':
body['hashFunction'] = 'crypt'
@@ -7846,6 +7162,10 @@ def getUserAttributes(i, cd, updateCmd):
if 'password' in body and need_to_hash_password:
body['password'] = gen_sha512_hash(body['password'])
body['hashFunction'] = 'crypt'
elif 'password' in body and need_to_b64_decrypt_password:
if body['password'].lower()[:5] in ['{md5}', '{sha}']:
body['password'] = body['password'][5:]
body['password'] = base64.b64decode(body['password']).hex()
return body
@@ -7890,34 +7210,36 @@ def enableGAMProjectAPIs(GAMProjectAPIs,
i=0,
count=0):
apis = GAMProjectAPIs[:]
project_name = f'project:{projectId}'
serveman = getService('servicemanagement', httpObj)
project_name = f'projects/{projectId}'
serveu = getService('serviceusage', httpObj)
status = True
if checkEnabled:
try:
services = gapi.get_all_pages(
serveman.services(),
serveu.services(),
'list',
'services',
throw_reasons=[gapi_errors.ErrorReason.NOT_FOUND],
consumerId=project_name,
fields='nextPageToken,services(serviceName)')
parent=project_name,
filter='state:ENABLED',
fields='nextPageToken,services(name)')
jcount = len(services)
print(
f' Project: {projectId}, Check {jcount} APIs{currentCount(i, count)}'
)
j = 0
for service in sorted(services, key=lambda k: k['serviceName']):
for service in sorted(services, key=lambda k: k['name']):
j += 1
if 'serviceName' in service:
if service['serviceName'] in apis:
if 'name' in service:
service_name = service['name'].split('/')[-1]
if service_name in apis:
print(
f' API: {service["serviceName"]}, Already enabled{currentCount(j, jcount)}'
f' API: {service_name}, Already enabled{currentCount(j, jcount)}'
)
apis.remove(service['serviceName'])
apis.remove(service_name)
else:
print(
f' API: {service["serviceName"]}, Already enabled (non-GAM which is fine){currentCount(j, jcount)}'
f' API: {service_name}, Already enabled (non-GAM which is fine){currentCount(j, jcount)}'
)
except gapi_errors.GapiNotFoundError as e:
print(
@@ -7931,18 +7253,18 @@ def enableGAMProjectAPIs(GAMProjectAPIs,
)
j = 0
for api in apis:
service_name = f'projects/{projectId}/services/{api}'
j += 1
while True:
try:
gapi.call(serveman.services(),
gapi.call(serveu.services(),
'enable',
throw_reasons=[
gapi_errors.ErrorReason.FAILED_PRECONDITION,
gapi_errors.ErrorReason.FORBIDDEN,
gapi_errors.ErrorReason.PERMISSION_DENIED
],
serviceName=api,
body={'consumerId': project_name})
name=service_name)
print(f' API: {api}, Enabled{currentCount(j, jcount)}')
break
except gapi_errors.GapiFailedPreconditionError as e:
@@ -9372,7 +8694,7 @@ def doUpdateGroup():
item.append(delivery)
item.append(user_email)
items.append(item)
else:
elif len(users_email) > 0:
body = {
'role':
role,
@@ -9498,7 +8820,7 @@ def doUpdateGroup():
for user_email in users_email:
items.append(
['gam', 'update', 'group', group, 'remove', user_email])
else:
elif len(users_email) > 0:
try:
gapi.call(cd.members(),
'delete',
@@ -9532,7 +8854,7 @@ def doUpdateGroup():
item.append(delivery)
item.append(user_email)
items.append(item)
else:
elif len(users_email) > 0:
body = {}
update_text = []
if role:
@@ -9617,7 +8939,7 @@ def doUpdateGroup():
'gam', 'update', 'group', group, 'remove',
user_email
])
else:
elif len(users_email) > 0:
try:
gapi.call(cd.members(),
'delete',
@@ -12818,11 +12140,12 @@ def getUsersToModify(entity_type=None,
]
elif entity_type == 'users':
users = entity.replace(',', ' ').split()
elif entity_type in ['group', 'group_ns', 'group_susp']:
elif entity_type in ['group', 'group_ns', 'group_susp', 'group_inde']:
if entity_type == 'group_ns':
checkSuspended = False
elif entity_type == 'group_susp':
checkSuspended = True
includeDerivedMembership = entity_type == 'group_inde'
got_uids = True
group = entity
if member_type is None:
@@ -12839,16 +12162,18 @@ def getUsersToModify(entity_type=None,
'...')
validRoles, listRoles, listFields = _getRoleVerification(
member_type, 'nextPageToken,members(email,id,type,status)')
members = gapi.get_all_pages(cd.members(),
'list',
'members',
page_message=page_message,
groupKey=group,
roles=listRoles,
fields=listFields)
members = gapi.get_all_pages(
cd.members(),
'list',
'members',
page_message=page_message,
groupKey=group,
roles=listRoles,
includeDerivedMembership=includeDerivedMembership,
fields=listFields)
users = []
for member in members:
if ((not groupUserMembersOnly) or
if ((not groupUserMembersOnly and not includeDerivedMembership) or
(member['type'] == 'USER')) and _checkMemberRoleIsSuspended(
member, validRoles, checkSuspended):
users.append(member.get('email', member['id']))
@@ -13236,11 +12561,6 @@ OAUTH2_SCOPES = [
'https://www.googleapis.com/auth/classroom.guardianlinks.students'
]
},
{
'name': 'Cloud Print API',
'subscopes': [],
'scopes': 'https://www.googleapis.com/auth/cloudprint'
},
{
'name': 'Data Transfer API',
'subscopes': ['readonly'],
@@ -14034,7 +13354,7 @@ def ProcessGAMCommand(args):
2,
f'batch file: {filename}, not processed, {errors} error{["", "s"][errors != 1]}'
)
elif command == 'csv':
elif command in ['csv', 'csvtest']:
if httplib2.debuglevel > 0:
controlflow.system_error_exit(
1,
@@ -14058,7 +13378,24 @@ def ProcessGAMCommand(args):
items.append(['gam'] +
processSubFields(GAM_argv, row, subFields))
fileutils.close_file(f)
run_batch(items)
if command == 'csv':
run_batch(items)
else:
print('The CSV file has the following headers:')
for field in csvFile.fieldnames:
print(f' {field}')
print()
num_items = min(len(items), 10)
print(
f'Here are the first {num_items} commands GAM will run (note that quoting may be lost/invalid in this output):\n'
)
for i in range(num_items):
c = ' '.join([item if (item and
(item.find(' ') == -1) and
(item.find(',') == -1) and
(item.find("'") == -1))
else '"'+item+'"' for item in items[i]])
print(f' {c}')
sys.exit(0)
elif command == 'version':
doGAMVersion()
@@ -14143,8 +13480,6 @@ def ProcessGAMCommand(args):
doCreateOrUpdateUserSchema(True)
elif argument in ['course', 'class']:
doUpdateCourse()
elif argument in ['printer', 'print']:
doUpdatePrinter()
elif argument == 'domain':
doUpdateDomain()
elif argument == 'customer':
@@ -14192,8 +13527,6 @@ def ProcessGAMCommand(args):
doGetUserSchema()
elif argument in ['course', 'class']:
doGetCourseInfo()
elif argument in ['printer', 'print']:
doGetPrinterInfo()
elif argument in ['transfer', 'datatransfer']:
doGetDataTransferInfo()
elif argument == 'customer':
@@ -14245,8 +13578,6 @@ def ProcessGAMCommand(args):
doDelSchema()
elif argument in ['course', 'class']:
doDelCourse()
elif argument in ['printer', 'printers']:
doDelPrinter()
elif argument == 'domain':
doDelDomain()
elif argument in ['domainalias', 'aliasdomain']:
@@ -14325,10 +13656,6 @@ def ProcessGAMCommand(args):
doPrintCourses()
elif argument in ['courseparticipants', 'classparticipants']:
doPrintCourseParticipants()
elif argument == 'printers':
doPrintPrinters()
elif argument == 'printjobs':
doPrintPrintJobs()
elif argument in ['transfers', 'datatransfers']:
doPrintDataTransfers()
elif argument == 'transferapps':
@@ -14426,35 +13753,6 @@ def ProcessGAMCommand(args):
else:
controlflow.invalid_argument_exit(argument, 'gam calendar')
sys.exit(0)
elif command == 'printer':
if sys.argv[2].lower() == 'register':
doPrinterRegister()
sys.exit(0)
argument = sys.argv[3].lower()
if argument == 'showacl':
doPrinterShowACL()
elif argument == 'add':
doPrinterAddACL()
elif argument in ['del', 'delete', 'remove']:
doPrinterDelACL()
else:
controlflow.invalid_argument_exit(argument, 'gam printer...')
sys.exit(0)
elif command == 'printjob':
argument = sys.argv[3].lower()
if argument == 'delete':
doDeletePrintJob()
elif argument == 'cancel':
doCancelPrintJob()
elif argument == 'submit':
doPrintJobSubmit()
elif argument == 'fetch':
doPrintJobFetch()
elif argument == 'resubmit':
doPrintJobResubmit()
else:
controlflow.invalid_argument_exit(argument, 'gam printjob')
sys.exit(0)
elif command == 'report':
gapi_reports.showReport()
sys.exit(0)

View File

@@ -372,6 +372,7 @@ def getEventAttributes(i, calendarId, cal, body, action):
# calendars are notified of changes
sendUpdates = 'externalOnly'
action = 'update' if body else 'add'
timeZone = None
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg in ['notifyattendees', 'sendnotifications', 'sendupdates']:

View File

@@ -316,7 +316,9 @@ def showReport():
eventName = sys.argv[i + 1]
i += 2
elif myarg == 'user':
userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
userKey = sys.argv[i + 1].lower()
if userKey != 'all':
userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
i += 2
elif myarg in ['filter', 'filters']:
filters = sys.argv[i + 1]

View File

@@ -8,7 +8,7 @@ import platform
import re
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
GAM_VERSION = '5.10'
GAM_VERSION = '5.11'
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = 'https://git.io/gam'
@@ -26,12 +26,12 @@ GAM_PROJECT_FILEPATH = 'https://raw.githubusercontent.com/jay0lee/GAM/master/src
true_values = ['on', 'yes', 'enabled', 'true', '1']
false_values = ['off', 'no', 'disabled', 'false', '0']
usergroup_types = [
'user', 'users', 'group', 'group_ns', 'grooup_susp', 'ou', 'org', 'ou_ns',
'org_ns', 'ou_susp', 'org_susp', 'ou_and_children', 'ou_and_child',
'ou_and_children_ns', 'ou_and_child_ns', 'ou_and_children_susp',
'ou_and_child_susp', 'query', 'queries', 'license', 'licenses', 'licence',
'licences', 'file', 'csv', 'csvfile', 'all', 'cros', 'cros_sn', 'crosquery',
'crosqueries', 'crosfile', 'croscsv', 'croscsvfile'
'user', 'users', 'group', 'group_ns', 'group_susp', 'group_inde', 'ou',
'org', 'ou_ns', 'org_ns', 'ou_susp', 'org_susp', 'ou_and_children',
'ou_and_child', 'ou_and_children_ns', 'ou_and_child_ns',
'ou_and_children_susp', 'ou_and_child_susp', 'query', 'queries', 'license',
'licenses', 'licence', 'licences', 'file', 'csv', 'csvfile', 'all', 'cros',
'cros_sn', 'crosquery', 'crosqueries', 'crosfile', 'croscsv', 'croscsvfile'
]
ERROR_PREFIX = 'ERROR: '
WARNING_PREFIX = 'WARNING: '
@@ -236,7 +236,6 @@ API_VER_MAPPING = {
'appsactivity': 'v1',
'calendar': 'v3',
'classroom': 'v1',
'cloudprint': 'v2',
'cloudresourcemanager': 'v2',
'cloudresourcemanagerv1': 'v1',
'datatransfer': 'datatransfer_v1',
@@ -253,6 +252,7 @@ API_VER_MAPPING = {
'reports': 'reports_v1',
'reseller': 'v1',
'servicemanagement': 'v1',
'serviceusage': 'v1',
'sheets': 'v4',
'siteVerification': 'v1',
'storage': 'v1',

View File

@@ -34,8 +34,10 @@ else
mkdir python
echo "RUNNING: apt update..."
sudo apt-get -qq --yes update > /dev/null
echo "RUNNING: apt dist-upgrade..."
sudo apt-get -qq --yes dist-upgrade > /dev/null
echo "RUNNING: apt upgrade..."
sudo apt-mark hold openssh-server
sudo apt-get --yes upgrade
sudo apt-get --yes --with-new-pkgs upgrade
echo "Installing build tools..."
sudo apt-get -qq --yes install build-essential
echo "Installing deps for python3"
@@ -72,7 +74,8 @@ else
echo "running configure with safe and unsafe"
./configure $safe_flags $unsafe_flags > /dev/null
fi
make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
#make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
make -j$cpucount -s
RESULT=$?
echo "First make exited with $RESULT"
if [ $RESULT != 0 ]; then