Example #1
0
    def GET(self, namespace=None):
        """
      List supported Cloudwatch namespaces

      ::

          GET /_metrics/cloudwatch/namespaces

      Returns:

      ::

          {'namespace-name1': {...},
           'namespace-name2': {...}
           ,...
        }


      OR

      List supported Cloudwatch metrics for a given namespace

      ::

          GET /_metrics/cloudwatch/{namespace-name}`

      Returns:

      ::

          {
              'namespace-name': {
                   'metrics': ['metric-name',...],
                   'dimensions': ['dimension-name',...]
              }
          }
    """
        adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter(
        )
        resources = adapter.describeSupportedMetrics()

        namespaces = _translateResourcesIntoNamespaces(resources)

        # Adding Autostacks namespaces to this list for now, to maintain API
        # backwards-compatibility during adapter refactor
        namespaces["Autostacks"] = {"metrics": ["InstanceCount"]}

        if namespace is None:
            self.addStandardHeaders()
            return utils.jsonEncode(namespaces)

        if not namespace in namespaces:
            raise web.NotFound("Namespace '%s' was not found" % namespace)

        self.addStandardHeaders()
        return utils.jsonEncode({str(namespace): namespaces[namespace]})
  def setUpClass(cls):
    cls.autostack = Mock(uid="blahblahblah",
                         region="bogus",
                         filters=jsonEncode({"tag:Name":["Bogus"]}))
    cls.autostack.name = "Test"

    cls.jsonAutostack = jsonEncode({"uid":"blahblahblah",
                                    "name":"Test",
                                    "region":"bogus",
                                    "filters":{"tag:Name":["Bogus"]}})
Example #3
0
  def GET(self, namespace=None):
    """
      List supported Cloudwatch namespaces

      ::

          GET /_metrics/cloudwatch/namespaces

      Returns:

      ::

          {'namespace-name1': {...},
           'namespace-name2': {...}
           ,...
        }


      OR

      List supported Cloudwatch metrics for a given namespace

      ::

          GET /_metrics/cloudwatch/{namespace-name}`

      Returns:

      ::

          {
              'namespace-name': {
                   'metrics': ['metric-name',...],
                   'dimensions': ['dimension-name',...]
              }
          }
    """
    adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter()
    resources = adapter.describeSupportedMetrics()

    namespaces = _translateResourcesIntoNamespaces(resources)

    # Adding Autostacks namespaces to this list for now, to maintain API
    # backwards-compatibility during adapter refactor
    namespaces["Autostacks"] = {"metrics": ["InstanceCount"]}

    if namespace is None:
      self.addStandardHeaders()
      return utils.jsonEncode(namespaces)

    if not namespace in namespaces:
      raise web.NotFound("Namespace '%s' was not found" % namespace)

    self.addStandardHeaders()
    return utils.jsonEncode({str(namespace): namespaces[namespace]})
Example #4
0
  def testAddAnnotationIncomplete(self, _):
    """
    Test Failed to Create incomplete Annotation

    Response::

      HTTP Status 400 Missing "field" in request
    """
    # Annotation without timestamp
    badRequest = self.request.copy()
    del badRequest["timestamp"]
    with self.assertRaises(AppError) as e:
      self.app.post("", app_utils.jsonEncode(badRequest), headers=self.headers)

    error = e.exception
    self.assertRegexpMatches(error.message, "Missing 'timestamp' in request")

    # Annotation without device
    badRequest = self.request.copy()
    del badRequest["device"]
    with self.assertRaises(AppError) as e:
      self.app.post("", app_utils.jsonEncode(badRequest), headers=self.headers)

    error = e.exception
    self.assertRegexpMatches(error.message, "Missing 'device' in request")

    # Annotation without server
    badRequest = self.request.copy()
    del badRequest["server"]
    with self.assertRaises(AppError) as e:
      self.app.post("", app_utils.jsonEncode(badRequest), headers=self.headers)

    error = e.exception
    self.assertRegexpMatches(error.message, "Missing 'server' in request")

    # Annotation without user
    badRequest = self.request.copy()
    del badRequest["user"]
    with self.assertRaises(AppError) as e:
      self.app.post("", app_utils.jsonEncode(badRequest), headers=self.headers)

    error = e.exception
    self.assertRegexpMatches(error.message, "Missing 'user' in request")

    # Annotation without data and message
    badRequest = self.request.copy()
    del badRequest["message"]
    del badRequest["data"]
    with self.assertRaises(AppError) as e:
      self.app.post("", app_utils.jsonEncode(badRequest), headers=self.headers)

    error = e.exception
    self.assertRegexpMatches(error.message,
                             "Annotation must contain either 'message' or 'data'")
