Exemple #1
0
  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)})
Exemple #2
0
  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))