Exemplo n.º 1
0
    def DELETE(self):
        """
    Delete models for multiple instances

    ::

        DELETE /_instances

    DELETE data:

    ::

        [
            "{region}/{namespace}/{instanceId}",
            ...
        ]

    Returns:

    ::

        {
            "result": "success"
        }
    """
        try:
            instances = json.loads(web.data())
        except:
            raise InvalidRequestResponse({"result": "Invalid request"})

        if not instances:
            raise InvalidRequestResponse(
                {"result": ("Missing instances in DELETE"
                            " request")})

        deleted = []
        if instances:
            for server in instances:
                if server.count("/") == 4:
                    (lhs, _, identifier) = server.rpartition("/")
                    (regionAndNamespace, _, _) = lhs.rpartition("/")
                    serverSansDimension = regionAndNamespace + "/" + identifier
                else:
                    serverSansDimension = server
                with web.ctx.connFactory() as conn:
                    modelIds = repository.listMetricIDsForInstance(
                        conn, serverSansDimension)
                if modelIds:
                    for modelId in modelIds:
                        ModelHandler.deleteModel(modelId)
                    deleted.append(server)

        if instances == deleted:
            self.addStandardHeaders()
            return encodeJson({'result': 'success'})

        raise web.notfound("Not able to delete %s" %
                           encodeJson(list(set(instances) - set(deleted))))
Exemplo n.º 2
0
  def DELETE(self):
    """
    Delete models for multiple instances

    ::

        DELETE /_instances

    DELETE data:

    ::

        [
            "{region}/{namespace}/{instanceId}",
            ...
        ]

    Returns:

    ::

        {
            "result": "success"
        }
    """
    try:
      instances = json.loads(web.data())
    except:
      raise InvalidRequestResponse({"result": "Invalid request"})

    if not instances:
      raise InvalidRequestResponse({"result": ("Missing instances in DELETE"
                                               " request")})

    deleted = []
    if instances:
      for server in instances:
        if server.count("/") == 4:
          (lhs, _, identifier) = server.rpartition("/")
          (regionAndNamespace, _, _) = lhs.rpartition("/")
          serverSansDimension = regionAndNamespace + "/" + identifier
        else:
          serverSansDimension = server
        with web.ctx.connFactory() as conn:
          modelIds = repository.listMetricIDsForInstance(conn,
                                                         serverSansDimension)
        if modelIds:
          for modelId in modelIds:
            ModelHandler.deleteModel(modelId)
          deleted.append(server)

    if instances == deleted:
      self.addStandardHeaders()
      return encodeJson({'result': 'success'})

    raise web.notfound("Not able to delete %s" %
                       encodeJson(list(set(instances)-set(deleted))))
Exemplo n.º 3
0
    def GET(self):
        """
    Get all instances

    ::

        GET /_instances

    Sample Output:

    ::

        [
            {
                "location": "us-west-2",
                "message": null,
                "name": "jenkins-master",
                "namespace": "AWS/EC2",
                "server": "i-12345678",
                "status": 2
                "parameters": {
                    "region": "us-west-2",
                    "AutoScalingGroupName": "YOMPsolutions-com-ssl"
                },

            },
            ...
        ]
    """
        with web.ctx.connFactory() as conn:
            instances = repository.getInstances(conn)
        # To support idempotency requirements of the web ui, ensure that server
        # parameter matches the same pattern as is required for POST, and DELETE.
        # This means inserting the dimension between the namespace and identifier
        # for AWS-only.  Autostacks are ignored here.
        for instance in instances:
            if "/AWS/" in instance["server"]:
                dimensions = instance["parameters"]["metricSpec"]["dimensions"]

                for (dimension, value) in dimensions.iteritems():
                    if instance["server"].endswith("/" + value):
                        # We're looking for the identifying dimension in the instance
                        # parameters
                        (lhs, _,
                         identifier) = (instance["server"].rpartition("/"))
                        instance["server"] = "/".join(
                            [lhs, dimension, identifier])
                    if instance["message"] is "":
                        instance["message"] = None

        self.addStandardHeaders()
        return encodeJson(instances)