Example #5
0
    def DELETE(self):
        """
    Delete models for multiple instances

    ::

        DELETE /_instances

    DELETE data:

    ::

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

    Returns:

    ::

        {
            "result": "success"
        }
    """
        try:
            instances = utils.jsonDecode(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:
                with web.ctx.connFactory() as conn:
                    modelIds = repository.listMetricIDsForInstance(
                        conn, server)
                if modelIds:
                    for modelId in modelIds:
                        ModelHandler.deleteModel(modelId)
                    deleted.append(server)

        if instances == deleted:
            self.addStandardHeaders()
            return utils.jsonEncode({"result": "success"})

        raise web.notfound(
            "Not able to delete %s" %
            utils.jsonEncode(list(set(instances) - set(deleted))))
    def setUpClass(cls):
        cls.autostack = Mock(uid="blahblahblah",
                             region="bogus",
                             filters=jsonEncode({"tag:Name": ["Bogus"]}))
        cls.autostack.name = "Test"

        cls.jsonAutostack = jsonEncode({
            "uid": "blahblahblah",
            "name": "Test",
            "region": "bogus",
            "filters": {
                "tag:Name": ["Bogus"]
            }
        })
  def setUpClass(cls):
    with open(
        os.path.join(
          htm.it.app.HTM_IT_HOME,
          "tests/py/data/app/webservices/models_list.json")) as fileObj:
      cls.model_list = json.load(fileObj)

    cls.autostack = Mock(uid="blahblahblah",
                         region="bogus",
                         filters=jsonEncode({"tag:Name":["Bogus"]}))
    cls.autostack.name = "Test"

    cls.jsonAutostack = jsonEncode({"uid":"blahblahblah",
                                    "name":"Test",
                                    "region":"bogus",
                                    "filters":{"tag:Name":["Bogus"]}})
    cls.metric = Mock(uid="cebe9fab-f416-4845-8dab-02d292244112",
                      datasource="autostack",
                      description="The number of database connections in use "
                                  "by Amazon RDS database",
                      server="htm-itdb2",
                      location="us-east-1",
                      parameters=jsonEncode(
                        {"region":"us-east-1",
                         "DBInstanceIdentifier":"htm-itdb2"}),
                      status=1,
                      message=None,
                      collector_error=None,
                      last_timestamp="2013-08-15 21:25:00",
                      poll_interval=60,
                      tag_name=None,
                      model_params=None,
                      last_rowid=20277)
    cls.metric.name = "AWS/RDS/DatabaseConnections"

    cls.jsonMetric = jsonEncode(
      {"uid":cls.metric.uid,
       "datasource":cls.metric.datasource,
       "name":cls.metric.name,
       "description":cls.metric.description,
       "server":cls.metric.server,
       "location":cls.metric.location,
       "parameters":jsonDecode(cls.metric.parameters),
       "status":cls.metric.status,
       "message":cls.metric.message,
       "last_timestamp":cls.metric.last_timestamp,
       "poll_interval":cls.metric.poll_interval,
       "tag_name":cls.metric.tag_name,
       "last_rowid":cls.metric.last_rowid,
       "display_name":cls.metric.server})
Example #8
0
  def DELETE(self):
    """
    Delete models for multiple instances

    ::

        DELETE /_instances

    DELETE data:

    ::

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

    Returns:

    ::

        {
            "result": "success"
        }
    """
    try:
      instances = utils.jsonDecode(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:
        with web.ctx.connFactory() as conn:
          modelIds = repository.listMetricIDsForInstance(conn, server)
        if modelIds:
          for modelId in modelIds:
            ModelHandler.deleteModel(modelId)
          deleted.append(server)

    if instances == deleted:
      self.addStandardHeaders()
      return utils.jsonEncode({"result": "success"})

    raise web.notfound("Not able to delete %s" %
                       utils.jsonEncode(list(set(instances)-set(deleted))))
Example #9
0
    def testMonitorMetricViaModelSpec(self):
        """
      Happy path testing for the route "/_models" with new modelSpec format
    """
        modelSpec = {
            "datasource": "cloudwatch",
            "metricSpec": {
                "region": "us-west-2",
                "namespace": "AWS/EC2",
                "metric": "CPUUtilization",
                "dimensions": {"InstanceId": "i-12d67826"},
            },
            "modelParams": {"min": 0, "max": 100},  # optional  # optional
        }

        # create a model
        response = self.app.post("/", utils.jsonEncode(modelSpec), headers=self.headers)
        assertions.assertSuccess(self, response, code=201)
        postResult = utils.jsonDecode(response.body)
        self.assertEqual(len(postResult), 1)
        self._checkCreateModelResult(postResult[0], modelSpec["metricSpec"])

        # get model that was previously created
        uid = postResult[0]["uid"]
        response = self.app.get("/%s" % uid, headers=self.headers)
        assertions.assertSuccess(self, response)
        getModelResult = utils.jsonDecode(response.body)
        self.assertItemsEqual(getModelResult[0].keys(), self.modelsTestData["get_response"].keys())

        # get all models in the system
        response = self.app.get("/", headers=self.headers)
        assertions.assertSuccess(self, response)
        allModelsResult = utils.jsonDecode(response.body)
        self.assertItemsEqual(allModelsResult[0].keys(), self.modelsTestData["get_response"].keys())
        self.assertItemsEqual(allModelsResult[0].keys(), self.modelsTestData["get_response"].keys())
        self.assertEqual(len(allModelsResult), 1)

        # Repeat the request to monitor same metric and verify that it returns the
        # same model uid instead of creating a new one
        response = self.app.post("/", utils.jsonEncode(modelSpec), headers=self.headers)
        assertions.assertSuccess(self, response, code=201)
        postResult = utils.jsonDecode(response.body)
        self.assertEqual(postResult[0]["uid"], uid)
        self.assertEqual(len(postResult), 1)
        self._checkCreateModelResult(postResult[0], modelSpec["metricSpec"])

        # Unmonitor the metric
        response = self.app.delete("/%s" % uid, headers=self.headers)
        assertions.assertDeleteSuccessResponse(self, response)
Example #10
0
  def testGetAnnotations(self, getAnnotations, _):
    """
      Test Get Annotation

      Request::

        GET /_annotations?device={device}&user={user}&server={server}
                          &from={from}&to={to}

    Response::

      HTTP 200 Ok
      [
        {
           "uid": "2a123bb1dd4d46e7a806d62efc29cbb9",
           "device", "1231AC32FE",
           "created":"2013-08-27 16:46:51",
           "timestamp":"2013-08-27 16:45:00",
           "user":"******",
           "server":" AWS/EC2/i-53f52b67",
           "message":" The CPU Utilization was high ...",
           "data": { Optional JSON Object }
         },
        ...
      ]
    """
    getAnnotations.return_value = [self.annotation]

    response = self.app.get("?%s=%s" % ("server", "dummy"),
                            headers=self.headers)
    self.assertEqual(response.status, 200)
    actual = json.loads(response.body)
    expected = json.loads(app_utils.jsonEncode(self.annotation))

    self.assertDictEqual(expected, actual[0])
  def testPOSTAutostacks(self, adapterMock, quotaRepositoryMock,
                         _repositoryMock):
    name = "Test"
    region = "Bogus"
    filters = {"tag:Name":["Bogus"]}

    quotaRepositoryMock.getInstanceCount.return_value = 0

    autostackMock = Mock()
    autostackMock.items.return_value = [("name", name),
                                       ("region", region),
                                       ("filters", filters)]
    adapterMock.return_value.createAutostack.return_value = autostackMock

    response = self.app.post("/",
                             app_utils.jsonEncode({"name": name,
                                                   "region": region,
                                                   "filters": filters}),
                             headers=self.headers)

    self.assertEqual(response.status, 201)
    result = json.loads(response.body)
    self.assertEqual(result["name"], "Test")

    self.assertTrue(adapterMock.return_value.createAutostack.called)
  def testPUTNotificationSettingsCreate(self, repositoryMock, engineMock):
    """ Test PUT notification settings (create)
    """

    repositoryMock.getDeviceNotificationSettings.side_effect = (
      ObjectNotFoundError("No settings yet"))

    update = {
      "windowsize": 3601,
      "sensitivity": 0.999999,
      "email_addr": "*****@*****.**"}



    response = self.app.put("/%s/settings" %
                            self.notification["device"],
                            app_utils.jsonEncode(update),
                            headers=self.headers)

    self.assertEqual(response.status, 201)
    self.assertFalse(response.body)
    self.assertTrue(repositoryMock.getDeviceNotificationSettings.called)
    repositoryMock.addDeviceNotificationSettings.assert_called_with(
      engineMock.return_value.connect.return_value.__enter__.return_value,
      self.notification["device"],
      update["windowsize"],
      update["sensitivity"],
      update["email_addr"])
  def GET(self, deviceId, notificationId):
    """
      Get single notification

      ::

          GET /_notifications/{deviceId}/{notificationId}

      Returns:

      ::

          {
            "uid": "e78599c4-758b-4c6e-87b1-daabaccff798",
            "timestamp": "2014-02-07 16:26:44",
            "metric": "e5511f295a474037a75bc966d02b67d2",
            "acknowledged": 0,
            "seen": 0,
            "windowsize": 3600,
            "device": "9a90eaf2-6374-4230-aa96-0830c0a737fe"
          }

      :param uid: Notification ID
      :type uid: str
      :param timestamp: Notification timestamp
      :type timestamp: timestamp
      :param metric: Metric that triggered notification
      :type metric: str
      :param acknowledged: Acknowledged status
      :type acknowledged: int
      :param seen: Seen status
      :type seen: int
      :param windowsize: Notification window in seconds during which no other
        notifications for a given instance should be sent to a given device
      :type windowsize: int
      :param device: Device ID
      :type device: str
    """
    # Update the last_timestamp for the notification device.
    try:
      with web.ctx.connFactory() as conn:
        repository.updateNotificationDeviceTimestamp(conn, deviceId)
    except ObjectNotFoundError:
      return web.notfound("Notification device not found: %s" % deviceId)

    try:
      with web.ctx.connFactory() as conn:
        notificationRow = repository.getNotification(conn,
                                                     notificationId,
                                                     NotificationHandler.fields)
      notificationDict = dict([(col, notificationRow[col])
                              for col in NotificationHandler.fields])

      if notificationRow["device"] != deviceId:
        return web.notfound("Device not found: %s" % deviceId)

      self.addStandardHeaders()
      return utils.jsonEncode(notificationDict)
    except ObjectNotFoundError:
      return web.notfound("Notification not found: %s" % notificationId)
Example #14
0
  def GET(self): # pylint: disable=R0201
    """
      Returns list of supported Clouwdwatch regions

      ::

          GET /_metrics/cloudwatch/regions

      Returns:

      ::

          { 'region-name': 'region-description',...}

      Sample output:

      ::

          {
            "ap-northeast-1": "Asia Pacific (Tokyo) Region",
            "ap-southeast-1": "Asia Pacific (Singapore) Region",
            "ap-southeast-2": "Asia Pacific (Sydney) Region",
            "eu-west-1": "EU (Ireland) Region",
            "sa-east-1": "South America (Sao Paulo) Region",
            "us-east-1": "US East (Northern Virginia) Region",
            "us-west-1": "US West (Northern California) Region",
            "us-west-2": "US West (Oregon) Region"
          }
    """
    adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter()
    AuthenticatedBaseHandler.addStandardHeaders()
    return utils.jsonEncode(dict(adapter.describeRegions()))
Example #15
0
  def GET(self): # pylint: disable=C0103
    """
      Get list of autostacks

      Example request::

        GET /_autostacks

      Example response::

        [
          {
            "name": {name}
            "region": {region}
            "filters": {
              "tag:{Name}": ["{value}", "{value}", ...],
              "tag:{Description}": ["{value}", "{value}", ...],
              "tag:{etc}": ["{value}", "{value}", ...]
            },
            "uid": {uid}
          }
          ...
        ]

    """
    self.addStandardHeaders()
    with web.ctx.connFactory() as conn:
      autostackRows = repository.getAutostackList(conn)
    autostackList = [{"uid":autostack.uid,
                      "name":autostack.name,
                      "region":autostack.region,
                      "filters":utils.jsonDecode(autostack.filters)}
                     for autostack in autostackRows]

    return utils.jsonEncode(autostackList)
  def testPOSTAutostackMetricsNoMinMax(
        self,
        addMetricMock,
        getMetricMock,
        adapterMock,
        *_args):

    getMetricMock.return_value = self.metric
    addMetricMock.return_value = self.metric

    response = self.app.post("/" + self.autostack.uid + "/metrics",
                             app_utils.jsonEncode([{"metric": "CPUUtilization",
                                                    "namespace": "AWS/EC2"},
                                                   {"metric": "NetworkIn",
                                                    "namespace": "AWS/EC2"}]),
                             headers=self.headers)
    self.assertEqual(response.status, 201)
    self.assertEqual(adapterMock.return_value.monitorMetric.call_count, 2)

    metricSpec = (
      adapterMock.return_value.monitorMetric
      .call_args_list[0][0][0]["metricSpec"])
    self.assertEqual(metricSpec["slaveMetric"]["metric"], "CPUUtilization")
    self.assertEqual(metricSpec["slaveMetric"]["namespace"], "AWS/EC2")

    metricSpec = (
      adapterMock.return_value.monitorMetric
      .call_args_list[1][0][0]["metricSpec"])
    self.assertEqual(metricSpec["slaveMetric"]["metric"], "NetworkIn")
    self.assertEqual(metricSpec["slaveMetric"]["namespace"], "AWS/EC2")
Example #17
0
    def setUp(self):
        self.headers = getDefaultHTTPHeaders(htm.it.app.config)
        self.app = TestApp(models_api.app.wsgifunc())
        metric = Mock(
            uid="cebe9fab-f416-4845-8dab-02d292244112",
            datasource="cloudwatch",
            description="The number of database connections in use by "
            "Amazon RDS database",
            server="htm-itdb2",
            location="us-east-1",
            parameters=app_utils.jsonEncode({
                "region": "us-east-1",
                "DBInstanceIdentifier": "htm-itdb2"
            }),
            status=1,
            message=None,
            collector_error=None,
            last_timestamp="2013-08-15 21:25:00",
            poll_interval=60,
            tag_name=None,
            model_params=None,
            last_rowid=20277)

        metric.name = "AWS/RDS/DatabaseConnections"
        self.metric = metric
Example #18
0
  def GET(self): # pylint: disable=R0201
    """
    Describe Cloudwatch datasource, listing all supported regions, namespaces
    and metrics

      ::

          GET /_metrics/cloudwatch

      Returns:

      ::

        {
            'regions': { 'region-name": 'region-description',...},
            'namespaces': {
                'namespace-name': {
                    'metrics': ['metric-name',...],
                    'dimensions': ['dimension-name',...]
                }, ....
            }
        }
    """
    web.header('Content-Type', 'application/json; charset=UTF-8', True)
    adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter()
    resources = adapter.describeSupportedMetrics()

    return utils.jsonEncode(
      {"regions": dict(adapter.describeRegions()),
       "namespaces": _translateResourcesIntoNamespaces(resources)})
Example #19
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": "groksolutions-com-ssl"
                },

            },
            ...
        ]
    """
        with web.ctx.connFactory() as conn:
            instances = repository.getInstances(conn)

        self.addStandardHeaders()
        return utils.jsonEncode(instances)
Example #20
0
  def testModelExportHandlerGETAutostack(self,
                                         getMetricMock,
                                         createDatasourceAdapterMock,
                                         _engineMock):
    metric = Mock(uid="1f76ba824fe147c8a4bc1c59565d8490",
                  datasource="cloudwatch",
                  description="CPUUtilization on EC2 instance i-ab15a19d in "
                              "us-west-2 region",
                  server="i-ab15a19d",
                  location="us-west-2",
                  parameters=app_utils.jsonEncode({"name": "test1",
                                                   "filters": {
                                                     "tag:Name": ["*d*"]
                                                   }}),
                  last_timestamp=datetime.datetime(2013, 11, 23, 18, 36),
                  poll_interval=300L,
                  status=1L,
                  message=None,
                  tag_name="jenkins-master",
                  model_params={},
                  last_rowid=1440L)
    metric.name = "AWS/EC2/CPUUtilization"

    getMetricMock.return_value = metric
    createDatasourceAdapterMock.return_value.exportModel.return_value = (
      metric.parameters)

    response = self.app.get("/1f76ba824fe147c8a4bc1c59565d8490/export",
      headers=self.headers)

    self.assertEqual(response.status, 200)

    self.assertEqual([metric.parameters],
                     json.loads(response.body))
Example #21
0
  def GET(self):
    """
    Get model data stats

    ::

        GET /_models/data/stats

    Returns:

    ::

        {
            "processing_time_remaining": 37
        }
    """
    with repository.engineFactory().connect() as conn:
      unprocessedDataCount = repository.getUnprocessedModelDataCount(conn)
    processingTimeRemaining = int(math.ceil(
        unprocessedDataCount * _PROCESSING_TIME_PER_RECORD))

    self.addStandardHeaders()
    return utils.jsonEncode({
        "processing_time_remaining": processingTimeRemaining,
    })
Example #22
0
    def GET(self):
        """
    Get model data stats

    ::

        GET /_models/data/stats

    Returns:

    ::

        {
            "processing_time_remaining": 37
        }
    """
        with repository.engineFactory().connect() as conn:
            unprocessedDataCount = repository.getUnprocessedModelDataCount(
                conn)
        processingTimeRemaining = int(
            math.ceil(unprocessedDataCount * _PROCESSING_TIME_PER_RECORD))

        self.addStandardHeaders()
        return utils.jsonEncode({
            "processing_time_remaining":
            processingTimeRemaining,
        })
Example #23
0
 def testCreateModelWithEmptyInstanceIdArg(self):
   """
     Test for the empty dimension field in json.
   """
   data = utils.jsonEncode(self.modelsTestData["create_empty_instanceid_data"])
   response = self.app.put("/", data, status="*", headers=self.headers)
   assertions.assertInvalidArgumentsError(self, response, "json")
    def testPUTNotificationSettingsUpdate(self, repositoryMock, engineMock):
        """ Test PUT notification settings (update)
    """
        repositoryMock.getDeviceNotificationSettings = Mock(
            return_value=self.settings)

        update = {
            "windowsize": 3601,
            "sensitivity": 0.999999,
            "email_addr": "*****@*****.**"
        }

        response = self.app.put("/%s/settings" % self.notification["device"],
                                app_utils.jsonEncode(update),
                                headers=self.headers)

        self.assertEqual(response.status, 204)
        self.assertFalse(response.body)
        repositoryMock.updateDeviceNotificationSettings.assert_called_with(
            engineMock.return_value.connect.return_value.__enter__.
            return_value, self.notification["device"], {
                "windowsize": 3601,
                "sensitivity": 0.999999,
                "email_addr": "*****@*****.**"
            })
Example #25
0
  def testPUTNotificationSettingsCreate(self, repositoryMock, engineMock):
    """ Test PUT notification settings (create)
    """

    repositoryMock.getDeviceNotificationSettings.side_effect = (
      ObjectNotFoundError("No settings yet"))

    update = {
      "windowsize": 3601,
      "sensitivity": 0.999999,
      "email_addr": "*****@*****.**"}



    response = self.app.put("/%s/settings" %
                            self.notification["device"],
                            app_utils.jsonEncode(update),
                            headers=self.headers)

    self.assertEqual(response.status, 201)
    self.assertFalse(response.body)
    self.assertTrue(repositoryMock.getDeviceNotificationSettings.called)
    repositoryMock.addDeviceNotificationSettings.assert_called_with(
      engineMock.return_value.connect.return_value.__enter__.return_value,
      self.notification["device"],
      update["windowsize"],
      update["sensitivity"],
      update["email_addr"])
Example #26
0
  def GET(self): # pylint: disable=C0103
    """
      Get list of autostacks

      Example request::

        GET /_autostacks

      Example response::

        [
          {
            "name": {name}
            "region": {region}
            "filters": {
              "tag:{Name}": ["{value}", "{value}", ...],
              "tag:{Description}": ["{value}", "{value}", ...],
              "tag:{etc}": ["{value}", "{value}", ...]
            },
            "uid": {uid}
          }
          ...
        ]

    """
    self.addStandardHeaders()
    with web.ctx.connFactory() as conn:
      autostackRows = repository.getAutostackList(conn)
    autostackList = [{"uid":autostack.uid,
                      "name":autostack.name,
                      "region":autostack.region,
                      "filters":utils.jsonDecode(autostack.filters)}
                     for autostack in autostackRows]

    return utils.jsonEncode(autostackList)
Example #27
0
    def deleteModel(metricId):
        try:
            with web.ctx.connFactory() as conn:
                metricRow = repository.getMetric(conn, metricId)
        except app_exceptions.ObjectNotFoundError:
            raise web.notfound(
                "ObjectNotFoundError Metric not found: Metric ID: %s" %
                metricId)

        if metricRow.datasource == "autostack":
            raise NotAllowedResponse({
                "result": ("Not a standalone model=%s; datasource=%s. Unable"
                           " to DELETE from this endpoint") % (
                               metricId,
                               metricRow.datasource,
                           )
            })

        log.debug("Deleting model for %s metric=%s", metricRow.datasource,
                  metricId)

        with web.ctx.connFactory() as conn:
            repository.deleteModel(conn, metricId)

        # NOTE: this is the new way using datasource adapters
        try:
            createDatasourceAdapter(
                metricRow.datasource).unmonitorMetric(metricId)
        except app_exceptions.ObjectNotFoundError:
            raise web.notfound(
                "ObjectNotFoundError Metric not found: Metric ID: %s" %
                (metricId, ))

        return utils.jsonEncode({'result': 'success'})
Example #28
0
 def testCreateModelWithEmptyMetricArg(self):
   """
     Test for the missing empty metric field in json.
   """
   data = utils.jsonEncode(self.modelsTestData["create_empty_metric_data"])
   response = self.app.put("/", data, status="*", headers=self.headers)
   assertions.assertInvalidArgumentsError(self, response, "json")
    def testGetAnnotations(self, getAnnotations, _):
        """
      Test Get Annotation

      Request::

        GET /_annotations?device={device}&user={user}&server={server}
                          &from={from}&to={to}

    Response::

      HTTP 200 Ok
      [
        {
           "uid": "2a123bb1dd4d46e7a806d62efc29cbb9",
           "device", "1231AC32FE",
           "created":"2013-08-27 16:46:51",
           "timestamp":"2013-08-27 16:45:00",
           "user":"******",
           "server":" AWS/EC2/i-53f52b67",
           "message":" The CPU Utilization was high ...",
           "data": { Optional JSON Object }
         },
        ...
      ]
    """
        getAnnotations.return_value = [self.annotation]

        response = self.app.get("?%s=%s" % ("server", "dummy"),
                                headers=self.headers)
        self.assertEqual(response.status, 200)
        actual = json.loads(response.body)
        expected = json.loads(app_utils.jsonEncode(self.annotation))

        self.assertDictEqual(expected, actual[0])
Example #30
0
  def deleteModel(metricId):
    try:
      with web.ctx.connFactory() as conn:
        metricRow = repository.getMetric(conn, metricId)
    except app_exceptions.ObjectNotFoundError:
      raise web.notfound("ObjectNotFoundError Metric not found: Metric ID: %s"
                         % metricId)

    if metricRow.datasource == "autostack":
      raise NotAllowedResponse(
        {"result":
          ("Not a standalone model=%s; datasource=%s. Unable"
           " to DELETE from this endpoint")
          % (metricId, metricRow.datasource,)
        })

    log.debug("Deleting model for %s metric=%s", metricRow.datasource,
              metricId)

    with web.ctx.connFactory() as conn:
      repository.deleteModel(conn, metricId)

    # NOTE: this is the new way using datasource adapters
    try:
      createDatasourceAdapter(metricRow.datasource).unmonitorMetric(metricId)
    except app_exceptions.ObjectNotFoundError:
      raise web.notfound(
        "ObjectNotFoundError Metric not found: Metric ID: %s" % (metricId,))

    return utils.jsonEncode({'result': 'success'})
  def GET(self, deviceId, notificationId):
    """
      Get single notification

      ::

          GET /_notifications/{deviceId}/{notificationId}

      Returns:

      ::

          {
            "uid": "e78599c4-758b-4c6e-87b1-daabaccff798",
            "timestamp": "2014-02-07 16:26:44",
            "metric": "e5511f295a474037a75bc966d02b67d2",
            "acknowledged": 0,
            "seen": 0,
            "windowsize": 3600,
            "device": "9a90eaf2-6374-4230-aa96-0830c0a737fe"
          }

      :param uid: Notification ID
      :type uid: str
      :param timestamp: Notification timestamp
      :type timestamp: timestamp
      :param metric: Metric that triggered notification
      :type metric: str
      :param acknowledged: Acknowledged status
      :type acknowledged: int
      :param seen: Seen status
      :type seen: int
      :param windowsize: Notification window in seconds during which no other
        notifications for a given instance should be sent to a given device
      :type windowsize: int
      :param device: Device ID
      :type device: str
    """
    # Update the last_timestamp for the notification device.
    try:
      with web.ctx.connFactory() as conn:
        repository.updateNotificationDeviceTimestamp(conn, deviceId)
    except ObjectNotFoundError:
      return web.notfound("Notification device not found: %s" % deviceId)

    try:
      with web.ctx.connFactory() as conn:
        notificationRow = repository.getNotification(conn,
                                                     notificationId,
                                                     NotificationHandler.fields)
      notificationDict = dict([(col, notificationRow[col])
                              for col in NotificationHandler.fields])

      if notificationRow["device"] != deviceId:
        return web.notfound("Device not found: %s" % deviceId)

      self.addStandardHeaders()
      return utils.jsonEncode(notificationDict)
    except ObjectNotFoundError:
      return web.notfound("Notification not found: %s" % notificationId)
Example #32
0
    def GET(self, autostackId, *args):  # pylint: disable=C0103,W0613
        """
      Get Metrics associated with autostack

      ::

          GET /_autostacks/{autostackId}/metrics

      NOTE: args is ignored.  Function signature for all method handlers must
      be compatible with the regexp pattern that matches.  POST optionally
      takes a second argument, DELETE requires it.
    """
        try:
            self.addStandardHeaders()
            engine = repository.engineFactory()
            metricRows = repository.getAutostackMetrics(
                engine, autostackId, getMetricDisplayFields(engine))
            metricsList = [
                convertMetricRowToMetricDict(metricRow)
                for metricRow in metricRows
            ]

            return utils.jsonEncode(metricsList)

        except ObjectNotFoundError:
            raise web.notfound("Autostack not found: Autostack ID: %s" %
                               autostackId)
        except web.HTTPError 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:
            raise web.internalerror(str(ex) or repr(ex))
    def testGETAnnotationById(self, getAnnotationById, _):
        """
      Test Get Annotation

      Request::

        GET /_annotations/{uid}

    Response::

      HTTP 200 Ok
      [
        {
           "uid": "2a123bb1dd4d46e7a806d62efc29cbb9",
           "device", "1231AC32FE",
           "created":"2013-08-27 16:46:51",
           "timestamp":"2013-08-27 16:45:00",
           "user":"******",
           "server":" AWS/EC2/i-53f52b67",
           "message":" The CPU Utilization was high ...",
           "data": { Optional JSON Object }
         }
      ]
    """
        getAnnotationById.return_value = self.annotation

        response = self.app.get("/%s" % self.annotation["uid"],
                                headers=self.headers)
        self.assertEqual(response.status, 200)
        actual = json.loads(response.body)
        expected = json.loads(app_utils.jsonEncode(self.annotation))

        self.assertDictEqual(expected, actual[0])
    def testPOSTAutostacks(self, adapterMock, quotaRepositoryMock,
                           _repositoryMock):
        name = "Test"
        region = "Bogus"
        filters = {"tag:Name": ["Bogus"]}

        quotaRepositoryMock.getInstanceCount.return_value = 0

        autostackMock = Mock()
        autostackMock.items.return_value = [("name", name), ("region", region),
                                            ("filters", filters)]
        adapterMock.return_value.createAutostack.return_value = autostackMock

        response = self.app.post("/",
                                 app_utils.jsonEncode({
                                     "name": name,
                                     "region": region,
                                     "filters": filters
                                 }),
                                 headers=self.headers)

        self.assertEqual(response.status, 201)
        result = json.loads(response.body)
        self.assertEqual(result["name"], "Test")

        self.assertTrue(adapterMock.return_value.createAutostack.called)
Example #35
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": "htm-itsolutions-com-ssl"
                },

            },
            ...
        ]
    """
    with web.ctx.connFactory() as conn:
      instances = repository.getInstances(conn)

    self.addStandardHeaders()
    return utils.jsonEncode(instances)
