예제 #1
0
class Zabbix(object):
    def __init__(self):
        url = get_value("ZABBIX_URL")
        user = get_value("ZABBIX_USER")
        password = get_value("ZABBIX_PASSWORD")
        self.host_group_id = get_value("ZABBIX_HOST_GROUP")
        self.watcher_default_password = get_value_or_default(
            "WATCHER_PASSWORD", "watcher")

        from pyzabbix import ZabbixAPI
        self.zapi = ZabbixAPI(url)
        self.zapi.login(user, password)

        from healthcheck.storage import MongoStorage
        self.storage = MongoStorage()
        self.storage.conn()

    def add_url(self, name, url, expected_string=None, comment=None):
        hc = self.storage.find_healthcheck_by_name(name)
        item_id = self._add_item(name, url, expected_string)
        trigger_id = self._add_trigger(name, url, comment)
        action_id = self._add_action(url, trigger_id, hc.group_id)
        item = Item(
            url,
            item_id=item_id,
            trigger_id=trigger_id,
            action_id=action_id,
            group_id=hc.group_id,
        )
        self.storage.add_item(item)

    def _add_item(self, healthcheck_name, url, expected_string=None):
        hc = self.storage.find_healthcheck_by_name(healthcheck_name)
        item_name = self._create_item_name(url)
        step = {"name": item_name, "url": url, "status_codes": "200", "no": 1}
        if expected_string:
            step["required"] = expected_string
        item_result = self.zapi.httptest.create(
            name=item_name,
            steps=[step],
            hostid=hc.host_id,
            retries=int(os.environ.get("ZABBIX_RETRIES", 3)),
        )
        return item_result['httptestids'][0]

    def _create_item_name(self, url):
        name = "hc for {}".format(url)
        if len(name) > 64:
            return name[:61] + "..."
        return name

    def _add_trigger(self, host_name, url, comment=None):
        item_name = self._create_item_name(url)
        status_expression = ("{{%s:web.test.rspcode[{item_name},"
                             "{item_name}].last()}}<>200") % host_name
        failed_expression = "{{%s:web.test.fail[{item_name}].last()}}<>0" % \
            host_name
        string_expression = ("{{%s:web.test.error[{item_name}]."
                             "str(required pattern not found)}}=1") % host_name
        expression = ("%s or %s and %s") % \
            (status_expression, failed_expression, string_expression)

        trigger_result = self.zapi.trigger.create(
            description="trigger for url {}".format(url),
            expression=expression.format(item_name=item_name),
            priority=5,
            comments=comment,
        )
        return trigger_result['triggerids'][0]

    def remove_url(self, name, url):
        item = self.storage.find_item_by_url(url)
        self._remove_action(item.action_id)
        self.zapi.httptest.delete(item.item_id)
        self.storage.remove_item(item)

    def list_urls(self, name):
        urls_comments = []
        urls = self.storage.find_urls_by_healthcheck_name(name)
        for url in urls:
            url_comment = [url]
            item = self.storage.find_item_by_url(url)
            trigger = self.zapi.trigger.get(triggerids=item.trigger_id)
            url_comment.append(trigger[0].get('comments', ""))
            urls_comments.append(url_comment)
        return urls_comments

    def new(self, name):
        host = self._add_host(name, self.host_group_id)
        group = self._add_group(name, self.host_group_id)
        hc = HealthCheck(name=name,
                         host_group_id=self.host_group_id,
                         host_id=host,
                         group_id=group)
        self.storage.add_healthcheck(hc)

    def add_watcher(self, name, email, password=None):
        hc = self.storage.find_healthcheck_by_name(name)
        try:
            user = self.storage.find_user_by_email(email)
            self._add_user_to_group(hc, user)
        except UserNotFoundError:
            if not password:
                password = self.watcher_default_password

            self._add_new_user(hc, email, password)

    def _add_user_to_group(self, hc, user):
        users = self.storage.find_users_by_group(hc.group_id)
        ids = [u.id for u in users]
        if user.id in ids:
            raise WatcherAlreadyRegisteredError()
        ids.append(user.id)
        self.zapi.usergroup.update(
            usrgrpid=hc.group_id,
            userids=ids,
        )
        self.storage.add_user_to_group(user, hc.group_id)

    def _add_new_user(self, hc, email, password):
        result = self.zapi.user.create(
            alias=email,
            passwd=password,
            usrgrps=[{
                "usrgrpid": hc.group_id,
            }],
            user_medias=[{
                "mediatypeid": "1",
                "sendto": email,
                "active": 0,
                "severity": 63,
                "period": "1-7,00:00-24:00",
            }],
        )
        user_id = result["userids"][0]
        user = User(user_id, email, hc.group_id)
        self.storage.add_user(user)

    def list_watchers(self, name):
        return self.storage.find_watchers_by_healthcheck_name(name)

    def remove_watcher(self, name, email):
        hc = self.storage.find_healthcheck_by_name(name)
        user = self.storage.find_user_by_email(email)
        if hc.group_id not in user.groups_id:
            raise WatcherNotInInstanceError()
        if len(user.groups_id) > 1:
            self._remove_user_from_group(hc, user)
        else:
            self._remove_user(user)

    def _remove_user_from_group(self, hc, user):
        users = self.storage.find_users_by_group(hc.group_id)
        ids = [u.id for u in users if u.id != user.id]
        self.zapi.usergroup.update(
            usrgrpid=hc.group_id,
            userids=ids,
        )
        self.storage.remove_user_from_group(user, hc.group_id)

    def _remove_user(self, user):
        self.zapi.user.delete(user.id)
        self.storage.remove_user(user)

    def remove(self, name):
        urls = self.storage.find_urls_by_healthcheck_name(name)
        for url in urls:
            self.remove_url(name, url)

        watchers = self.list_watchers(name)
        for watcher in watchers:
            self.remove_watcher(name, watcher)

        healthcheck = self.storage.find_healthcheck_by_name(name)
        self._remove_group(healthcheck.group_id)
        self._remove_host(healthcheck.host_id)
        self.storage.remove_healthcheck(healthcheck)

    def _add_action(self, url, trigger_id, group_id):
        result = self.zapi.action.create(
            name="action for url {}".format(url),
            eventsource=0,
            recovery_msg=1,
            status=0,
            esc_period=3600,
            def_shortdata=("hcaas {HOST.NAME} #{EVENT.ID} {TRIGGER.STATUS}: "
                           "{ITEM.VALUE3}"),
            def_longdata=("{TRIGGER.NAME}: {TRIGGER.STATUS}\r\n"
                          "HTTP status code: {ITEM.VALUE1}"),
            r_shortdata="hcaas {HOST.NAME} #{EVENT.ID} {TRIGGER.STATUS}",
            r_longdata=("{TRIGGER.NAME}: {TRIGGER.STATUS}\r\n"
                        "HTTP status code: {ITEM.VALUE1}"),
            evaltype=0,
            conditions=[
                # Maintenance status not in maintenance
                {
                    "conditiontype": 16,
                    "value": "",
                    "operator": 7
                },
                # Trigger value = PROBLEM
                {
                    "conditiontype": 5,
                    "value": "1"
                },
                # Trigger = trigger id
                {
                    "conditiontype": 2,
                    "value": trigger_id
                },
            ],
            operations=[{
                "operationtype": 0,
                "esc_period": 0,
                "esc_step_from": 1,
                "esc_step_to": 1,
                "evaltype": 0,
                "mediatypeid": 0,
                "opmessage_grp": [{
                    "usrgrpid": group_id
                }],
                "opmessage": {
                    "default_msg": 1,
                    "mediatypeid": "0"
                }
            }],
        )
        return result["actionids"][0]

    def _add_group(self, name, host_group):
        result = self.zapi.usergroup.create(
            name=name,
            rights={
                "permission": 2,
                "id": host_group
            },
        )
        return result["usrgrpids"][0]

    def _add_host(self, name, host_group):
        result = self.zapi.host.create(host=name,
                                       groups=[{
                                           "groupid": host_group
                                       }],
                                       interfaces=[{
                                           "type": 1,
                                           "main": 1,
                                           "useip": 1,
                                           "ip": "127.0.0.1",
                                           "dns": "",
                                           "port": "10050"
                                       }])
        return result["hostids"][0]

    def _remove_host(self, id):
        self.zapi.host.delete(id)

    def _remove_group(self, id):
        self.zapi.usergroup.delete(id)

    def _remove_action(self, id):
        self.zapi.action.delete(id)
