blob: 4b4c47f656f665da5753c71e19e46ecddddcca2a [file] [log] [blame]
emayecs5656b2b2021-08-04 12:44:13 -04001#!/usr/bin/env python3
emayecs5966a532021-07-29 10:07:02 -04002#
3# cace_design_upload.py
4#
5# The purpose of this script is to package up the user
6# design schematic and associated files and send them to
7# the remote marketplace server, precipitating a launch
8# of CACE to officially characterize the design.
9#
10
11import os
12import json
13import re
14import sys
15import requests
16import subprocess
17
18import file_compressor
19import file_request_hash
20import local_uid_services
21
emayecsb2487ae2021-08-05 10:30:13 -040022import config
emayecs5966a532021-07-29 10:07:02 -040023
24"""
emayecs14748312021-08-05 14:21:26 -040025 standalone script.
emayecs5966a532021-07-29 10:07:02 -040026 Makes rest calls to marketplace REST server to save datasheet
27 and associated file(s). Request hash is generated so the two
28 requests can be associated on the server side. This action
29 causes the marketplace to run the official characterization
30 on the CACE server.
31"""
32
emayecsb2487ae2021-08-05 10:30:13 -040033mktp_server_url = config.mktp_server_url
34cace_server_url = config.cace_server_url
emayecs5966a532021-07-29 10:07:02 -040035
36# Make request to server sending json passed in.
37def send_doc(doc):
38 result = requests.post(mktp_server_url + '/cace/simulate_request', json=doc)
39 print('send_doc', result.status_code)
40
41# Cancel simulation (sent directly to CACE)
42def send_cancel_doc(doc):
43 result = requests.post(cace_server_url + '/cace/cancel_sims', json=doc)
44 print('send_cancel_doc', result.status_code)
45
46# Pure HTTP post here. Add the file to files object and the hash/filename
47# to the data params.
48def send_file(hash, file, file_name):
49 files = {'file': file.getvalue()}
50 data = {'request-hash': hash, 'file-name': file_name}
51 result = requests.post(mktp_server_url + '/cace/simulate_request_files', files=files, data=data)
52 print('send_file', result.status_code)
53
54if __name__ == '__main__':
55
56 # Divide up command line into options and arguments
57 options = []
58 arguments = []
59 for item in sys.argv[1:]:
60 if item.find('-', 0) == 0:
61 options.append(item)
62 else:
63 arguments.append(item)
64
65 # There should be two arguments passed to the script. One is
66 # the path and filename of the datasheet JSON file, and the
67 # other a path to the location of the design (netlist and/or
68 # schematic).
69
70 datasheet_filepath = []
71 design_filepath = []
72
73 for argval in arguments:
74 if os.path.isfile(argval):
75 datasheet_filepath = argval
76 elif os.path.isdir(argval):
77 design_filepath = argval
78 elif os.path.splitext(argval)[1] == '':
79 argname = argval + '.json'
80 if os.path.isfile(argval):
81 datasheet_filepath = argname
82
83 if not datasheet_filepath:
84 # Check for JSON file 'project.json' in the current or parent directory
85 if design_filepath:
86 argtry = design_filepath + '/project.json'
87 if os.path.isfile(argtry):
88 datasheet_filepath = argtry
89 else:
90 argtry = os.path.split(design_filepath)[0] + '/project.json'
91 if os.path.isfile(argtry):
92 datasheet_filepath = argtry
93
94 if not os.path.isfile(datasheet_filepath):
95 # Legacy behavior support:
96 # Check for JSON file with same name as netlist filepath,
97 # but with a .json extension, in the netlist filepath directory
98 # or the directory above it.
99 if design_filepath:
100 argtry = design_filepath + '/' + os.path.basename(design_filepath) + '.json'
101 if os.path.isfile(argtry):
102 datasheet_filepath = argtry
103 else:
104 argtry = os.path.split(design_filepath)[0] + '/' + os.path.basename(design_filepath) + '.json'
105 if os.path.isfile(argtry):
106 datasheet_filepath = argtry
107
108 if not datasheet_filepath:
109 print('Error: No datasheet JSON file specified\n')
110 sys.exit(1)
111
112 if not os.path.isfile(datasheet_filepath):
113 print('Error: No datasheet JSON file ' + datasheet_filepath + ' found\n')
114 sys.exit(1)
115
116 # Read the datasheet now. Get the expected design name
117
118 dsheet = {}
119 print('Reading JSON datasheet ' + datasheet_filepath)
120 with open(datasheet_filepath, 'r') as user_doc_file:
121 docinfo = json.load(user_doc_file)
122 dsheet = docinfo['data-sheet']
123 name = dsheet['ip-name']
124
125 # Get JSON file of settings if it exists. It should be in the same
emayecsb2487ae2021-08-05 10:30:13 -0400126 # location as the JSON datasheet file (generated by cace.py)
emayecs5966a532021-07-29 10:07:02 -0400127 testmode = False
128 force = False
129 settings_filepath = os.path.split(datasheet_filepath)[0] + '/settings.json'
130 if os.path.exists(settings_filepath):
131 with open(settings_filepath, 'r') as user_settings_file:
132 settings = json.load(user_settings_file)
133 docinfo['settings'] = settings
134 if 'submit-as-schematic' in settings:
135 if settings['submit-as-schematic'] == True:
136 force = True
137 if 'submit-test-mode' in settings:
138 if settings['submit-test-mode'] == True:
139 testmode = True
140
141 # Use of "-force" in the options overrides any settings from the JSON file.
142 if '-force' in options:
143 force = True
144
145 if '-test' in options:
146 testmode = True
147
148 # Diagnostic
149 if 'identifiers' in docinfo:
150 print('Identifiers: ' + str(docinfo['identifiers']))
151
152 if not design_filepath:
153 print('Error: No schematic or netlist directory given\n')
154 sys.exit(1)
155 else:
156 # If design_filepath has a subdirectory "design", add that to
157 # the path name.
158 if os.path.isdir(design_filepath + '/spi'):
159 spice_filepath = design_filepath + '/spi'
160 filelist = os.listdir(spice_filepath)
161 if os.path.isdir(design_filepath + '/elec'):
162 if os.path.isdir(design_filepath + '/elec/' + name + '.delib'):
163 schem_filepath = design_filepath + '/elec/' + name + '.delib'
164 filelist.extend(os.listdir(schem_filepath))
165
166 # To be valid, the filepath must contain either a .spi file with
167 # the name of ip-name, or a .sch file with the name of ip-name.
168 netlistname = name + '.spi'
169 schemname = name + '.sch'
170 if netlistname not in filelist and schemname not in filelist:
171 print('Error: Path ' + design_filepath + ' has no schematic '
172 + 'or netlist for design ' + name + '\n')
173 sys.exit(1)
174
175 # Add key 'project-folder' to the document, containing the path to the
176 # JSON file. This is used by the CACE to ensure that progress information
177 # is passed to the correct folder, not relying on hard-coded home paths,
178 # and allowing for copies of projects in different paths.
179
180 foldername = os.path.split(datasheet_filepath)[0]
181 docinfo['project-folder'] = foldername
182
183 # Current expectation is to use UID (username). If it is not in the
184 # document, then add it here.
185
186 if testmode:
187 uid = {}
188 else:
189 if 'UID' not in docinfo:
190 uid = local_uid_services.get_uid(os.environ['USER'])
191 else:
192 uid = docinfo['UID']
193
194 if not uid or uid == 'null':
195 uid = os.environ['USER']
196 docinfo['UID'] = uid
197
198 # Handle cancel requests
199 if '-cancel' in options:
200 # Read last message . . .
201 if os.path.exists(design_filepath + '/ngspice/char/remote_status.json'):
202 with open(design_filepath + '/ngspice/char/remote_status.json', 'r') as f:
203 status = json.load(f)
204 if 'hash' in status:
205 docinfo['request-hash'] = status['hash']
206 send_cancel_doc(docinfo)
207 else:
208 print('No hash value in status file, cannot cancel.')
209 else:
210 print('No status file found, cannot cancel.')
211 sys.exit(0)
212
213 # If settings specify that the submission should be forced to be schematic-only,
214 # pass the setting to CACE as 'netlist-source' in the data-sheet record.
215 if force:
216 dsheet['netlist-source'] = 'schematic'
217
218 # Put the current git system state into the target directory
219 # prior to tarballing
220 if os.path.isfile('/ef/.ef-version'):
221 with open('/ef/.ef-version', 'r') as f:
222 ef_version = f.read().rstrip()
223 docinfo['ef-version'] = ef_version
224
225 rhash, timestamp = file_request_hash.get_hash(name)
226 docinfo['request-hash'] = rhash
227 print('request hash = ' + rhash + '\n')
228
229 # Now send the document
230 if testmode:
231 print('Test: running send_doc(docinfo)\n')
232 else:
233 send_doc(docinfo)
234
235 # Send the tarballed design file directory to the marketplace server for storage.
236 # Ignore the log file, which is meant for in-system diagnostics, not for storage.
237 exclusions = [name + '\.log', '.*\.raw',
238 'elec/\.java',
239 'ngspice/run/\.allwaves']
240
241 # If settings specify that the submission should be forced to be schematic-only,
242 # then don't tarball the layout database files.
243 if force:
244 exclusions.append('mag/.*\.mag')
245 exclusions.append('mag/.*\.ext')
246
247 # Now send the netlist file tarball
248 tarballname = name + '.tar.gz'
249 if testmode:
250 file_compressor.tar_directory_contents_to_file(design_filepath,
251 tarballname, exclude=exclusions)
252 os.rename(design_filepath + '/' + tarballname, tarballname)
253 print('Test: running send_file(' + rhash + ', <tarball>, ' + tarballname + ')\n')
254 else:
255 tar = file_compressor.tar_directory_contents(design_filepath,
256 exclude=exclusions)
257 send_file(rhash, tar, tarballname)
258