Example #36
0
 def testCreateModelWithInvalidDatasourceArg(self):
   """
     Test for the invalid metric field in json.
   """
   data = utils.jsonEncode(self.modelsTestData["create_invalid_ds_data"])
   response = self.app.put("/", data, status="*", headers=self.headers)
   assertions.assertInvalidArgumentsError(self, response, "json")
Example #37
0
    def GET(self):  # pylint: disable=R0201
        """
    Describe Cloudwatch datasource, listing all supported regions, namespaces
    and metrics

      ::

          GET /_metrics/cloudwatch

      Returns:

      ::

        {
            'regions': { 'region-name": 'region-description',...},
            'namespaces': {
                'namespace-name': {
                    'metrics': ['metric-name',...],
                    'dimensions': ['dimension-name',...]
                }, ....
            }
        }
    """
        web.header('Content-Type', 'application/json; charset=UTF-8', True)
        adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter(
        )
        resources = adapter.describeSupportedMetrics()

        return utils.jsonEncode({
            "regions":
            dict(adapter.describeRegions()),
            "namespaces":
            _translateResourcesIntoNamespaces(resources)
        })
Example #38
0
 def testCreateModelWithInvalidRegionKey(self):
   """
     Test for the invalid region field in json.
   """
   data = utils.jsonEncode(self.modelsTestData["create_invalid_region_key"])
   response = self.app.put("/", data, status="*", headers=self.headers)
   assertions.assertInvalidArgumentsError(self, response, "json")
    def testPOSTAutostackMetricsNoMinMax(self, addMetricMock, getMetricMock,
                                         adapterMock, *_args):

        getMetricMock.return_value = self.metric
        addMetricMock.return_value = self.metric

        response = self.app.post("/" + self.autostack.uid + "/metrics",
                                 app_utils.jsonEncode([{
                                     "metric": "CPUUtilization",
                                     "namespace": "AWS/EC2"
                                 }, {
                                     "metric": "NetworkIn",
                                     "namespace": "AWS/EC2"
                                 }]),
                                 headers=self.headers)
        self.assertEqual(response.status, 201)
        self.assertEqual(adapterMock.return_value.monitorMetric.call_count, 2)

        metricSpec = (adapterMock.return_value.monitorMetric.call_args_list[0]
                      [0][0]["metricSpec"])
        self.assertEqual(metricSpec["slaveMetric"]["metric"], "CPUUtilization")
        self.assertEqual(metricSpec["slaveMetric"]["namespace"], "AWS/EC2")

        metricSpec = (adapterMock.return_value.monitorMetric.call_args_list[1]
                      [0][0]["metricSpec"])
        self.assertEqual(metricSpec["slaveMetric"]["metric"], "NetworkIn")
        self.assertEqual(metricSpec["slaveMetric"]["namespace"], "AWS/EC2")
