| #!/usr/bin/env python3 | |
| # | |
| # cace_design_upload.py | |
| # | |
| # The purpose of this script is to package up the user | |
| # design schematic and associated files and send them to | |
| # the remote marketplace server, precipitating a launch | |
| # of CACE to officially characterize the design. | |
| # | |
| import os | |
| import json | |
| import re | |
| import sys | |
| import requests | |
| import subprocess | |
| import file_compressor | |
| import file_request_hash | |
| import local_uid_services | |
| """ | |
| standalone script. | |
| Makes rest calls to marketplace REST server to save datasheet | |
| and associated file(s). Request hash is generated so the two | |
| requests can be associated on the server side. This action | |
| causes the marketplace to run the official characterization | |
| on the CACE server. | |
| """ | |
| mktp_server_url = "" | |
| cace_server_url = "" | |
| # Make request to server sending json passed in. | |
| def send_doc(doc): | |
| result = requests.post(mktp_server_url + '/cace/simulate_request', json=doc) | |
| print('send_doc', result.status_code) | |
| # Cancel simulation (sent directly to CACE) | |
| def send_cancel_doc(doc): | |
| result = requests.post(cace_server_url + '/cace/cancel_sims', json=doc) | |
| print('send_cancel_doc', result.status_code) | |
| # Pure HTTP post here. Add the file to files object and the hash/filename | |
| # to the data params. | |
| def send_file(hash, file, file_name): | |
| files = {'file': file.getvalue()} | |
| data = {'request-hash': hash, 'file-name': file_name} | |
| result = requests.post(mktp_server_url + '/cace/simulate_request_files', files=files, data=data) | |
| print('send_file', result.status_code) | |
| if __name__ == '__main__': | |
| # Divide up command line into options and arguments | |
| options = [] | |
| arguments = [] | |
| for item in sys.argv[1:]: | |
| if item.find('-', 0) == 0: | |
| options.append(item) | |
| else: | |
| arguments.append(item) | |
| # There should be two arguments passed to the script. One is | |
| # the path and filename of the datasheet JSON file, and the | |
| # other a path to the location of the design (netlist and/or | |
| # schematic). | |
| datasheet_filepath = [] | |
| design_filepath = [] | |
| for argval in arguments: | |
| if os.path.isfile(argval): | |
| datasheet_filepath = argval | |
| elif os.path.isdir(argval): | |
| design_filepath = argval | |
| elif os.path.splitext(argval)[1] == '': | |
| argname = argval + '.json' | |
| if os.path.isfile(argval): | |
| datasheet_filepath = argname | |
| if not datasheet_filepath: | |
| # Check for JSON file 'project.json' in the current or parent directory | |
| if design_filepath: | |
| argtry = design_filepath + '/project.json' | |
| if os.path.isfile(argtry): | |
| datasheet_filepath = argtry | |
| else: | |
| argtry = os.path.split(design_filepath)[0] + '/project.json' | |
| if os.path.isfile(argtry): | |
| datasheet_filepath = argtry | |
| if not os.path.isfile(datasheet_filepath): | |
| # Legacy behavior support: | |
| # Check for JSON file with same name as netlist filepath, | |
| # but with a .json extension, in the netlist filepath directory | |
| # or the directory above it. | |
| if design_filepath: | |
| argtry = design_filepath + '/' + os.path.basename(design_filepath) + '.json' | |
| if os.path.isfile(argtry): | |
| datasheet_filepath = argtry | |
| else: | |
| argtry = os.path.split(design_filepath)[0] + '/' + os.path.basename(design_filepath) + '.json' | |
| if os.path.isfile(argtry): | |
| datasheet_filepath = argtry | |
| if not datasheet_filepath: | |
| print('Error: No datasheet JSON file specified\n') | |
| sys.exit(1) | |
| if not os.path.isfile(datasheet_filepath): | |
| print('Error: No datasheet JSON file ' + datasheet_filepath + ' found\n') | |
| sys.exit(1) | |
| # Read the datasheet now. Get the expected design name | |
| dsheet = {} | |
| print('Reading JSON datasheet ' + datasheet_filepath) | |
| with open(datasheet_filepath, 'r') as user_doc_file: | |
| docinfo = json.load(user_doc_file) | |
| dsheet = docinfo['data-sheet'] | |
| name = dsheet['ip-name'] | |
| # Get JSON file of settings if it exists. It should be in the same | |
| # location as the JSON datasheet file (generated by cace.py) | |
| testmode = False | |
| force = False | |
| settings_filepath = os.path.split(datasheet_filepath)[0] + '/settings.json' | |
| if os.path.exists(settings_filepath): | |
| with open(settings_filepath, 'r') as user_settings_file: | |
| settings = json.load(user_settings_file) | |
| docinfo['settings'] = settings | |
| if 'submit-as-schematic' in settings: | |
| if settings['submit-as-schematic'] == True: | |
| force = True | |
| if 'submit-test-mode' in settings: | |
| if settings['submit-test-mode'] == True: | |
| testmode = True | |
| # Use of "-force" in the options overrides any settings from the JSON file. | |
| if '-force' in options: | |
| force = True | |
| if '-test' in options: | |
| testmode = True | |
| # Diagnostic | |
| if 'identifiers' in docinfo: | |
| print('Identifiers: ' + str(docinfo['identifiers'])) | |
| if not design_filepath: | |
| print('Error: No schematic or netlist directory given\n') | |
| sys.exit(1) | |
| else: | |
| # If design_filepath has a subdirectory "design", add that to | |
| # the path name. | |
| if os.path.isdir(design_filepath + '/spi'): | |
| spice_filepath = design_filepath + '/spi' | |
| filelist = os.listdir(spice_filepath) | |
| if os.path.isdir(design_filepath + '/elec'): | |
| if os.path.isdir(design_filepath + '/elec/' + name + '.delib'): | |
| schem_filepath = design_filepath + '/elec/' + name + '.delib' | |
| filelist.extend(os.listdir(schem_filepath)) | |
| # To be valid, the filepath must contain either a .spi file with | |
| # the name of ip-name, or a .sch file with the name of ip-name. | |
| netlistname = name + '.spi' | |
| schemname = name + '.sch' | |
| if netlistname not in filelist and schemname not in filelist: | |
| print('Error: Path ' + design_filepath + ' has no schematic ' | |
| + 'or netlist for design ' + name + '\n') | |
| sys.exit(1) | |
| # Add key 'project-folder' to the document, containing the path to the | |
| # JSON file. This is used by the CACE to ensure that progress information | |
| # is passed to the correct folder, not relying on hard-coded home paths, | |
| # and allowing for copies of projects in different paths. | |
| foldername = os.path.split(datasheet_filepath)[0] | |
| docinfo['project-folder'] = foldername | |
| # Current expectation is to use UID (username). If it is not in the | |
| # document, then add it here. | |
| if testmode: | |
| uid = {} | |
| else: | |
| if 'UID' not in docinfo: | |
| uid = local_uid_services.get_uid(os.environ['USER']) | |
| else: | |
| uid = docinfo['UID'] | |
| if not uid or uid == 'null': | |
| uid = os.environ['USER'] | |
| docinfo['UID'] = uid | |
| # Handle cancel requests | |
| if '-cancel' in options: | |
| # Read last message . . . | |
| if os.path.exists(design_filepath + '/ngspice/char/remote_status.json'): | |
| with open(design_filepath + '/ngspice/char/remote_status.json', 'r') as f: | |
| status = json.load(f) | |
| if 'hash' in status: | |
| docinfo['request-hash'] = status['hash'] | |
| send_cancel_doc(docinfo) | |
| else: | |
| print('No hash value in status file, cannot cancel.') | |
| else: | |
| print('No status file found, cannot cancel.') | |
| sys.exit(0) | |
| # If settings specify that the submission should be forced to be schematic-only, | |
| # pass the setting to CACE as 'netlist-source' in the data-sheet record. | |
| if force: | |
| dsheet['netlist-source'] = 'schematic' | |
| # Put the current git system state into the target directory | |
| # prior to tarballing | |
| if os.path.isfile('.version'): | |
| with open('.version', 'r') as f: | |
| ef_version = f.read().rstrip() | |
| docinfo['ef-version'] = ef_version | |
| rhash, timestamp = file_request_hash.get_hash(name) | |
| docinfo['request-hash'] = rhash | |
| print('request hash = ' + rhash + '\n') | |
| # Now send the document | |
| if testmode: | |
| print('Test: running send_doc(docinfo)\n') | |
| else: | |
| send_doc(docinfo) | |
| # Send the tarballed design file directory to the marketplace server for storage. | |
| # Ignore the log file, which is meant for in-system diagnostics, not for storage. | |
| exclusions = [name + '\.log', '.*\.raw', | |
| 'elec/\.java', | |
| 'ngspice/run/\.allwaves'] | |
| # If settings specify that the submission should be forced to be schematic-only, | |
| # then don't tarball the layout database files. | |
| if force: | |
| exclusions.append('mag/.*\.mag') | |
| exclusions.append('mag/.*\.ext') | |
| # Now send the netlist file tarball | |
| tarballname = name + '.tar.gz' | |
| if testmode: | |
| file_compressor.tar_directory_contents_to_file(design_filepath, | |
| tarballname, exclude=exclusions) | |
| os.rename(design_filepath + '/' + tarballname, tarballname) | |
| print('Test: running send_file(' + rhash + ', <tarball>, ' + tarballname + ')\n') | |
| else: | |
| tar = file_compressor.tar_directory_contents(design_filepath, | |
| exclude=exclusions) | |
| send_file(rhash, tar, tarballname) | |