Exemplo n.º 4
0
  def GET(self):
    """
    Get all instances

    ::

        GET /_instances

    Sample Output:

    ::

        [
            {
                "location": "us-west-2",
                "message": null,
                "name": "jenkins-master",
                "namespace": "AWS/EC2",
                "server": "i-12345678",
                "status": 2
                "parameters": {
                    "region": "us-west-2",
                    "AutoScalingGroupName": "YOMPsolutions-com-ssl"
                },

            },
            ...
        ]
    """
    with web.ctx.connFactory() as conn:
      instances = repository.getInstances(conn)
    # To support idempotency requirements of the web ui, ensure that server
    # parameter matches the same pattern as is required for POST, and DELETE.
    # This means inserting the dimension between the namespace and identifier
    # for AWS-only.  Autostacks are ignored here.
    for instance in instances:
      if "/AWS/" in instance["server"]:
        dimensions = instance["parameters"]["metricSpec"]["dimensions"]

        for (dimension, value) in dimensions.iteritems():
          if instance["server"].endswith("/" + value):
            # We're looking for the identifying dimension in the instance
            # parameters
            (lhs, _, identifier) = (instance["server"].rpartition("/"))
            instance["server"] = "/".join([lhs, dimension, identifier])
          if instance["message"] is "":
            instance["message"] = None

    self.addStandardHeaders()
    return encodeJson(instances)
Exemplo n.º 5
0
    def POST(self):  # pylint: disable=R0201,C0103
        """
    Handles calls from clients for messages. Expects to received batched
    requests, keyed by the name of the template used to identify responses.
    """
        global messageManager  # pylint: disable=W0603
        data = web.input()
        messagesOut = {}

        if YOMP.app.DEBUG_LEVEL > 0:
            # When debugging, read the file and create new MessageManager on
            # every request so we can change messages.json on the fly.
            with open(
                    os.path.join(YOMP.app.YOMP_HOME,
                                 "resources/messages/us/messages.json")) as f:
                messageManager = messagemanager.MessageManager(f.read())

        # Make sure we have the latest version of configuration
        YOMP.app.config.loadConfig()
        for templateKey in data:
            templateLocation = data[templateKey]

            if templateKey == "explicit":
                # The templateKey is either an id or the string "explicit", which means
                # that the client wants to identify the key directly, not by template,
                # which is easy enough.
                for templateId in templateLocation.split(","):
                    messagesOut[templateId] = messageManager.getMessagesByKey(
                        templateId)
            else:
                # To get the right relative lookup path for messages.json, we"ll
                # remove known template paths.
                baseUrl = YOMP.app.config.get("web", "base_url")
                path = templateLocation\
                      .replace(baseUrl + "/static/js/program/templates/", "")
                msgs = messageManager.getMessagesForTemplate(path)
                messagesOut[templateKey] = msgs

        web.header("Content-Type", "application/json; charset=UTF-8", True)
        return encodeJson(messagesOut)
Exemplo n.º 6
0
    def POST(self):  # pylint: disable=C0103
        data = jsonDecode(web.data())
        YOMP.app.config.loadConfig()
        # if they already authed, new aws creds must match the originals
        if YOMP.app.config.has_section("aws"):
            if YOMP.app.config.has_option("aws", "aws_access_key_id"):
                awsAccessKeyId = YOMP.app.config.get("aws",
                                                     "aws_access_key_id")
                if awsAccessKeyId and awsAccessKeyId != data[
                        "aws_access_key_id"]:
                    raise UnauthorizedResponse({
                        "result":
                        ("Please use the same AWS Credentials that you "
                         "initially authenticated with.")
                    })

        try:
            checkEC2Authorization(data["aws_access_key_id"],
                                  data["aws_secret_access_key"])
        except (AuthFailure, AWSPermissionsError) as e:
            raise UnauthorizedResponse({"result": str(e)})

        result = {"result": "success"}

        apikey = None

        if YOMP.app.config.has_section("security"):
            if YOMP.app.config.has_option("security", "apikey"):
                apikey = YOMP.app.config.get("security", "apikey")

        if not apikey:
            apikey = self.generateAPIKey()
            YOMP.app.config.set("security", "apikey", apikey)
            YOMP.app.config.save()

        result["apikey"] = apikey

        web.header("Content-Type", "application/json; charset=UTF-8", True)
        return encodeJson(result)