Example #40
0
    def GET(self):  # pylint: disable=R0201
        """
      Returns list of supported Clouwdwatch regions

      ::

          GET /_metrics/cloudwatch/regions

      Returns:

      ::

          { 'region-name': 'region-description',...}

      Sample output:

      ::

          {
            "ap-northeast-1": "Asia Pacific (Tokyo) Region",
            "ap-southeast-1": "Asia Pacific (Singapore) Region",
            "ap-southeast-2": "Asia Pacific (Sydney) Region",
            "eu-west-1": "EU (Ireland) Region",
            "sa-east-1": "South America (Sao Paulo) Region",
            "us-east-1": "US East (Northern Virginia) Region",
            "us-west-1": "US West (Northern California) Region",
            "us-west-2": "US West (Oregon) Region"
          }
    """
        adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter(
        )
        AuthenticatedBaseHandler.addStandardHeaders()
        return utils.jsonEncode(dict(adapter.describeRegions()))
Example #41
0
  def testGETAnnotationById(self, getAnnotationById, _):
    """
      Test Get Annotation

      Request::

        GET /_annotations/{uid}

    Response::

      HTTP 200 Ok
      [
        {
           "uid": "2a123bb1dd4d46e7a806d62efc29cbb9",
           "device", "1231AC32FE",
           "created":"2013-08-27 16:46:51",
           "timestamp":"2013-08-27 16:45:00",
           "user":"******",
           "server":" AWS/EC2/i-53f52b67",
           "message":" The CPU Utilization was high ...",
           "data": { Optional JSON Object }
         }
      ]
    """
    getAnnotationById.return_value = self.annotation

    response = self.app.get("/%s" % self.annotation["uid"],
                            headers=self.headers)
    self.assertEqual(response.status, 200)
    actual = json.loads(response.body)
    expected = json.loads(app_utils.jsonEncode(self.annotation))

    self.assertDictEqual(expected, actual[0])
