ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jonathanhur...@apache.org
Subject [46/50] [abbrv] AMBARI-6887. Alerts: groundwork for alert collection (ncole)
Date Wed, 20 Aug 2014 15:30:47 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/14e79ed1/ambari-agent/src/main/python/ambari_agent/apscheduler/triggers/simple.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/apscheduler/triggers/simple.py b/ambari-agent/src/main/python/ambari_agent/apscheduler/triggers/simple.py
new file mode 100644
index 0000000..ea61b3f
--- /dev/null
+++ b/ambari-agent/src/main/python/ambari_agent/apscheduler/triggers/simple.py
@@ -0,0 +1,17 @@
+from apscheduler.util import convert_to_datetime
+
+
+class SimpleTrigger(object):
+    def __init__(self, run_date):
+        self.run_date = convert_to_datetime(run_date)
+
+    def get_next_fire_time(self, start_date):
+        if self.run_date >= start_date:
+            return self.run_date
+
+    def __str__(self):
+        return 'date[%s]' % str(self.run_date)
+
+    def __repr__(self):
+        return '<%s (run_date=%s)>' % (
+            self.__class__.__name__, repr(self.run_date))

http://git-wip-us.apache.org/repos/asf/ambari/blob/14e79ed1/ambari-agent/src/main/python/ambari_agent/apscheduler/util.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/apscheduler/util.py b/ambari-agent/src/main/python/ambari_agent/apscheduler/util.py
new file mode 100644
index 0000000..dcede4c
--- /dev/null
+++ b/ambari-agent/src/main/python/ambari_agent/apscheduler/util.py
@@ -0,0 +1,230 @@
+"""
+This module contains several handy functions primarily meant for internal use.
+"""
+
+from datetime import date, datetime, timedelta
+from time import mktime
+import re
+import sys
+
+__all__ = ('asint', 'asbool', 'convert_to_datetime', 'timedelta_seconds',
+           'time_difference', 'datetime_ceil', 'combine_opts',
+           'get_callable_name', 'obj_to_ref', 'ref_to_obj', 'maybe_ref',
+           'to_unicode', 'iteritems', 'itervalues', 'xrange')
+
+
+def asint(text):
+    """
+    Safely converts a string to an integer, returning None if the string
+    is None.
+
+    :type text: str
+    :rtype: int
+    """
+    if text is not None:
+        return int(text)
+
+
+def asbool(obj):
+    """
+    Interprets an object as a boolean value.
+
+    :rtype: bool
+    """
+    if isinstance(obj, str):
+        obj = obj.strip().lower()
+        if obj in ('true', 'yes', 'on', 'y', 't', '1'):
+            return True
+        if obj in ('false', 'no', 'off', 'n', 'f', '0'):
+            return False
+        raise ValueError('Unable to interpret value "%s" as boolean' % obj)
+    return bool(obj)
+
+
+_DATE_REGEX = re.compile(
+    r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})'
+    r'(?: (?P<hour>\d{1,2}):(?P<minute>\d{1,2}):(?P<second>\d{1,2})'
+    r'(?:\.(?P<microsecond>\d{1,6}))?)?')
+
+
+def convert_to_datetime(input):
+    """
+    Converts the given object to a datetime object, if possible.
+    If an actual datetime object is passed, it is returned unmodified.
+    If the input is a string, it is parsed as a datetime.
+
+    Date strings are accepted in three different forms: date only (Y-m-d),
+    date with time (Y-m-d H:M:S) or with date+time with microseconds
+    (Y-m-d H:M:S.micro).
+
+    :rtype: datetime
+    """
+    if isinstance(input, datetime):
+        return input
+    elif isinstance(input, date):
+        return datetime.fromordinal(input.toordinal())
+    elif isinstance(input, basestring):
+        m = _DATE_REGEX.match(input)
+        if not m:
+            raise ValueError('Invalid date string')
+        values = [(k, int(v or 0)) for k, v in m.groupdict().items()]
+        values = dict(values)
+        return datetime(**values)
+    raise TypeError('Unsupported input type: %s' % type(input))
+
+
+def timedelta_seconds(delta):
+    """
+    Converts the given timedelta to seconds.
+
+    :type delta: timedelta
+    :rtype: float
+    """
+    return delta.days * 24 * 60 * 60 + delta.seconds + \
+        delta.microseconds / 1000000.0
+
+
+def time_difference(date1, date2):
+    """
+    Returns the time difference in seconds between the given two
+    datetime objects. The difference is calculated as: date1 - date2.
+
+    :param date1: the later datetime
+    :type date1: datetime
+    :param date2: the earlier datetime
+    :type date2: datetime
+    :rtype: float
+    """
+    later = mktime(date1.timetuple()) + date1.microsecond / 1000000.0
+    earlier = mktime(date2.timetuple()) + date2.microsecond / 1000000.0
+    return later - earlier
+
+
+def datetime_ceil(dateval):
+    """
+    Rounds the given datetime object upwards.
+
+    :type dateval: datetime
+    """
+    if dateval.microsecond > 0:
+        return dateval + timedelta(seconds=1,
+                                   microseconds=-dateval.microsecond)
+    return dateval
+
+
+def combine_opts(global_config, prefix, local_config={}):
+    """
+    Returns a subdictionary from keys and values of  ``global_config`` where
+    the key starts with the given prefix, combined with options from
+    local_config. The keys in the subdictionary have the prefix removed.
+
+    :type global_config: dict
+    :type prefix: str
+    :type local_config: dict
+    :rtype: dict
+    """
+    prefixlen = len(prefix)
+    subconf = {}
+    for key, value in global_config.items():
+        if key.startswith(prefix):
+            key = key[prefixlen:]
+            subconf[key] = value
+    subconf.update(local_config)
+    return subconf
+
+
+def get_callable_name(func):
+    """
+    Returns the best available display name for the given function/callable.
+    """
+    f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None)
+
+    if f_self and hasattr(func, '__name__'):
+        if isinstance(f_self, type):
+            # class method
+            clsname = getattr(f_self, '__qualname__', None) or f_self.__name__
+            return '%s.%s' % (clsname, func.__name__)
+        # bound method
+        return '%s.%s' % (f_self.__class__.__name__, func.__name__)
+
+    if hasattr(func, '__call__'):
+        if hasattr(func, '__name__'):
+            # function, unbound method or a class with a __call__ method
+            return func.__name__
+        # instance of a class with a __call__ method
+        return func.__class__.__name__
+
+    raise TypeError('Unable to determine a name for %s -- '
+                    'maybe it is not a callable?' % repr(func))
+
+
+def obj_to_ref(obj):
+    """
+    Returns the path to the given object.
+    """
+    ref = '%s:%s' % (obj.__module__, get_callable_name(obj))
+    try:
+        obj2 = ref_to_obj(ref)
+        if obj != obj2:
+            raise ValueError
+    except Exception:
+        raise ValueError('Cannot determine the reference to %s' % repr(obj))
+
+    return ref
+
+
+def ref_to_obj(ref):
+    """
+    Returns the object pointed to by ``ref``.
+    """
+    if not isinstance(ref, basestring):
+        raise TypeError('References must be strings')
+    if not ':' in ref:
+        raise ValueError('Invalid reference')
+
+    modulename, rest = ref.split(':', 1)
+    try:
+        obj = __import__(modulename)
+    except ImportError:
+        raise LookupError('Error resolving reference %s: '
+                          'could not import module' % ref)
+
+    try:
+        for name in modulename.split('.')[1:] + rest.split('.'):
+            obj = getattr(obj, name)
+        return obj
+    except Exception:
+        raise LookupError('Error resolving reference %s: '
+                          'error looking up object' % ref)
+
+
+def maybe_ref(ref):
+    """
+    Returns the object that the given reference points to, if it is indeed
+    a reference. If it is not a reference, the object is returned as-is.
+    """
+    if not isinstance(ref, str):
+        return ref
+    return ref_to_obj(ref)
+
+
+def to_unicode(string, encoding='ascii'):
+    """
+    Safely converts a string to a unicode representation on any
+    Python version.
+    """
+    if hasattr(string, 'decode'):
+        return string.decode(encoding, 'ignore')
+    return string  # pragma: nocover
+
+
+if sys.version_info < (3, 0):  # pragma: nocover
+    iteritems = lambda d: d.iteritems()
+    itervalues = lambda d: d.itervalues()
+    xrange = xrange
+    basestring = basestring
+else:  # pragma: nocover
+    iteritems = lambda d: d.items()
+    itervalues = lambda d: d.values()
+    xrange = range
+    basestring = str

