|
|
15f218 |
From b438fe5c0eb4e6fa738e21287540c0d8f6b91c68 Mon Sep 17 00:00:00 2001
|
|
|
15f218 |
From: Ivan Devat <idevat@redhat.com>
|
|
|
15f218 |
Date: Fri, 19 Aug 2016 02:57:39 +0200
|
|
|
15f218 |
Subject: [PATCH] squash bz1315371 [RFE] Provide configurable alerts
|
|
|
15f218 |
|
|
|
15f218 |
25a25c534ff6 show help when unknown subcommand of 'pcs alert recipient' was given
|
|
|
15f218 |
|
|
|
15f218 |
c352ce184093 make syntax of command 'pcs alert recipient add' more consistent
|
|
|
15f218 |
|
|
|
15f218 |
8c6ec586d57c fix error handling when upgrading cib schema
|
|
|
15f218 |
---
|
|
|
15f218 |
pcs/alert.py | 9 +++--
|
|
|
15f218 |
pcs/lib/cib/tools.py | 36 +++++++++--------
|
|
|
15f218 |
pcs/pcs.8 | 2 +-
|
|
|
15f218 |
pcs/test/test_alert.py | 44 +++++++++++++-------
|
|
|
15f218 |
pcs/test/test_lib_cib_tools.py | 91 ++++++++++++++++++++++++++++++++++++++++++
|
|
|
15f218 |
pcs/usage.py | 2 +-
|
|
|
15f218 |
6 files changed, 147 insertions(+), 37 deletions(-)
|
|
|
15f218 |
|
|
|
15f218 |
diff --git a/pcs/alert.py b/pcs/alert.py
|
|
|
15f218 |
index 693bb8d..17f4e8d 100644
|
|
|
15f218 |
--- a/pcs/alert.py
|
|
|
15f218 |
+++ b/pcs/alert.py
|
|
|
15f218 |
@@ -63,6 +63,8 @@ def recipient_cmd(*args):
|
|
|
15f218 |
recipient_update(*args)
|
|
|
15f218 |
elif sub_cmd == "remove":
|
|
|
15f218 |
recipient_remove(*args)
|
|
|
15f218 |
+ else:
|
|
|
15f218 |
+ raise CmdLineInputError()
|
|
|
15f218 |
except CmdLineInputError as e:
|
|
|
15f218 |
utils.exit_on_cmdline_input_errror(
|
|
|
15f218 |
e, "alert", "recipient {0}".format(sub_cmd)
|
|
|
15f218 |
@@ -127,15 +129,14 @@ def recipient_add(lib, argv, modifiers):
|
|
|
15f218 |
raise CmdLineInputError()
|
|
|
15f218 |
|
|
|
15f218 |
alert_id = argv[0]
|
|
|
15f218 |
- recipient_value = argv[1]
|
|
|
15f218 |
|
|
|
15f218 |
- sections = parse_cmd_sections(argv[2:], set(["options", "meta"]))
|
|
|
15f218 |
+ sections = parse_cmd_sections(argv[1:], set(["options", "meta"]))
|
|
|
15f218 |
main_args = prepare_options(sections["main"])
|
|
|
15f218 |
- ensure_only_allowed_options(main_args, ["description", "id"])
|
|
|
15f218 |
+ ensure_only_allowed_options(main_args, ["description", "id", "value"])
|
|
|
15f218 |
|
|
|
15f218 |
lib.alert.add_recipient(
|
|
|
15f218 |
alert_id,
|
|
|
15f218 |
- recipient_value,
|
|
|
15f218 |
+ main_args.get("value", None),
|
|
|
15f218 |
prepare_options(sections["options"]),
|
|
|
15f218 |
prepare_options(sections["meta"]),
|
|
|
15f218 |
recipient_id=main_args.get("id", None),
|
|
|
15f218 |
diff --git a/pcs/lib/cib/tools.py b/pcs/lib/cib/tools.py
|
|
|
15f218 |
index d8ce57a..8141360 100644
|
|
|
15f218 |
--- a/pcs/lib/cib/tools.py
|
|
|
15f218 |
+++ b/pcs/lib/cib/tools.py
|
|
|
15f218 |
@@ -176,29 +176,31 @@ def upgrade_cib(cib, runner):
|
|
|
15f218 |
cib -- cib etree
|
|
|
15f218 |
runner -- CommandRunner
|
|
|
15f218 |
"""
|
|
|
15f218 |
- temp_file = tempfile.NamedTemporaryFile("w+", suffix=".pcs")
|
|
|
15f218 |
- temp_file.write(etree.tostring(cib).decode())
|
|
|
15f218 |
- temp_file.flush()
|
|
|
15f218 |
- output, retval = runner.run(
|
|
|
15f218 |
- [
|
|
|
15f218 |
- os.path.join(settings.pacemaker_binaries, "cibadmin"),
|
|
|
15f218 |
- "--upgrade",
|
|
|
15f218 |
- "--force"
|
|
|
15f218 |
- ],
|
|
|
15f218 |
- env_extend={"CIB_file": temp_file.name}
|
|
|
15f218 |
- )
|
|
|
15f218 |
+ temp_file = None
|
|
|
15f218 |
+ try:
|
|
|
15f218 |
+ temp_file = tempfile.NamedTemporaryFile("w+", suffix=".pcs")
|
|
|
15f218 |
+ temp_file.write(etree.tostring(cib).decode())
|
|
|
15f218 |
+ temp_file.flush()
|
|
|
15f218 |
+ output, retval = runner.run(
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ os.path.join(settings.pacemaker_binaries, "cibadmin"),
|
|
|
15f218 |
+ "--upgrade",
|
|
|
15f218 |
+ "--force"
|
|
|
15f218 |
+ ],
|
|
|
15f218 |
+ env_extend={"CIB_file": temp_file.name}
|
|
|
15f218 |
+ )
|
|
|
15f218 |
|
|
|
15f218 |
- if retval != 0:
|
|
|
15f218 |
- temp_file.close()
|
|
|
15f218 |
- LibraryError(reports.cib_upgrade_failed(output))
|
|
|
15f218 |
+ if retval != 0:
|
|
|
15f218 |
+ temp_file.close()
|
|
|
15f218 |
+ raise LibraryError(reports.cib_upgrade_failed(output))
|
|
|
15f218 |
|
|
|
15f218 |
- try:
|
|
|
15f218 |
temp_file.seek(0)
|
|
|
15f218 |
return etree.fromstring(temp_file.read())
|
|
|
15f218 |
except (EnvironmentError, etree.XMLSyntaxError, etree.DocumentInvalid) as e:
|
|
|
15f218 |
- LibraryError(reports.cib_upgrade_failed(str(e)))
|
|
|
15f218 |
+ raise LibraryError(reports.cib_upgrade_failed(str(e)))
|
|
|
15f218 |
finally:
|
|
|
15f218 |
- temp_file.close()
|
|
|
15f218 |
+ if temp_file:
|
|
|
15f218 |
+ temp_file.close()
|
|
|
15f218 |
|
|
|
15f218 |
|
|
|
15f218 |
def ensure_cib_version(runner, cib, version):
|
|
|
15f218 |
diff --git a/pcs/pcs.8 b/pcs/pcs.8
|
|
|
15f218 |
index 7a054ca..b3c4877 100644
|
|
|
15f218 |
--- a/pcs/pcs.8
|
|
|
15f218 |
+++ b/pcs/pcs.8
|
|
|
15f218 |
@@ -727,7 +727,7 @@ Update existing alert handler with specified id.
|
|
|
15f218 |
remove <alert\-id>
|
|
|
15f218 |
Remove alert handler with specified id.
|
|
|
15f218 |
.TP
|
|
|
15f218 |
-recipient add <alert\-id> <recipient\-value> [id=<recipient\-id>] [description=<description>] [options [<option>=<value>]...] [meta [<meta-option>=<value>]...]
|
|
|
15f218 |
+recipient add <alert\-id> value=<recipient\-value> [id=<recipient\-id>] [description=<description>] [options [<option>=<value>]...] [meta [<meta-option>=<value>]...]
|
|
|
15f218 |
Add new recipient to specified alert handler.
|
|
|
15f218 |
.TP
|
|
|
15f218 |
recipient update <recipient\-id> [value=<recipient\-value>] [description=<description>] [options [<option>=<value>]...] [meta [<meta-option>=<value>]...]
|
|
|
15f218 |
diff --git a/pcs/test/test_alert.py b/pcs/test/test_alert.py
|
|
|
15f218 |
index f6ea70d..d919ff6 100644
|
|
|
15f218 |
--- a/pcs/test/test_alert.py
|
|
|
15f218 |
+++ b/pcs/test/test_alert.py
|
|
|
15f218 |
@@ -233,7 +233,7 @@ Alerts:
|
|
|
15f218 |
Alert: alert (path=test)
|
|
|
15f218 |
"""
|
|
|
15f218 |
)
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value")
|
|
|
15f218 |
+ self.assert_pcs_success("alert recipient add alert value=rec_value")
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
"alert config",
|
|
|
15f218 |
"""\
|
|
|
15f218 |
@@ -244,7 +244,7 @@ Alerts:
|
|
|
15f218 |
"""
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
- "alert recipient add alert rec_value2 id=my-recipient "
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value2 id=my-recipient "
|
|
|
15f218 |
"description=description options o1=1 o2=2 meta m1=v1 m2=v2"
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
@@ -263,21 +263,25 @@ Alerts:
|
|
|
15f218 |
|
|
|
15f218 |
def test_already_exists(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value id=rec")
|
|
|
15f218 |
+ self.assert_pcs_success(
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value id=rec"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
self.assert_pcs_fail(
|
|
|
15f218 |
- "alert recipient add alert value id=rec",
|
|
|
15f218 |
+ "alert recipient add alert value=value id=rec",
|
|
|
15f218 |
"Error: 'rec' already exists\n"
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assert_pcs_fail(
|
|
|
15f218 |
- "alert recipient add alert value id=alert",
|
|
|
15f218 |
+ "alert recipient add alert value=value id=alert",
|
|
|
15f218 |
"Error: 'alert' already exists\n"
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
def test_same_value(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value id=rec")
|
|
|
15f218 |
+ self.assert_pcs_success(
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value id=rec"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
self.assert_pcs_fail(
|
|
|
15f218 |
- "alert recipient add alert rec_value",
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value",
|
|
|
15f218 |
"Error: Recipient 'rec_value' in alert 'alert' already exists, "
|
|
|
15f218 |
"use --force to override\n"
|
|
|
15f218 |
)
|
|
|
15f218 |
@@ -291,7 +295,7 @@ Alerts:
|
|
|
15f218 |
"""
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
- "alert recipient add alert rec_value --force",
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value --force",
|
|
|
15f218 |
"Warning: Recipient 'rec_value' in alert 'alert' already exists\n"
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
@@ -305,13 +309,21 @@ Alerts:
|
|
|
15f218 |
"""
|
|
|
15f218 |
)
|
|
|
15f218 |
|
|
|
15f218 |
+ def test_no_value(self):
|
|
|
15f218 |
+ self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
+ self.assert_pcs_fail(
|
|
|
15f218 |
+ "alert recipient add alert id=rec",
|
|
|
15f218 |
+ "Error: required option 'value' is missing\n"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+
|
|
|
15f218 |
|
|
|
15f218 |
@unittest.skipUnless(ALERTS_SUPPORTED, ALERTS_NOT_SUPPORTED_MSG)
|
|
|
15f218 |
class UpdateRecipientAlert(PcsAlertTest):
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
- "alert recipient add alert rec_value description=description "
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value description=description "
|
|
|
15f218 |
"options o1=1 o2=2 meta m1=v1 m2=v2"
|
|
|
15f218 |
)
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
@@ -360,8 +372,8 @@ Alerts:
|
|
|
15f218 |
|
|
|
15f218 |
def test_value_exists(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert value")
|
|
|
15f218 |
+ self.assert_pcs_success("alert recipient add alert value=rec_value")
|
|
|
15f218 |
+ self.assert_pcs_success("alert recipient add alert value=value")
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
"alert config",
|
|
|
15f218 |
"""\
|
|
|
15f218 |
@@ -394,7 +406,7 @@ Alerts:
|
|
|
15f218 |
|
|
|
15f218 |
def test_value_same_as_previous(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value")
|
|
|
15f218 |
+ self.assert_pcs_success("alert recipient add alert value=rec_value")
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
"alert config",
|
|
|
15f218 |
"""\
|
|
|
15f218 |
@@ -425,7 +437,9 @@ Alerts:
|
|
|
15f218 |
|
|
|
15f218 |
def test_empty_value(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value id=rec")
|
|
|
15f218 |
+ self.assert_pcs_success(
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value id=rec"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
self.assert_pcs_fail(
|
|
|
15f218 |
"alert recipient update rec value=",
|
|
|
15f218 |
"Error: Recipient value '' is not valid.\n"
|
|
|
15f218 |
@@ -436,7 +450,9 @@ Alerts:
|
|
|
15f218 |
class RemoveRecipientTest(PcsAlertTest):
|
|
|
15f218 |
def test_success(self):
|
|
|
15f218 |
self.assert_pcs_success("alert create path=test")
|
|
|
15f218 |
- self.assert_pcs_success("alert recipient add alert rec_value id=rec")
|
|
|
15f218 |
+ self.assert_pcs_success(
|
|
|
15f218 |
+ "alert recipient add alert value=rec_value id=rec"
|
|
|
15f218 |
+ )
|
|
|
15f218 |
self.assert_pcs_success(
|
|
|
15f218 |
"alert config",
|
|
|
15f218 |
"""\
|
|
|
15f218 |
diff --git a/pcs/test/test_lib_cib_tools.py b/pcs/test/test_lib_cib_tools.py
|
|
|
15f218 |
index 10f8a96..0fd4d22 100644
|
|
|
15f218 |
--- a/pcs/test/test_lib_cib_tools.py
|
|
|
15f218 |
+++ b/pcs/test/test_lib_cib_tools.py
|
|
|
15f218 |
@@ -7,6 +7,7 @@ from __future__ import (
|
|
|
15f218 |
|
|
|
15f218 |
from unittest import TestCase
|
|
|
15f218 |
|
|
|
15f218 |
+from os.path import join
|
|
|
15f218 |
from lxml import etree
|
|
|
15f218 |
|
|
|
15f218 |
from pcs.test.tools.assertions import (
|
|
|
15f218 |
@@ -17,6 +18,7 @@ from pcs.test.tools.misc import get_test_resource as rc
|
|
|
15f218 |
from pcs.test.tools.pcs_mock import mock
|
|
|
15f218 |
from pcs.test.tools.xml import get_xml_manipulation_creator_from_file
|
|
|
15f218 |
|
|
|
15f218 |
+from pcs import settings
|
|
|
15f218 |
from pcs.common import report_codes
|
|
|
15f218 |
from pcs.lib.external import CommandRunner
|
|
|
15f218 |
from pcs.lib.errors import ReportItemSeverity as severities
|
|
|
15f218 |
@@ -369,3 +371,92 @@ class EnsureCibVersionTest(TestCase):
|
|
|
15f218 |
)
|
|
|
15f218 |
)
|
|
|
15f218 |
mock_upgrade_cib.assert_called_once_with(self.cib, self.mock_runner)
|
|
|
15f218 |
+
|
|
|
15f218 |
+
|
|
|
15f218 |
+@mock.patch("tempfile.NamedTemporaryFile")
|
|
|
15f218 |
+class UpgradeCibTest(TestCase):
|
|
|
15f218 |
+ def setUp(self):
|
|
|
15f218 |
+ self.mock_runner = mock.MagicMock(spec_set=CommandRunner)
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_success(self, mock_named_file):
|
|
|
15f218 |
+ mock_file = mock.MagicMock()
|
|
|
15f218 |
+ mock_file.name = "mock_file_name"
|
|
|
15f218 |
+ mock_file.read.return_value = "<cib/>"
|
|
|
15f218 |
+ mock_named_file.return_value = mock_file
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ assert_xml_equal(
|
|
|
15f218 |
+ "<cib/>",
|
|
|
15f218 |
+ etree.tostring(
|
|
|
15f218 |
+ lib.upgrade_cib(etree.XML("<old_cib/>"), self.mock_runner)
|
|
|
15f218 |
+ ).decode()
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_named_file.assert_called_once_with("w+", suffix=".pcs")
|
|
|
15f218 |
+ mock_file.write.assert_called_once_with("<old_cib/>")
|
|
|
15f218 |
+ mock_file.flush.assert_called_once_with()
|
|
|
15f218 |
+ self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ join(settings.pacemaker_binaries, "cibadmin"),
|
|
|
15f218 |
+ "--upgrade",
|
|
|
15f218 |
+ "--force"
|
|
|
15f218 |
+ ],
|
|
|
15f218 |
+ env_extend={"CIB_file": "mock_file_name"}
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_file.seek.assert_called_once_with(0)
|
|
|
15f218 |
+ mock_file.read.assert_called_once_with()
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_upgrade_failed(self, mock_named_file):
|
|
|
15f218 |
+ mock_file = mock.MagicMock()
|
|
|
15f218 |
+ mock_file.name = "mock_file_name"
|
|
|
15f218 |
+ mock_named_file.return_value = mock_file
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("reason", 1)
|
|
|
15f218 |
+ assert_raise_library_error(
|
|
|
15f218 |
+ lambda: lib.upgrade_cib(etree.XML("<old_cib/>"), self.mock_runner),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severities.ERROR,
|
|
|
15f218 |
+ report_codes.CIB_UPGRADE_FAILED,
|
|
|
15f218 |
+ {"reason": "reason"}
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_named_file.assert_called_once_with("w+", suffix=".pcs")
|
|
|
15f218 |
+ mock_file.write.assert_called_once_with("<old_cib/>")
|
|
|
15f218 |
+ mock_file.flush.assert_called_once_with()
|
|
|
15f218 |
+ self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ join(settings.pacemaker_binaries, "cibadmin"),
|
|
|
15f218 |
+ "--upgrade",
|
|
|
15f218 |
+ "--force"
|
|
|
15f218 |
+ ],
|
|
|
15f218 |
+ env_extend={"CIB_file": "mock_file_name"}
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+
|
|
|
15f218 |
+ def test_unable_to_parse_upgraded_cib(self, mock_named_file):
|
|
|
15f218 |
+ mock_file = mock.MagicMock()
|
|
|
15f218 |
+ mock_file.name = "mock_file_name"
|
|
|
15f218 |
+ mock_file.read.return_value = "not xml"
|
|
|
15f218 |
+ mock_named_file.return_value = mock_file
|
|
|
15f218 |
+ self.mock_runner.run.return_value = ("", 0)
|
|
|
15f218 |
+ assert_raise_library_error(
|
|
|
15f218 |
+ lambda: lib.upgrade_cib(etree.XML("<old_cib/>"), self.mock_runner),
|
|
|
15f218 |
+ (
|
|
|
15f218 |
+ severities.ERROR,
|
|
|
15f218 |
+ report_codes.CIB_UPGRADE_FAILED,
|
|
|
15f218 |
+ {
|
|
|
15f218 |
+ "reason":
|
|
|
15f218 |
+ "Start tag expected, '<' not found, line 1, column 1",
|
|
|
15f218 |
+ }
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_named_file.assert_called_once_with("w+", suffix=".pcs")
|
|
|
15f218 |
+ mock_file.write.assert_called_once_with("<old_cib/>")
|
|
|
15f218 |
+ mock_file.flush.assert_called_once_with()
|
|
|
15f218 |
+ self.mock_runner.run.assert_called_once_with(
|
|
|
15f218 |
+ [
|
|
|
15f218 |
+ join(settings.pacemaker_binaries, "cibadmin"),
|
|
|
15f218 |
+ "--upgrade",
|
|
|
15f218 |
+ "--force"
|
|
|
15f218 |
+ ],
|
|
|
15f218 |
+ env_extend={"CIB_file": "mock_file_name"}
|
|
|
15f218 |
+ )
|
|
|
15f218 |
+ mock_file.seek.assert_called_once_with(0)
|
|
|
15f218 |
+ mock_file.read.assert_called_once_with()
|
|
|
15f218 |
+
|
|
|
15f218 |
diff --git a/pcs/usage.py b/pcs/usage.py
|
|
|
15f218 |
index 9ebbca9..78e340b 100644
|
|
|
15f218 |
--- a/pcs/usage.py
|
|
|
15f218 |
+++ b/pcs/usage.py
|
|
|
15f218 |
@@ -1507,7 +1507,7 @@ Commands:
|
|
|
15f218 |
remove <alert-id>
|
|
|
15f218 |
Remove alert handler with specified id.
|
|
|
15f218 |
|
|
|
15f218 |
- recipient add <alert-id> <recipient-value> [id=<recipient-id>]
|
|
|
15f218 |
+ recipient add <alert-id> value=<recipient-value> [id=<recipient-id>]
|
|
|
15f218 |
[description=<description>] [options [<option>=<value>]...]
|
|
|
15f218 |
[meta [<meta-option>=<value>]...]
|
|
|
15f218 |
Add new recipient to specified alert handler.
|
|
|
15f218 |
--
|
|
|
15f218 |
1.8.3.1
|
|
|
15f218 |
|