Example #42
0
  def GET(self, autostackId, *args): # pylint: disable=C0103,W0613
    """
      Get Metrics associated with autostack

      ::

          GET /_autostacks/{autostackId}/metrics

      NOTE: args is ignored.  Function signature for all method handlers must
      be compatible with the regexp pattern that matches.  POST optionally
      takes a second argument, DELETE requires it.
    """
    try:
      self.addStandardHeaders()
      engine = repository.engineFactory()
      metricRows = repository.getAutostackMetrics(engine,
                                                  autostackId,
                                                  getMetricDisplayFields(engine))
      metricsList = [convertMetricRowToMetricDict(metricRow)
                     for metricRow in metricRows]

      return utils.jsonEncode(metricsList)

    except ObjectNotFoundError:
      raise web.notfound("Autostack not found: Autostack ID: %s" % autostackId)
    except web.HTTPError 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:
      raise web.internalerror(str(ex) or repr(ex))
 def testDeleteInstancesHandlerEmptyData(self, listMetricIDsMock, _engineFactoryMock):
     """
 Test for Delete "/_instances" with with empty input data
 response is validated for appropriate headers, body and status
 """
     params = []
     response = self.app.delete("", params=app_utils.jsonEncode(params), headers=self.headers, status="*")
     assertions.assertBadRequest(self, response, "json")
     self.assertFalse(listMetricIDsMock.called)
