in esrally/track/loader.py [0:0]
def read(self, track_name, track_spec_file, mapping_dir):
"""
Reads a track file, verifies it against the JSON schema and if valid, creates a track.
:param track_name: The name of the track.
:param track_spec_file: The complete path to the track specification file.
:param mapping_dir: The directory where the mapping files for this track are stored locally.
:return: A corresponding track instance if the track file is valid.
"""
self.logger.info("Reading track specification file [%s].", track_spec_file)
# render the track to a temporary file instead of dumping it into the logs. It is easier to check for error messages
# involving lines numbers and it also does not bloat Rally's log file so much.
with tempfile.NamedTemporaryFile(delete=False, suffix=".json") as tmp:
try:
rendered = render_template_from_file(
track_spec_file,
self.track_params,
complete_track_params=self.complete_track_params,
build_flavor=self.build_flavor,
serverless_operator=self.serverless_operator,
)
with open(tmp.name, "w", encoding="utf-8") as f:
f.write(rendered)
self.logger.info("Final rendered track for '%s' has been written to '%s'.", track_spec_file, tmp.name)
track_spec = json.loads(rendered)
except jinja2.exceptions.TemplateSyntaxError as te:
self.logger.exception("Could not load [%s] due to Jinja Syntax Exception.", track_spec_file)
msg = f"Could not load '{track_spec_file}' due to Jinja Syntax Exception. "
msg += f"The track file ({tmp.name}) likely hasn't been written."
raise TrackSyntaxError(msg, te)
except jinja2.exceptions.TemplateNotFound:
self.logger.exception("Could not load [%s]", track_spec_file)
raise exceptions.SystemSetupError(f"Track {track_name} does not exist")
except json.JSONDecodeError as e:
self.logger.exception("Could not load [%s].", track_spec_file)
msg = f"Could not load '{track_spec_file}': {str(e)}."
if e.doc and e.lineno > 0 and e.colno > 0:
line_idx = e.lineno - 1
lines = e.doc.split("\n")
ctx_line_count = 3
ctx_start = max(0, line_idx - ctx_line_count)
ctx_end = min(line_idx + ctx_line_count, len(lines))
erroneous_lines = lines[ctx_start:ctx_end]
erroneous_lines.insert(line_idx - ctx_start + 1, "-" * (e.colno - 1) + "^ Error is here")
msg += " Lines containing the error:\n\n{}\n\n".format("\n".join(erroneous_lines))
msg += f"The complete track has been written to '{tmp.name}' for diagnosis."
raise TrackSyntaxError(msg)
except Exception as e:
self.logger.exception("Could not load [%s].", track_spec_file)
msg = f"Could not load '{track_spec_file}'. The complete track has been written to '{tmp.name}' for diagnosis."
raise TrackSyntaxError(msg, e)
# check the track version before even attempting to validate the JSON format to avoid bogus errors.
raw_version = track_spec.get("version", TrackFileReader.MAXIMUM_SUPPORTED_TRACK_VERSION)
try:
track_version = int(raw_version)
except ValueError:
raise exceptions.InvalidSyntax("version identifier for track %s must be numeric but was [%s]" % (track_name, str(raw_version)))
if TrackFileReader.MINIMUM_SUPPORTED_TRACK_VERSION > track_version:
raise exceptions.RallyError(
"Track {} is on version {} but needs to be updated at least to version {} to work with the "
"current version of Rally.".format(track_name, track_version, TrackFileReader.MINIMUM_SUPPORTED_TRACK_VERSION)
)
if TrackFileReader.MAXIMUM_SUPPORTED_TRACK_VERSION < track_version:
raise exceptions.RallyError(
"Track {} requires a newer version of Rally. Please upgrade Rally (supported track version: {}, "
"required track version: {}).".format(track_name, TrackFileReader.MAXIMUM_SUPPORTED_TRACK_VERSION, track_version)
)
try:
jsonschema.validate(track_spec, self.track_schema)
except jsonschema.exceptions.ValidationError as ve:
raise TrackSyntaxError(
"Track '{}' is invalid.\n\nError details: {}\nInstance: {}\nPath: {}\nSchema path: {}".format(
track_name, ve.message, json.dumps(ve.instance, indent=4, sort_keys=True), ve.absolute_path, ve.absolute_schema_path
)
)
current_track = self.read_track(track_name, track_spec, mapping_dir, track_spec_file)
internal_user_defined_track_params = self.complete_track_params.internal_user_defined_track_params()
if len(internal_user_defined_track_params) > 0:
params_list = ",".join(opts.double_quoted_list_of(sorted(internal_user_defined_track_params)))
err_msg = f"Some of your track parameter(s) {params_list} are defined by Rally and cannot be modified.\n"
self.logger.critical(err_msg)
# also dump the message on the console
console.println(err_msg)
raise exceptions.TrackConfigError(f"Reserved track parameters {sorted(internal_user_defined_track_params)}.")
unused_user_defined_track_params = self.complete_track_params.unused_user_defined_track_params()
if len(unused_user_defined_track_params) > 0:
err_msg = (
"Some of your track parameter(s) {} are not used by this track; perhaps you intend to use {} instead.\n\n"
"All track parameters you provided are:\n"
"{}\n\n"
"All parameters exposed by this track:\n"
"{}".format(
",".join(opts.double_quoted_list_of(sorted(unused_user_defined_track_params))),
",".join(
opts.double_quoted_list_of(
sorted(
opts.make_list_of_close_matches(
unused_user_defined_track_params, self.complete_track_params.track_defined_params
)
)
)
),
"\n".join(opts.bulleted_list_of(sorted(list(self.track_params.keys())))),
"\n".join(opts.bulleted_list_of(self.complete_track_params.sorted_track_defined_params)),
)
)
self.logger.critical(err_msg)
# also dump the message on the console
console.println(err_msg)
raise exceptions.TrackConfigError(f"Unused track parameters {sorted(unused_user_defined_track_params)}.")
return current_track