def createModel(cls, modelSpec=None): """ NOTE MER-3479: this code path is presently incorrectly used for two purposes: * Creating CloudWatch models (correct) * Importing of all types of metrics (not desirable; there should be a separate endpoint or an import-specific flag in this endpoint for importing that facilitates slightly different behavior, such as suppressing certain errors to allow for re-import in case of tranisent error part way through the prior import) """ if not modelSpec: # Metric data is missing log.error("Data is missing in request, raising BadRequest exception") raise InvalidRequestResponse({"result": "Metric data is missing"}) # TODO MER-3479: import using import-specific endpoint # NOTE: pending MER-3479, this is presently a hack for exercising # the adapter import API importing = False if modelSpec.get("datasource") == "custom": # Convert to new YOMP-custom metric modelSpec format # NOTE: backward compatibility during first phase refactoring modelSpec = cls.upgradeCustomModelSpec(modelSpec) if "data" in modelSpec: importing = True elif (modelSpec.get("datasource") == "cloudwatch" and "filters" not in modelSpec): if "type" in modelSpec: # The legacy cloudwatch import modelSpec had the "type" property assert modelSpec["type"] == "metric", repr(modelSpec) importing = True # Convert to new YOMP-custom metric modelSpec format # NOTE: backward compatibility during first phase refactoring modelSpec = cls.upgradeCloudwatchModelSpec(modelSpec) elif (modelSpec.get("datasource") == "autostack" or modelSpec.get("type") == "autostack"): importing = True # Convert to new autostack metric modelSpec format # NOTE: backward compatibility during first phase refactoring modelSpec = cls.upgradeAutostackModelSpec(modelSpec) try: with web.ctx.connFactory() as conn: with conn.begin(): adapter = createDatasourceAdapter(modelSpec["datasource"]) if modelSpec["datasource"] == "custom": checkQuotaForCustomMetricAndRaise(conn) else: checkQuotaForInstanceAndRaise( conn, adapter.getInstanceNameForModelSpec(modelSpec)) try: if importing: # TODO MER-3479: import using import-specific endpoint # NOTE: pending MER-3479, this is presently a hack for exercising # the adapter import API metricId = adapter.importModel(modelSpec) else: metricId = adapter.monitorMetric(modelSpec) except app_exceptions.MetricAlreadyMonitored as e: metricId = e.uid return repository.getMetric(conn, metricId) except (ValueError, app_exceptions.MetricNotSupportedError) as e: raise InvalidRequestResponse({"result": repr(e)})
def POST(self): # pylint: disable=C0103 r""" Create an Autostack :: POST /_autostacks { "name": {name}, "region": {region}, "filters": { "tag:{Name}": ["{value}", "{value}", ...], "tag:{Description}": ["{value}", "{value}", ...], "tag:{etc}": ["{value}", "{value}", ...] } } Request body must be a dictionary that includes: :param name: Unique autostack name :type name: str :param region: AWS region :type region: str :param filters: AWS Tag value pattern :type filters: dict The creation request will be rejected if the filters match more than MAX_INSTANCES_PER_AUTOSTACK. From http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Filtering.html: :: You can also use wildcards with the filter values. An asterisk (*) matches zero or more characters, and a question mark (?) matches exactly one character. For example, you can use *database* as a filter value to get all EBS snapshots that include database in the description. If you were to specify database as the filter value, then only snapshots whose description equals database would be returned. Filter values are case sensitive. We support only exact string matching, or substring matching (with wildcards). Tip Your search can include the literal values of the wildcard characters; you just need to escape them with a backslash before the character. For example, a value of \*numenta\?\\ searches for the literal string *numenta?\. """ try: self.addStandardHeaders() data = web.data() if not data: raise web.badrequest("Metric data is missing") nativeMetric = utils.jsonDecode(data) try: stackSpec = { "name": nativeMetric["name"], "aggSpec": { "datasource": "cloudwatch", # only support cloudwatch for now "region": nativeMetric["region"], "resourceType": "AWS::EC2::Instance", # only support EC2 for now "filters": nativeMetric["filters"] } } adapter = createAutostackDatasourceAdapter() with web.ctx.connFactory() as conn: checkQuotaForInstanceAndRaise(conn, None) autostack = adapter.createAutostack(stackSpec) result = dict(autostack.items()) except DuplicateRecordError: # TODO [MER-3543]: Make sure this actually gets hit raise web.internalerror( "The name you are trying to use, '%s', is already in use in AWS " "region '%s'. Please enter a unique Autostack name." % ( nativeMetric.get("name", "None"), nativeMetric.get("region", "None"))) raise web.created(utils.jsonEncode(result)) except (web.HTTPError, QuotaError) as ex: if bool(re.match(r"([45][0-9][0-9])\s?", web.ctx.status)): # Log 400-599 status codes as errors, ignoring 200-399 log.error(str(ex) or repr(ex)) raise except Exception as ex: log.exception("POST Failed") raise web.internalerror(str(ex) or repr(ex))