class MongoStorageTest(unittest.TestCase):

    def remove_env(self, env):
        if env in os.environ:
            del os.environ[env]

    def setUp(self):
        self.storage = MongoStorage()
        self.url = "http://myurl.com"
        self.item = Item(self.url)
        self.user = User("id", "*****@*****.**", ["group_id"])
        self.healthcheck = HealthCheck("bla")

    @mock.patch("pymongo.MongoClient")
    def test_mongodb_host_environ(self, mongo_mock):
        self.storage.conn()
        mongo_mock.assert_called_with('mongodb://*****:*****@mock.patch("pymongo.MongoClient")
    def test_mongodb_port_environ(self, mongo_mock):
        self.storage.conn()
        mongo_mock.assert_called_with('mongodb://*****:*****@otherthing.io")

    def test_find_users_by_group(self):
        user1 = User("id1", "*****@*****.**", "group_id1", "group_id2", "group_id3")
        user2 = User("id2", "*****@*****.**", "group_id2", "group_id3")
        user3 = User("id3", "*****@*****.**", "group_id3")
        self.storage.add_user(user1)
        self.addCleanup(self.storage.remove_user, user1)
        self.storage.add_user(user2)
        self.addCleanup(self.storage.remove_user, user2)
        self.storage.add_user(user3)
        self.addCleanup(self.storage.remove_user, user3)
        users = self.storage.find_users_by_group("group_id3")
        self.assertEqual([user1, user2, user3], users)
        users = self.storage.find_users_by_group("group_id2")
        self.assertEqual([user1, user2], users)
        users = self.storage.find_users_by_group("group_id1")
        self.assertEqual([user1], users)

    def test_include_user_in_group(self):
        user = User("userid", "*****@*****.**", "group1")
        self.storage.add_user(user)
        self.addCleanup(self.storage.remove_user, user)
        self.storage.add_user_to_group(user, "group2")
        self.storage.add_user_to_group(user, "group3")
        self.storage.add_user_to_group(user, "group4")
        self.storage.add_user_to_group(user, "group5")
        user = self.storage.find_user_by_email(user.email)
        self.assertEqual(("group1", "group2", "group3", "group4", "group5"),
                         user.groups_id)

    def test_remove_user_from_group(self):
        user = User("userid", "*****@*****.**", "group1")
        self.storage.add_user(user)
        self.addCleanup(self.storage.remove_user, user)
        self.storage.add_user_to_group(user, "group2")
        self.storage.add_user_to_group(user, "group3")
        self.storage.add_user_to_group(user, "group4")
        self.storage.add_user_to_group(user, "group5")
        self.storage.remove_user_from_group(user, "group4")
        self.storage.remove_user_from_group(user, "group3")
        user = self.storage.find_user_by_email(user.email)
        self.assertEqual(("group1", "group2", "group5"),
                         user.groups_id)
class Zabbix(object):
    def __init__(self):
        url = get_value("ZABBIX_URL")
        user = get_value("ZABBIX_USER")
        password = get_value("ZABBIX_PASSWORD")
        self.host_group_id = get_value("ZABBIX_HOST_GROUP")

        from pyzabbix import ZabbixAPI
        self.zapi = ZabbixAPI(url)
        self.zapi.login(user, password)

        from healthcheck.storage import MongoStorage
        self.storage = MongoStorage()
        self.storage.conn()

    def add_url(self, name, url, expected_string=None, comment=None):
        hc = self.storage.find_healthcheck_by_name(name)
        item_id = self._add_item(name, url, expected_string)
        trigger_id = self._add_trigger(name, url, comment)
        action_id = self._add_action(url, trigger_id, hc.group_id)
        item = Item(
            url,
            item_id=item_id,
            trigger_id=trigger_id,
            action_id=action_id,
            group_id=hc.group_id,
        )
        self.storage.add_item(item)

    def _add_item(self, healthcheck_name, url, expected_string=None):
        hc = self.storage.find_healthcheck_by_name(healthcheck_name)
        item_name = self._create_item_name(url)
        step = {"name": item_name, "url": url,
                "status_codes": 200, "no": 1}
        if expected_string:
            step["required"] = expected_string
        item_result = self.zapi.httptest.create(
            name=item_name,
            steps=[step],
            hostid=hc.host_id,
            retries=int(os.environ.get("ZABBIX_RETRIES", 3)),
        )
        return item_result['httptestids'][0]

    def _create_item_name(self, url):
        name = "hc for {}".format(url)
        if len(name) > 64:
            return name[:61] + "..."
        return name

    def _add_trigger(self, host_name, url, comment=None):
        item_name = self._create_item_name(url)
        status_expression = ("{{%s:web.test.rspcode[{item_name},"
                             "{item_name}].last()}}#200") % host_name
        failed_expression = "{{%s:web.test.fail[{item_name}].last()}}#0" % \
            host_name
        string_expression = ("{{%s:web.test.error[{item_name}]."
                             "str(required pattern not found)}}=1") % host_name
        expression = "%s | %s & %s" % (status_expression, failed_expression,
                                       string_expression)
        trigger_result = self.zapi.trigger.create(
            description="trigger for url {}".format(url),
            expression=expression.format(item_name=item_name),
            priority=5,
            comments=comment,
        )
        return trigger_result['triggerids'][0]

    def remove_url(self, name, url):
        item = self.storage.find_item_by_url(url)
        self._remove_action(item.action_id)
        self.zapi.httptest.delete(item.item_id)
        self.storage.remove_item(item)

    def list_urls(self, name):
        urls_comments = []
        urls = self.storage.find_urls_by_healthcheck_name(name)
        for url in urls:
            url_comment = [url]
            item = self.storage.find_item_by_url(url)
            trigger = self.zapi.trigger.get(triggerids=item.trigger_id)
            url_comment.append(trigger[0].get('comments', ""))
            urls_comments.append(url_comment)
        return urls_comments

    def new(self, name):
        host = self._add_host(name, self.host_group_id)
        group = self._add_group(name, self.host_group_id)
        hc = HealthCheck(
            name=name,
            host_group_id=self.host_group_id,
            host_id=host,
            group_id=group
        )
        self.storage.add_healthcheck(hc)

    def add_watcher(self, name, email):
        hc = self.storage.find_healthcheck_by_name(name)
        try:
            user = self.storage.find_user_by_email(email)
            self._add_user_to_group(hc, user)
        except UserNotFoundError:
            self._add_new_user(hc, email)

    def _add_user_to_group(self, hc, user):
        users = self.storage.find_users_by_group(hc.group_id)
        ids = [u.id for u in users]
        if user.id in ids:
            raise WatcherAlreadyRegisteredError()
        ids.append(user.id)
        self.zapi.usergroup.update(
            usrgrpid=hc.group_id,
            userids=ids,
        )
        self.storage.add_user_to_group(user, hc.group_id)

    def _add_new_user(self, hc, email):
        result = self.zapi.user.create(
            alias=email,
            passwd="",
            usrgrps=[hc.group_id],
            user_medias=[{
                "mediatypeid": "1",
                "sendto": email,
                "active": 0,
                "severity": 63,
                "period": "1-7,00:00-24:00",
            }],
        )
        user_id = result["userids"][0]
        user = User(user_id, email, hc.group_id)
        self.storage.add_user(user)

    def list_watchers(self, name):
        return self.storage.find_watchers_by_healthcheck_name(name)

    def remove_watcher(self, name, email):
        hc = self.storage.find_healthcheck_by_name(name)
        user = self.storage.find_user_by_email(email)
        if hc.group_id not in user.groups_id:
            raise WatcherNotInInstanceError()
        if len(user.groups_id) > 1:
            self._remove_user_from_group(hc, user)
        else:
            self._remove_user(user)

    def _remove_user_from_group(self, hc, user):
        users = self.storage.find_users_by_group(hc.group_id)
        ids = [u.id for u in users if u.id != user.id]
        self.zapi.usergroup.update(
            usrgrpid=hc.group_id,
            userids=ids,
        )
        self.storage.remove_user_from_group(user, hc.group_id)

    def _remove_user(self, user):
        self.zapi.user.delete(user.id)
        self.storage.remove_user(user)

    def remove(self, name):
        urls = self.storage.find_urls_by_healthcheck_name(name)
        for url in urls:
            self.remove_url(name, url)

        watchers = self.list_watchers(name)
        for watcher in watchers:
            self.remove_watcher(name, watcher)

        healthcheck = self.storage.find_healthcheck_by_name(name)
        self._remove_group(healthcheck.group_id)
        self._remove_host(healthcheck.host_id)
        self.storage.remove_healthcheck(healthcheck)

    def _add_action(self, url, trigger_id, group_id):
        result = self.zapi.action.create(
            name="action for url {}".format(url),
            eventsource=0,
            recovery_msg=1,
            status=0,
            esc_period=3600,
            def_shortdata=("[hcaas {HOST.NAME} #{EVENT.ID}] {TRIGGER.STATUS}: "
                           "{ITEM.VALUE3}"),
            def_longdata=("{TRIGGER.NAME}: {TRIGGER.STATUS}\r\n"
                          "HTTP status code: {ITEM.VALUE1}"),
            r_shortdata="[hcaas {HOST.NAME} #{EVENT.ID}] {TRIGGER.STATUS}",
            r_longdata=("{TRIGGER.NAME}: {TRIGGER.STATUS}\r\n"
                        "HTTP status code: {ITEM.VALUE1}"),
            evaltype=0,
            conditions=[
                # Maintenance status not in maintenance
                {"conditiontype": 16, "value": "", "operator": 7},
                # Trigger value = PROBLEM
                {"conditiontype": 5, "value": "1"},
                # Trigger = trigger id
                {"conditiontype": 2, "value": trigger_id},
            ],
            operations=[
                {
                    "operationtype": 0,
                    "esc_period": 0,
                    "esc_step_from": 1,
                    "esc_step_to": 1,
                    "evaltype": 0,
                    "mediatypeid": 0,
                    "opmessage_grp": [
                        {
                            "usrgrpid": group_id
                        }
                    ],
                    "opmessage": {
                        "default_msg": 1,
                        "mediatypeid": "0"
                    }
                }
            ],
        )
        return result["actionids"][0]

    def _add_group(self, name, host_group):
        result = self.zapi.usergroup.create(
            name=name,
            rights={"permission": 2, "id": host_group},
        )
        return result["usrgrpids"][0]

    def _add_host(self, name, host_group):
        result = self.zapi.host.create(
            host=name,
            groups=[{"groupid": host_group}],
            interfaces=[{
                "type": 1,
                "main": 1,
                "useip": 1,
                "ip": "127.0.0.1",
                "dns": "",
                "port": "10050"
            }]
        )
        return result["hostids"][0]

    def _remove_host(self, id):
        self.zapi.host.delete(id)

    def _remove_group(self, id):
        self.zapi.usergroup.delete(id)

    def _remove_action(self, id):
        self.zapi.action.delete(id)
class MongoStorageTest(unittest.TestCase):

    def remove_env(self, env):
        if env in os.environ:
            del os.environ[env]

    def setUp(self):
        self.storage = MongoStorage()
        self.url = "http://myurl.com"
        self.item = Item(self.url)
        self.user = User("id", "*****@*****.**", ["group_id"])
        self.healthcheck = HealthCheck("bla")

    @mock.patch("pymongo.MongoClient")
    def test_mongodb_host_environ(self, mongo_mock):
        self.storage.conn()
        mongo_mock.assert_called_with('mongodb://*****:*****@mock.patch("pymongo.MongoClient")
    def test_mongodb_port_environ(self, mongo_mock):
        self.storage.conn()
        mongo_mock.assert_called_with('mongodb://*****:*****@otherthing.io")

    def test_find_users_by_group(self):
        user1 = User("id1", "*****@*****.**", "group_id1", "group_id2", "group_id3")
        user2 = User("id2", "*****@*****.**", "group_id2", "group_id3")
        user3 = User("id3", "*****@*****.**", "group_id3")
        self.storage.add_user(user1)
        self.addCleanup(self.storage.remove_user, user1)
        self.storage.add_user(user2)
        self.addCleanup(self.storage.remove_user, user2)
        self.storage.add_user(user3)
        self.addCleanup(self.storage.remove_user, user3)
        users = self.storage.find_users_by_group("group_id3")
        self.assertEqual([user1, user2, user3], users)
        users = self.storage.find_users_by_group("group_id2")
        self.assertEqual([user1, user2], users)
        users = self.storage.find_users_by_group("group_id1")
        self.assertEqual([user1], users)

    def test_include_user_in_group(self):
        user = User("userid", "*****@*****.**", "group1")
        self.storage.add_user(user)
        self.addCleanup(self.storage.remove_user, user)
        self.storage.add_user_to_group(user, "group2")
        self.storage.add_user_to_group(user, "group3")
        self.storage.add_user_to_group(user, "group4")
        self.storage.add_user_to_group(user, "group5")
        user = self.storage.find_user_by_email(user.email)
        self.assertEqual(("group1", "group2", "group3", "group4", "group5"),
                         user.groups_id)

    def test_remove_user_from_group(self):
        user = User("userid", "*****@*****.**", "group1")
        self.storage.add_user(user)
        self.addCleanup(self.storage.remove_user, user)
        self.storage.add_user_to_group(user, "group2")
        self.storage.add_user_to_group(user, "group3")
        self.storage.add_user_to_group(user, "group4")
        self.storage.add_user_to_group(user, "group5")
        self.storage.remove_user_from_group(user, "group4")
        self.storage.remove_user_from_group(user, "group3")
        user = self.storage.find_user_by_email(user.email)
        self.assertEqual(("group1", "group2", "group5"),
                         user.groups_id)

    def test_add_group_to_instance(self):
        self.storage.add_healthcheck(self.healthcheck)
        self.storage.add_group_to_instance(self.healthcheck, "group1")
        self.storage.add_group_to_instance(self.healthcheck, "group2")
        result = self.storage.find_healthcheck_by_name(self.healthcheck.name)
        self.assertEqual(["group1", "group2"], result.host_groups)
        self.storage.remove_healthcheck(self.healthcheck)

    def test_remove_group_from_instance(self):
        self.storage.add_healthcheck(self.healthcheck)
        self.storage.add_group_to_instance(self.healthcheck, "group1")
        self.storage.add_group_to_instance(self.healthcheck, "group2")
        self.storage.add_group_to_instance(self.healthcheck, "group3")
        self.storage.remove_group_from_instance(self.healthcheck, "group1")
        self.storage.remove_group_from_instance(self.healthcheck, "group2")
        result = self.storage.find_healthcheck_by_name(self.healthcheck.name)
        self.assertEqual(["group3"], result.host_groups)
        self.storage.remove_healthcheck(self.healthcheck)