Exemplo n.º 7
0
  def POST(self): # pylint: disable=R0201,C0103
    """
    Handles calls from clients for messages. Expects to received batched
    requests, keyed by the name of the template used to identify responses.
    """
    global messageManager # pylint: disable=W0603
    data = web.input()
    messagesOut = {}

    if YOMP.app.DEBUG_LEVEL > 0:
      # When debugging, read the file and create new MessageManager on
      # every request so we can change messages.json on the fly.
      with open(os.path.join(YOMP.app.YOMP_HOME,
                             "resources/messages/us/messages.json")) as f:
        messageManager = messagemanager.MessageManager(f.read())

    # Make sure we have the latest version of configuration
    YOMP.app.config.loadConfig()
    for templateKey in data:
      templateLocation = data[templateKey]

      if templateKey == "explicit":
        # The templateKey is either an id or the string "explicit", which means
        # that the client wants to identify the key directly, not by template,
        # which is easy enough.
        for templateId in templateLocation.split(","):
          messagesOut[templateId] = messageManager.getMessagesByKey(templateId)
      else:
        # To get the right relative lookup path for messages.json, we"ll
        # remove known template paths.
        baseUrl = YOMP.app.config.get("web", "base_url")
        path = templateLocation\
              .replace(baseUrl + "/static/js/program/templates/", "")
        msgs = messageManager.getMessagesForTemplate(path)
        messagesOut[templateKey] = msgs

    web.header("Content-Type", "application/json; charset=UTF-8", True)
    return encodeJson(messagesOut)
Exemplo n.º 8
0
  def POST(self):  # pylint: disable=C0103
    data = jsonDecode(web.data())
    YOMP.app.config.loadConfig()
    # if they already authed, new aws creds must match the originals
    if YOMP.app.config.has_section("aws"):
      if YOMP.app.config.has_option("aws", "aws_access_key_id"):
        awsAccessKeyId = YOMP.app.config.get("aws", "aws_access_key_id")
        if awsAccessKeyId and awsAccessKeyId != data["aws_access_key_id"]:
          raise UnauthorizedResponse({
              "result": ("Please use the same AWS Credentials that you "
                         "initially authenticated with.")
          })

    try:
      checkEC2Authorization(data["aws_access_key_id"],
                            data["aws_secret_access_key"])
    except (AuthFailure, AWSPermissionsError) as e:
      raise UnauthorizedResponse({"result": str(e)})

    result = {"result": "success"}

    apikey = None

    if YOMP.app.config.has_section("security"):
      if YOMP.app.config.has_option("security", "apikey"):
        apikey = YOMP.app.config.get("security", "apikey")

    if not apikey:
      apikey = self.generateAPIKey()
      YOMP.app.config.set("security", "apikey", apikey)
      YOMP.app.config.save()

    result["apikey"] = apikey

    web.header("Content-Type", "application/json; charset=UTF-8", True)
    return encodeJson(result)