Example #44
0
    def testCreateModelWithInvalidDimensionsArg(self):
        """
      Test for the invalid dimension field in json.
    """
        data = utils.jsonEncode(self.modelsTestData["create_invalid_dimension_data"])
        response = self.app.put("/", data, status="*", headers=self.headers)
        assertions.assertBadRequest(self, response, "json")

        response = self.app.get("/", headers=self.headers)
        self.assertFalse(json.loads(response.body), "Model actually created with invalid dimension")
 def testPOSTSectionInvalid(self):
   """
   Test for POST for '/_settings', Set some invalid setting
   resoponse is validated for appropriate headers and body
   """
   with self.assertRaises(AppError) as e:
     self.app.post("/foo", app_utils.jsonEncode(
       {"aws_access_key_id" : "dummy_aws_key2"}), headers=self.headers)
   self.assertIn("Bad response: 400 Bad Request (not 200 OK or 3xx redirect"
   " for /foo)\nFailed to update configuration settings", str(e.exception))
 def testPostMultipleWithInstanceToIncorrectRegion(self):
   """
   Test for post '/_instances'
   response is validated for appropriate headers, body and status
   invoke post with valid instance id to incorrect region
   """
   params = [VALID_EC2_INSTANCES["jenkins-master"]]
   response = self.app.post("/us-east-1/AWS/EC2",
     params=app_utils.jsonEncode(params), headers=self.headers, status="*")
   assertions.assertBadRequest(self, response, "json")
   self.assertIn("InvalidArgumentsError", response.body)