http://git-wip-us.apache.org/repos/asf/ambari/blob/14e79ed1/ambari-agent/src/test/python/ambari_agent/TestAlerts.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/ambari_agent/TestAlerts.py b/ambari-agent/src/test/python/ambari_agent/TestAlerts.py
new file mode 100644
index 0000000..51c3af9
--- /dev/null
+++ b/ambari-agent/src/test/python/ambari_agent/TestAlerts.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+
+'''
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+'''
+
+import os
+import sys
+from ambari_agent.AlertSchedulerHandler import AlertSchedulerHandler
+from ambari_agent.apscheduler.scheduler import Scheduler
+from ambari_agent.alerts.port_alert import PortAlert
+from mock.mock import patch
+from unittest import TestCase
+
+class TestAlerts(TestCase):
+
+  def setUp(self):
+    pass
+
+  def tearDown(self):
+    sys.stdout == sys.__stdout__
+
+  @patch.object(Scheduler, "add_interval_job")
+  def test_build(self, aps_add_interval_job_mock):
+    test_file_path = os.path.join('ambari_agent', 'dummy_files', 'alert_definitions.json')
+
+    ash = AlertSchedulerHandler(test_file_path)
+
+    self.assertTrue(aps_add_interval_job_mock.called)
+
+  def test_port_alert(self):
+    json = { "name": "namenode_process",
+      "service": "HDFS",
+      "component": "NAMENODE",
+      "label": "NameNode process",
+      "interval": 6,
+      "scope": "host",
+      "source": {
+        "type": "PORT",
+        "uri": "http://c6401.ambari.apache.org:50070",
+        "default_port": 50070,
+        "reporting": {
+          "ok": {
+            "text": "TCP OK - {0:.4f} response time on port {1}"
+          },
+          "critical": {
+            "text": "Could not load process info: {0}"
+          }
+        }
+      }
+    }
+
+    pa = PortAlert(json, json['source'])
+    self.assertEquals(6, pa.interval())
+
+    res = pa.collect()
+    
+    pass
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/14e79ed1/ambari-agent/src/test/python/ambari_agent/dummy_files/alert_definitions.json
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/ambari_agent/dummy_files/alert_definitions.json
b/ambari-agent/src/test/python/ambari_agent/dummy_files/alert_definitions.json
new file mode 100644
index 0000000..6c55966
--- /dev/null
+++ b/ambari-agent/src/test/python/ambari_agent/dummy_files/alert_definitions.json
@@ -0,0 +1,46 @@
+{
+  "c1": [
+    {
+      "name": "namenode_cpu",
+      "label": "NameNode host CPU Utilization",
+      "scope": "host",
+      "source": {
+        "type": "METRIC",
+        "jmx": "java.lang:type=OperatingSystem/SystemCpuLoad",
+        "host": "{{hdfs-site/dfs.namenode.secondary.http-address}}"
+      }
+    },
+    {
+      "name": "namenode_process",
+      "service": "HDFS",
+      "component": "NAMENODE",
+      "label": "NameNode process",
+      "interval": 6,
+      "scope": "host",
+      "source": {
+        "type": "PORT",
+        "uri": "http://c6401.ambari.apache.org:50070",
+        "default_port": 50070,
+        "reporting": {
+          "ok": {
+            "text": "TCP OK - {0:.4f} response time on port {1}"
+          },
+          "critical": {
+            "text": "Could not load process info: {0}"
+          }
+        }
+      }
+    },
+    {
+      "name": "hdfs_last_checkpoint",
+      "label": "Last Checkpoint Time",
+      "interval": 1,
+      "scope": "service",
+      "enabled": false,
+      "source": {
+        "type": "SCRIPT",
+        "path": "scripts/alerts/last_checkpoint.py"
+      }
+    }
+  ]
+}


Mime
View raw message