Exemplo n.º 9
0
  def POST(self, region, namespace, instanceId=None):
    """
    Monitor a set of default metrics for a specific instance

    ::

        POST /_instances/{region}/{namespace}/{instanceId}

    Returns:

    ::

        {
            "result": "success"
        }

    OR

    Monitor a set of default metrics for multiple specific instances

    ::

        POST /_instances/{region}/{namespace}

    POST data:

    ::

        [
            {instanceId},
            ...
        ]

    Returns:

    ::

        {
            "result": "success"
        }

    Note:
    We expect a 200 OK even when attempting to POST to an instanece in the wrong
    namespace or the wrong region, this saves the overhead of asking AWS if
    we're dealing with a valid instance in the given namespace or region
    with every POST request.
    We expect the CLI user to know the correct instance ID.
    """
    if instanceId is None:
      try:
        dimension = None
        instances = json.loads(web.data())
      except:
        raise InvalidRequestResponse({"result": "Invalid request"})

    else:
      (dimension, _, identifier) = instanceId.rpartition("/")
      instances = [identifier]

    # Check for invalid region or namespace
    cwAdapter = datasource.createDatasourceAdapter("cloudwatch")

    supportedRegions = set(region for region, _desc in
                           cwAdapter.describeRegions())
    if region not in supportedRegions:
      raise InvalidRequestResponse({"result": ("Not supported. Region '%s' was"
                                               " not found.") % region})

    supportedNamespaces = set()
    for resourceInfo in cwAdapter.describeSupportedMetrics().values():
      for metricInfo in resourceInfo.values():
        supportedNamespaces.add(metricInfo["namespace"])
    if namespace not in supportedNamespaces:
      raise InvalidRequestResponse({"result": ("Not supported. Namespace '%s' "
                                               "was not found.") % namespace})

    try:
      # Attempt to validate instances list using validictory
      validate(instances, _INSTANCES_MODEL_CREATION_SCHEMA)
    except ValidationError as e:
      response = "InvalidArgumentsError: " + str(e)
      raise InvalidRequestResponse({"result": response})

    if instances:
      for instanceId in instances:
        server = "/".join([region, namespace, instanceId])
        with web.ctx.connFactory() as conn:
          numMetrics = repository.getMetricCountForServer(conn, server)
        if numMetrics > 0:
          # Metrics exist for instance id.
          pass

        else:
          try:
            resourceType = cloudwatch.NAMESPACE_TO_RESOURCE_TYPE[namespace]
          except KeyError:
            raise InvalidRequestResponse({"result": "Not supported."})

          modelSpecs = cwAdapter.getDefaultModelSpecs(
              resourceType, region, instanceId, dimension)

          for modelSpec in modelSpecs:
            ModelHandler.createModel(modelSpec)

    self.addStandardHeaders()
    return encodeJson({"result": "success"})
Exemplo n.º 10
0
  def GET(self, region=None): # pylint: disable=R0201
    """
    Get quick selection instance suggestions to monitor

    ::

        GET /_instances/suggestions

    Sample Output:

    ::

        {
            "suggested": [
              {
                  "region": "us-west-2",
                  "namespace": "AWS/EC2",
                  "id": "i-12345678"
              },
              ... (up to 8 total suggested) ...
          ],
          "alternates": [
              {
                  "region": "us-west-2",
                  "namespace": "AWS/ELB",
                  "id": "YOMP-docs-elb"
              },
              ... (up to 22 total alternatives) ...
          ]
      }
      """
    if region is None:
      region = config.get("aws", "default_region")

    ec2Queue = Queue.Queue()
    ec2Thread = threading.Thread(
        target=ec2_utils.getSuggestedInstances,
        args=(region, ec2Queue, _AWS_INSTANCE_FETCHING_TIME_LIMIT))
    ec2Thread.start()

    rdsQueue = Queue.Queue()
    rdsThread = threading.Thread(
        target=rds_utils.getSuggestedInstances,
        args=(region, rdsQueue, _AWS_INSTANCE_FETCHING_TIME_LIMIT))
    rdsThread.start()

    elbQueue = Queue.Queue()
    elbThread = threading.Thread(
        target=elb_utils.getSuggestedInstances,
        args=(region, elbQueue, _AWS_INSTANCE_FETCHING_TIME_LIMIT))
    elbThread.start()

    asgQueue = Queue.Queue()
    asgThread = threading.Thread(
        target=asg_utils.getSuggestedInstances,
        args=(region, asgQueue, _AWS_INSTANCE_FETCHING_TIME_LIMIT))
    asgThread.start()

    response = {
        "suggested": [],
        "alternates": [],
    }

    # Wait for the threads to finish
    ec2Thread.join()
    rdsThread.join()
    elbThread.join()
    asgThread.join()

    n = 0
    done = False
    while n < _MAX_SUGGESTED_INSTANCES_TOTAL and not done:
      done = True

      # EC2 Instances
      try:
        instance = ec2Queue.get(block=False)
        if n < _NUM_SUGGESTED_INSTANCES:
          response["suggested"].append(instance)
        else:
          response["alternates"].append(instance)
        done = False
        n += 1
        if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
          break
      except Queue.Empty:
        pass

      # RDS Instances
      try:
        instance = rdsQueue.get(block=False)
        if n < _NUM_SUGGESTED_INSTANCES:
          response["suggested"].append(instance)
        else:
          response["alternates"].append(instance)
        done = False
        n += 1
        if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
          break
      except Queue.Empty:
        pass

      # Load Balancers
      try:
        instance = elbQueue.get(block=False)
        if n < _NUM_SUGGESTED_INSTANCES:
          response["suggested"].append(instance)
        else:
          response["alternates"].append(instance)
        done = False
        n += 1
        if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
          break
      except Queue.Empty:
        pass

      # AutoScaling groups
      try:
        instance = asgQueue.get(block=False)
        if n < _NUM_SUGGESTED_INSTANCES:
          response["suggested"].append(instance)
        else:
          response["alternates"].append(instance)
        done = False
        n += 1
        if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
          break
      except Queue.Empty:
        pass

    return encodeJson(response)