Example #47
0
    def GET(self, region):
        """
      List all existing Cloudwatch metrics for a given region

      ::

          GET /_metrics/cloudwatch/regions/{region}

      Returns:

      ::

          [
              {
                  'name': 'tag-or-empty-string',
                  'region': 'region-name',
                  'namespace': 'namespace-name',
                  'datasource': 'cloudwatch',
                  'identifier': 'id-from-dimension',
                  'metric': 'metric-name',
                  'dimensions': {
                      ...
                  }
              },...
          ]
    """

        adapter = datasource_adapter_factory.createCloudwatchDatasourceAdapter(
        )
        resources = adapter.describeSupportedMetrics()

        def translateResourcesIntoMetrics():
            for resource, metrics in resources.items():
                for specificResource in adapter.describeResources(
                        region, resource):
                    for metric, cloudwatchParams in metrics.items():
                        yield {
                            "datasource": "cloudwatch",
                            "dimensions": {
                                cloudwatchParams["dimensionGroups"][0][0]:
                                specificResource["resID"]
                            },
                            "identifier": specificResource["resID"],
                            "metric": metric,
                            "name": specificResource["name"],
                            "namespace": cloudwatchParams["namespace"],
                            "region": region
                        }

        if region not in dict(adapter.describeRegions()):
            raise web.NotFound("Region '%s' was not found" % region)

        self.addStandardHeaders()
        return utils.jsonEncode(list(translateResourcesIntoMetrics()))
