From d74e011d99173eae2b4c57a660d627029980dcb0 Mon Sep 17 00:00:00 2001 From: Arthur Wang Date: Tue, 30 Jun 2015 17:33:57 -0400 Subject: [PATCH 1/4] Raise AppError for 403 API responses Raising an AppError instead of an HTTPError for 403 responses also exposes the informational error message in the response. --- datadog/api/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog/api/base.py b/datadog/api/base.py index 74f067743..1016b3cc1 100644 --- a/datadog/api/base.py +++ b/datadog/api/base.py @@ -121,7 +121,7 @@ def request(cls, method, path, body=None, attach_host_name=False, response_forma cls._timeout_counter += 1 raise HttpTimeout('%s %s timed out after %d seconds.' % (method, url, _timeout)) except requests.exceptions.HTTPError as e: - if e.response.status_code == 404 or e.response.status_code == 400: + if e.response.status_code in (400, 403, 404): pass else: raise From d9d104cba818ac1c1d9445ec1d089e27b96ff7be Mon Sep 17 00:00:00 2001 From: Arthur Wang Date: Tue, 30 Jun 2015 17:35:01 -0400 Subject: [PATCH 2/4] Clarify monitors mute_all command Command does not add or clear mute settings for individual monitors. Rather, `mute_all` and `unmute_all` enable/disable a global setting (also known as downtime). --- datadog/api/monitors.py | 7 +++++-- datadog/dogshell/monitor.py | 12 +++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/datadog/api/monitors.py b/datadog/api/monitors.py index 514467363..5c4487905 100644 --- a/datadog/api/monitors.py +++ b/datadog/api/monitors.py @@ -72,6 +72,9 @@ def unmute(cls, id, **params): :param scope: scope to apply the unmute :type scope: string + :param all_scopes: if True, clears mute settings for all scopes + :type all_scopes: boolean + :returns: JSON response from HTTP request """ return super(Monitor, cls)._trigger_class_action('POST', 'unmute', id, **params) @@ -79,7 +82,7 @@ def unmute(cls, id, **params): @classmethod def mute_all(cls): """ - Mute all monitors. + Globally mute monitors. :returns: JSON response from HTTP request """ @@ -88,7 +91,7 @@ def mute_all(cls): @classmethod def unmute_all(cls): """ - Unmute all monitors. + Cancel global monitor mute setting (does not remove mute settings for individual monitors). :returns: JSON response from HTTP request """ diff --git a/datadog/dogshell/monitor.py b/datadog/dogshell/monitor.py index 5f4ef9b1a..07e829043 100644 --- a/datadog/dogshell/monitor.py +++ b/datadog/dogshell/monitor.py @@ -46,23 +46,25 @@ def setup_parser(cls, subparsers): delete_parser.add_argument('monitor_id', help="monitor to delete") delete_parser.set_defaults(func=cls._delete) - mute_all_parser = verb_parsers.add_parser('mute_all', help="Mute all monitors") + mute_all_parser = verb_parsers.add_parser('mute_all', help="Globally mute " + "monitors (downtime over *)") mute_all_parser.set_defaults(func=cls._mute_all) - unmute_all_parser = verb_parsers.add_parser('unmute_all', help="Unmute all monitors") + unmute_all_parser = verb_parsers.add_parser('unmute_all', help="Globally unmute " + "monitors (cancel downtime over *)") unmute_all_parser.set_defaults(func=cls._unmute_all) mute_parser = verb_parsers.add_parser('mute', help="Mute a monitor") mute_parser.add_argument('monitor_id', help="monitor to mute") mute_parser.add_argument('--scope', help="scope to apply the mute to," - " e.g. role:db", default=[]) + " e.g. role:db (optional)", default=[]) mute_parser.add_argument('--end', help="POSIX timestamp for when" - " the mute should end", default=None) + " the mute should end (optional)", default=None) mute_parser.set_defaults(func=cls._mute) unmute_parser = verb_parsers.add_parser('unmute', help="Unmute a monitor") unmute_parser.add_argument('monitor_id', help="monitor to unmute") - unmute_parser.add_argument('--scope', help="scope to apply the mute to, " + unmute_parser.add_argument('--scope', help="scope to unmute (must be muted), " "e.g. role:db", default=[]) unmute_parser.set_defaults(func=cls._unmute) From 487a86f98aec2aead575345b868bb4a956a84a89 Mon Sep 17 00:00:00 2001 From: Arthur Wang Date: Tue, 30 Jun 2015 17:36:02 -0400 Subject: [PATCH 3/4] Add all_scopes arg to monitor unmuting This argument allows users to clear all muted scopes of a monitor, instead of unmuting individual scopes one-by-one. --- datadog/dogshell/monitor.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/datadog/dogshell/monitor.py b/datadog/dogshell/monitor.py index 07e829043..f2b9d8ff3 100644 --- a/datadog/dogshell/monitor.py +++ b/datadog/dogshell/monitor.py @@ -66,6 +66,8 @@ def setup_parser(cls, subparsers): unmute_parser.add_argument('monitor_id', help="monitor to unmute") unmute_parser.add_argument('--scope', help="scope to unmute (must be muted), " "e.g. role:db", default=[]) + unmute_parser.add_argument('--all_scopes', help="clear muting across all scopes", + action='store_true') unmute_parser.set_defaults(func=cls._unmute) @classmethod @@ -196,8 +198,10 @@ def _mute(cls, args): @classmethod def _unmute(cls, args): api._timeout = args.timeout - # TODO CHECK - res = api.Monitor.unmute(args.monitor_id, scope=args.scope) - if res is not None: - report_warnings(res) - report_errors(res) + res = api.Monitor.unmute(args.monitor_id, scope=args.scope, all_scopes=args.all_scopes) + report_warnings(res) + report_errors(res) + if format == 'pretty': + print(cls._pretty_json(res)) + else: + print(json.dumps(res)) From a124a3578271088449896cd786d57cfedfe12abd Mon Sep 17 00:00:00 2001 From: Arthur Wang Date: Tue, 30 Jun 2015 17:36:27 -0400 Subject: [PATCH 4/4] Update monitor muting tests - Add test for unmuting all scopes - Update outdated mute tests --- tests/integration/api/test_api.py | 25 ++++++++++++----- tests/integration/dogshell/test_dogshell.py | 31 ++++++++++++++++++--- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/tests/integration/api/test_api.py b/tests/integration/api/test_api.py index 9c02ced82..2842d2807 100644 --- a/tests/integration/api/test_api.py +++ b/tests/integration/api/test_api.py @@ -356,10 +356,8 @@ def test_type_check(self): dog.Metric.send(metric="test.metric", points=1.0) dog.Metric.send(metric="test.metric", points=(time.time(), 1.0)) + @attr('monitor') def test_monitors(self): - # Stop testing 'silenced' parameter - # Depending on the enabled flag (i.e. API release), 'silenced' can be a boolean or a dictionnary - query = "avg(last_1h):sum:system.net.bytes_rcvd{host:host0} > 100" monitor_id = dog.Monitor.create(query=query, type="metric alert")['id'] @@ -370,13 +368,15 @@ def test_monitors(self): options = { "notify_no_data": True, - "no_data_timeframe": 20 + "no_data_timeframe": 20, + "silenced": {"*": None} } - dog.Monitor.update(monitor_id, query=query, silenced=True, options=options, timeout_h=1) + dog.Monitor.update(monitor_id, query=query, options=options, timeout_h=1) monitor = dog.Monitor.get(monitor_id) assert monitor['query'] == query, monitor['query'] assert monitor['options']['notify_no_data'] == True, monitor['options']['notify_no_data'] - assert monitor['options']['no_data_timeframe'] == 20, monitor['options']['notify_no_data'] + assert monitor['options']['no_data_timeframe'] == 20, monitor['options']['no_data_timeframe'] + assert monitor['options']['silenced'] == {"*": None}, monitor['options']['silenced'] dog.Monitor.delete(monitor_id) try: @@ -667,6 +667,17 @@ def test_monitor_muting(self): monitor = dog.Monitor.get(monitor_id) eq(monitor['options']['silenced'], {}) + options = { + "silenced": {"host:abcd1234": None, "host:abcd1235": None} + } + dog.Monitor.update(monitor_id, query=query, options=options) + monitor = dog.Monitor.get(monitor_id) + eq(monitor['options']['silenced'], options['silenced']) + + dog.Monitor.unmute(monitor_id, all_scopes=True) + monitor = dog.Monitor.get(monitor_id) + eq(monitor['options']['silenced'], {}) + dog.Monitor.delete(monitor_id) @attr('monitor') @@ -731,7 +742,7 @@ def test_host_muting(self): eq(mute['action'], "Muted") eq(mute['end'], end2) - unmute = dog.Host.unmute(hostname) + dog.Host.unmute(hostname) if __name__ == '__main__': unittest.main() diff --git a/tests/integration/dogshell/test_dogshell.py b/tests/integration/dogshell/test_dogshell.py index 1039be37a..f1cf3baa2 100644 --- a/tests/integration/dogshell/test_dogshell.py +++ b/tests/integration/dogshell/test_dogshell.py @@ -350,7 +350,7 @@ def test_screenboards(self): def test_monitors(self): # Create a monitor - query = "avg(last_1h):sum:system.net.bytes_rcvd{host:host0} > 100" + query = "avg(last_1h):sum:system.net.bytes_rcvd{*} by {host} > 100" type_alert = "metric alert" out, err, return_code = self.dogshell(["monitor", "post", type_alert, query]) @@ -389,12 +389,35 @@ def test_monitors(self): assert "id" in out, out out = json.loads(out) self.assertEquals(str(out["id"]), monitor_id) + self.assertEquals(out["options"]["silenced"], {"*": None}) # Unmute monitor - self.dogshell(["monitor", "unmute", monitor_id]) out, err, return_code = self.dogshell(["monitor", "unmute", monitor_id], check_return_code=False) - self.assertEquals(out, '') - self.assertEquals(return_code, 1) + out = json.loads(out) + self.assertEquals(str(out["id"]), monitor_id) + self.assertEquals(out["options"]["silenced"], {}) + + # Unmute all scopes of a monitor + options = { + "silenced": {"host:abcd1234": None, "host:abcd1235": None} + } + + out, err, return_code = self.dogshell( + ["monitor", "update", monitor_id, type_alert, + query, "--options", json.dumps(options)]) + + assert "id" in out, out + assert "options" in out, out + out = json.loads(out) + self.assertEquals(out["query"], query) + self.assertEquals(out["options"]["silenced"], {"host:abcd1234": None, "host:abcd1235": None}) + + out, err, return_code = self.dogshell(["monitor", "unmute", str(out["id"]), + "--all_scopes"]) + assert "id" in out, out + out = json.loads(out) + self.assertEquals(str(out["id"]), monitor_id) + self.assertEquals(out["options"]["silenced"], {}) # Delete a monitor self.dogshell(["monitor", "delete", monitor_id])