Exemplo n.º 11
0
    def POST(self, region, namespace, instanceId=None):
        """
    Monitor a set of default metrics for a specific instance

    ::

        POST /_instances/{region}/{namespace}/{instanceId}

    Returns:

    ::

        {
            "result": "success"
        }

    OR

    Monitor a set of default metrics for multiple specific instances

    ::

        POST /_instances/{region}/{namespace}

    POST data:

    ::

        [
            {instanceId},
            ...
        ]

    Returns:

    ::

        {
            "result": "success"
        }

    Note:
    We expect a 200 OK even when attempting to POST to an instanece in the wrong
    namespace or the wrong region, this saves the overhead of asking AWS if
    we're dealing with a valid instance in the given namespace or region
    with every POST request.
    We expect the CLI user to know the correct instance ID.
    """
        if instanceId is None:
            try:
                dimension = None
                instances = json.loads(web.data())
            except:
                raise InvalidRequestResponse({"result": "Invalid request"})

        else:
            (dimension, _, identifier) = instanceId.rpartition("/")
            instances = [identifier]

        # Check for invalid region or namespace
        cwAdapter = datasource.createDatasourceAdapter("cloudwatch")

        supportedRegions = set(
            region for region, _desc in cwAdapter.describeRegions())
        if region not in supportedRegions:
            raise InvalidRequestResponse({
                "result": ("Not supported. Region '%s' was"
                           " not found.") % region
            })

        supportedNamespaces = set()
        for resourceInfo in cwAdapter.describeSupportedMetrics().values():
            for metricInfo in resourceInfo.values():
                supportedNamespaces.add(metricInfo["namespace"])
        if namespace not in supportedNamespaces:
            raise InvalidRequestResponse({
                "result": ("Not supported. Namespace '%s' "
                           "was not found.") % namespace
            })

        try:
            # Attempt to validate instances list using validictory
            validate(instances, _INSTANCES_MODEL_CREATION_SCHEMA)
        except ValidationError as e:
            response = "InvalidArgumentsError: " + str(e)
            raise InvalidRequestResponse({"result": response})

        if instances:
            for instanceId in instances:
                server = "/".join([region, namespace, instanceId])
                with web.ctx.connFactory() as conn:
                    numMetrics = repository.getMetricCountForServer(
                        conn, server)
                if numMetrics > 0:
                    # Metrics exist for instance id.
                    pass

                else:
                    try:
                        resourceType = cloudwatch.NAMESPACE_TO_RESOURCE_TYPE[
                            namespace]
                    except KeyError:
                        raise InvalidRequestResponse(
                            {"result": "Not supported."})

                    modelSpecs = cwAdapter.getDefaultModelSpecs(
                        resourceType, region, instanceId, dimension)

                    for modelSpec in modelSpecs:
                        ModelHandler.createModel(modelSpec)

        self.addStandardHeaders()
        return encodeJson({"result": "success"})