Example #48
0
  def testCreateModelWithInvalidMetricArg(self):
    """
      Test for the invalid metric field in json.
    """
    data = utils.jsonEncode(self.modelsTestData["create_invalid_metric_data"])
    response = self.app.put("/", data, status="*", headers=self.headers)
    assertions.assertBadRequest(self, response, "json")

    response = self.app.get("/", headers=self.headers)
    self.assertFalse(json.loads(response.body),
                     "Model actually created with invalid metric")
 def testPostMultipleWithInstanceToIncorrectRegion(self):
   """
   Test for post '/_instances'
   response is validated for appropriate headers, body and status
   invoke post with valid instance id to incorrect region
   """
   params = [VALID_EC2_INSTANCES["jenkins-master"]]
   response = self.app.post("/us-east-1/AWS/EC2",
     params=app_utils.jsonEncode(params), headers=self.headers, status="*")
   assertions.assertBadRequest(self, response, "json")
   self.assertIn("InvalidArgumentsError", response.body)
 def testDeleteMultipleWithInvalidInstanceId(self):
   """
   Test for post '/_instances'
   response is validated for appropriate headers, body and status
   invoke delete with invalid Instance id
   """
   params = ["abcd1234"]
   response = self.app.delete("", params=app_utils.jsonEncode(params),
     headers=self.headers, status="*")
   assertions.assertNotFound(self, response)
   self.assertIn("Not able to delete", response.body)
 def testPostMultipleWithEmptyData(self):
   """
   Test for post '/_instances'
   response is validated for appropriate headers, body and status
   Invoke post with empty data
   """
   params = []
   response = self.app.post("/us-west-2/AWS/EC2",
     params=app_utils.jsonEncode(params), headers=self.headers, status="*")
   assertions.assertBadRequest(self, response, "json")
   self.assertIn("InvalidArgumentsError", response.body)
 def testPostMultipleWithEmptyData(self):
   """
   Test for post '/_instances'
   response is validated for appropriate headers, body and status
   Invoke post with empty data
   """
   params = []
   response = self.app.post("/us-west-2/AWS/EC2",
     params=app_utils.jsonEncode(params), headers=self.headers, status="*")
   assertions.assertBadRequest(self, response, "json")
   self.assertIn("InvalidArgumentsError", response.body)
Example #53
0
 def testDeleteInstancesHandlerEmptyData(self, listMetricIDsMock,
                                           _engineFactoryMock):
   """
   Test for Delete "/_instances" with with empty input data
   response is validated for appropriate headers, body and status
   """
   params = []
   response = self.app.delete("", params=app_utils.jsonEncode(params),
     headers=self.headers, status="*")
   assertions.assertBadRequest(self, response, "json")
   self.assertFalse(listMetricIDsMock.called)
 def testDeleteMultipleWithInvalidInstanceId(self):
   """
   Test for post '/_instances'
   response is validated for appropriate headers, body and status
   invoke delete with invalid Instance id
   """
   params = ["abcd1234"]
   response = self.app.delete("", params=app_utils.jsonEncode(params),
     headers=self.headers, status="*")
   assertions.assertNotFound(self, response)
   self.assertIn("Not able to delete", response.body)
Example #55
0
    def _handleMetricCollectionError(self, engine, metricObj, startTime,
                                     error):
        """ Update the metric's collector_error member and promote the metric
    to ERROR state if needed

    :param engine: SQLAlchemy engine object
    :type engine: sqlalchemy.engine.Engine

    :param metricObj: Metric instance

    :param startTime: metric collection start time; unix epoch timestamp

    :param error: exception object that is the result of metric data collection
      or (possibly pending) resource status check
    """
        # Quarantine the metric temporarily
        self._metricInfoCache[metricObj.uid].quarantineEndTime = (
            startTime +
            metricObj.poll_interval * self._METRIC_QUARANTINE_DURATION_RATIO)

        try:
            if metricObj.collector_error is None:
                # TODO: unit-test
                # Begin error grace period for this metric
                deadline = time.time() + self._metricErrorGracePeriod
                with engine.connect() as conn:
                    repository.retryOnTransientErrors(
                        repository.setMetricCollectorError)(
                            conn, metricObj.uid,
                            utils.jsonEncode(
                                dict(deadline=deadline, message=repr(error))))
                self._log.error(
                    "Started error grace period on metric=<%r> through %sZ due to "
                    "error=%r", metricObj,
                    datetime.utcfromtimestamp(deadline).isoformat(), error)
            elif (time.time() > utils.jsonDecode(
                    metricObj.collector_error)["deadline"]):
                # TODO: unit-test
                # Error grace period expired: promote the metric to ERROR state
                with engine.connect() as conn:
                    repository.retryOnTransientErrors(
                        repository.setMetricStatus)(conn, metricObj.uid,
                                                    MetricStatus.ERROR,
                                                    repr(error))
                self._log.error(
                    "Metric Collector: grace period expired; placed metric=<%r> in "
                    "ERROR state due to error=%r", metricObj, error)
        except app_exceptions.ObjectNotFoundError:
            # TODO: unit-test
            self._log.warning("Metric deleted? metric=%r",
                              metricObj,
                              exc_info=True)