Exemplo n.º 12
0
    def GET(self, region=None):  # pylint: disable=R0201
        """
    Get quick selection instance suggestions to monitor

    ::

        GET /_instances/suggestions

    Sample Output:

    ::

        {
            "suggested": [
              {
                  "region": "us-west-2",
                  "namespace": "AWS/EC2",
                  "id": "i-12345678"
              },
              ... (up to 8 total suggested) ...
          ],
          "alternates": [
              {
                  "region": "us-west-2",
                  "namespace": "AWS/ELB",
                  "id": "YOMP-docs-elb"
              },
              ... (up to 22 total alternatives) ...
          ]
      }
      """
        if region is None:
            region = config.get("aws", "default_region")

        ec2Queue = Queue.Queue()
        ec2Thread = threading.Thread(target=ec2_utils.getSuggestedInstances,
                                     args=(region, ec2Queue,
                                           _AWS_INSTANCE_FETCHING_TIME_LIMIT))
        ec2Thread.start()

        rdsQueue = Queue.Queue()
        rdsThread = threading.Thread(target=rds_utils.getSuggestedInstances,
                                     args=(region, rdsQueue,
                                           _AWS_INSTANCE_FETCHING_TIME_LIMIT))
        rdsThread.start()

        elbQueue = Queue.Queue()
        elbThread = threading.Thread(target=elb_utils.getSuggestedInstances,
                                     args=(region, elbQueue,
                                           _AWS_INSTANCE_FETCHING_TIME_LIMIT))
        elbThread.start()

        asgQueue = Queue.Queue()
        asgThread = threading.Thread(target=asg_utils.getSuggestedInstances,
                                     args=(region, asgQueue,
                                           _AWS_INSTANCE_FETCHING_TIME_LIMIT))
        asgThread.start()

        response = {
            "suggested": [],
            "alternates": [],
        }

        # Wait for the threads to finish
        ec2Thread.join()
        rdsThread.join()
        elbThread.join()
        asgThread.join()

        n = 0
        done = False
        while n < _MAX_SUGGESTED_INSTANCES_TOTAL and not done:
            done = True

            # EC2 Instances
            try:
                instance = ec2Queue.get(block=False)
                if n < _NUM_SUGGESTED_INSTANCES:
                    response["suggested"].append(instance)
                else:
                    response["alternates"].append(instance)
                done = False
                n += 1
                if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
                    break
            except Queue.Empty:
                pass

            # RDS Instances
            try:
                instance = rdsQueue.get(block=False)
                if n < _NUM_SUGGESTED_INSTANCES:
                    response["suggested"].append(instance)
                else:
                    response["alternates"].append(instance)
                done = False
                n += 1
                if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
                    break
            except Queue.Empty:
                pass

            # Load Balancers
            try:
                instance = elbQueue.get(block=False)
                if n < _NUM_SUGGESTED_INSTANCES:
                    response["suggested"].append(instance)
                else:
                    response["alternates"].append(instance)
                done = False
                n += 1
                if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
                    break
            except Queue.Empty:
                pass

            # AutoScaling groups
            try:
                instance = asgQueue.get(block=False)
                if n < _NUM_SUGGESTED_INSTANCES:
                    response["suggested"].append(instance)
                else:
                    response["alternates"].append(instance)
                done = False
                n += 1
                if n >= _MAX_SUGGESTED_INSTANCES_TOTAL:
                    break
            except Queue.Empty:
                pass

        return encodeJson(response)