├── .circleci
└── config.yml
├── .github
├── CODEOWNERS
└── workflows
│ ├── build_and_test.yaml
│ └── release.yaml
├── .gitignore
├── CHANGELOG.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── actions
├── .gitkeep
├── ack_event.yaml
├── call_api.py
├── create_host.py
├── create_host.yaml
├── event_action_runner.py
├── host_delete.py
├── host_delete.yaml
├── host_delete_by_id.yaml
├── host_get_active_triggers.yaml
├── host_get_alerts.yaml
├── host_get_events.yaml
├── host_get_hostgroups.py
├── host_get_hostgroups.yaml
├── host_get_id.py
├── host_get_id.yaml
├── host_get_interfaces.py
├── host_get_interfaces.yaml
├── host_get_inventory.py
├── host_get_inventory.yaml
├── host_get_multiple_ids.py
├── host_get_multiple_ids.yaml
├── host_get_status.py
├── host_get_status.yaml
├── host_get_triggers.yaml
├── host_update_status.py
├── host_update_status.yaml
├── lib
│ ├── __init__.py
│ └── actions.py
├── list_host_groups.yaml
├── list_host_interfaces.yaml
├── list_hosts.yaml
├── list_templates.yaml
├── maintenance_create_or_update.py
├── maintenance_create_or_update.yaml
├── maintenance_delete.py
├── maintenance_delete.yaml
├── test_credentials.py
├── test_credentials.yaml
├── update_host.yaml
└── workflows
│ └── host_get_active_triggers.yaml
├── config.schema.yaml
├── docker-compose.yaml
├── icon.png
├── images
├── apikey_example.png
├── configuration_for_action1.png
├── configuration_for_action2.png
├── configuration_for_mediatype1.png
├── configuration_for_mediatype2.png
├── description_alertscript1.png
├── description_alertscript2.png
├── internal_construction.png
└── zabbix_dependency_flow.png
├── pack.yaml
├── requirements.txt
├── spec
├── localhost
│ └── tools_register_config_for_st2_spec.rb
└── spec_helper.rb
├── tests
├── fixtures
│ ├── blank.yaml
│ └── full.yaml
├── test_action_base.py
├── test_call_api.py
├── test_create_host.py
├── test_host_delete.py
├── test_host_get_id.py
├── test_host_get_interfaces.py
├── test_host_get_inventory.py
├── test_host_get_multiple_ids.py
├── test_host_get_status.py
├── test_host_update_status.py
├── test_maintenance_create_or_update.py
├── test_maintenance_delete.py
├── test_test_credentials.py
├── test_tool_register_st2_config_to_zabbix.py
├── test_tool_st2_dispatch.py
└── zabbix_base_action_test_case.py
├── tools
├── register_st2_config_to_zabbix.py
└── scripts
│ └── st2_dispatch.py
├── triggers
└── event_handler.yaml
└── zabbix.yaml.example
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | commands:
4 | integration_test_with_zabbix:
5 | steps:
6 | - checkout
7 | - run:
8 | name: Execute integration test with Zabbix
9 | command: |
10 | set -x
11 | sudo apt -y install python3-pip
12 | sudo pip3 install pip --upgrade
13 | sudo pip3 install -r requirements.txt
14 | bundle install
15 | bundle exec rspec --format documentation
16 |
17 | executors:
18 | zabbix:
19 | parameters:
20 | tag:
21 | type: string
22 | default: latest
23 | docker:
24 | - image: circleci/ruby:2.7.2-buster
25 | - image: mysql:5.7
26 | environment:
27 | MYSQL_DATABASE: zabbix
28 | MYSQL_USER: zabbix
29 | MYSQL_PASSWORD: zabbix
30 | MYSQL_ROOT_PASSWORD: passwd
31 |
32 | - image: "zabbix/zabbix-server-mysql:<< parameters.tag >>"
33 | environment:
34 | DB_SERVER_HOST: 127.0.0.1
35 | MYSQL_ROOT_PASSWORD: passwd
36 |
37 | - image: "zabbix/zabbix-web-nginx-mysql:<< parameters.tag >>"
38 | environment:
39 | DB_SERVER_HOST: 127.0.0.1
40 | MYSQL_ROOT_PASSWORD: passwd
41 |
42 | jobs:
43 | integration_test_with_zabbix_32:
44 | executor:
45 | name: zabbix
46 | tag: ubuntu-3.2-latest
47 | working_directory: ~/repo
48 | environment:
49 | ZABBIX_API: http://localhost/
50 | steps:
51 | - integration_test_with_zabbix
52 |
53 | integration_test_with_zabbix_40:
54 | executor:
55 | name: zabbix
56 | tag: ubuntu-4.0-latest
57 | working_directory: ~/repo
58 | environment:
59 | ZABBIX_API: http://localhost:8080/
60 | steps:
61 | - integration_test_with_zabbix
62 |
63 | circleci_is_disabled_job:
64 | docker:
65 | - image: cimg/base:stable
66 | steps:
67 | - run:
68 | shell: /bin/bash
69 | command: echo CircleCI disabled on StackStorm-Exchange
70 |
71 | workflows:
72 | version: 2
73 | circleci_is_disabled:
74 | jobs:
75 | - circleci_is_disabled_job
76 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This is a comment.
2 | # Each line is a file pattern followed by one or more owners.
3 |
4 | # These owners will be the default owners for everything in
5 | # the repo. Unless a later match takes precedence, users and
6 | # teams which are put in owners will be requested for review
7 | # when someone opens a pull request.
8 |
9 | # This is base configuration. These owners could review the
10 | # whole file in this repository.
11 | * @namachieli @StackStorm-Exchange/encoretechnologies @StackStorm-Exchange/tsc
12 |
13 | # CI configuration files should be reviewed by specific owners
14 | # who are more responsible for ensuring the quality of this pack
15 | # or orchestrate StackStorm-Exchanges.
16 | .circleci/** @StackStorm-Exchange/tsc
17 |
--------------------------------------------------------------------------------
/.github/workflows/build_and_test.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | schedule:
6 | # NOTE: We run this weekly at 1 am UTC on every Saturday
7 | - cron: '0 1 * * 6'
8 |
9 | jobs:
10 | # This is mirrored in the release workflow.
11 | build_and_test:
12 | name: 'Build and Test'
13 | uses: StackStorm-Exchange/ci/.github/workflows/pack-build_and_test.yaml@master
14 | with:
15 | enable-common-libs: true
16 | #apt-cache-version: v0
17 | #py-cache-version: v0
18 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | # the default branch
7 | - master
8 |
9 | permissions:
10 | contents: write
11 |
12 | jobs:
13 | # This mirrors build_and_test workflow
14 | build_and_test:
15 | name: 'Build and Test'
16 | uses: StackStorm-Exchange/ci/.github/workflows/pack-build_and_test.yaml@master
17 | with:
18 | enable-common-libs: true
19 | #apt-cache-version: v0
20 | #py-cache-version: v0
21 |
22 | tag_release:
23 | needs: build_and_test
24 | name: Tag Release
25 | uses: StackStorm-Exchange/ci/.github/workflows/pack-tag_release.yaml@master
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | ./lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Flask stuff:
57 | instance/
58 | .webassets-cache
59 |
60 | # Scrapy stuff:
61 | .scrapy
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # IPython Notebook
70 | .ipynb_checkpoints
71 |
72 | # pyenv
73 | .python-version
74 |
75 | # celery beat schedule file
76 | celerybeat-schedule
77 |
78 | # dotenv
79 | .env
80 |
81 | # virtualenv
82 | venv/
83 | ENV/
84 |
85 | # Spyder project settings
86 | .spyderproject
87 |
88 | # Rope project settings
89 | .ropeproject
90 |
91 | # Auto generated files by Bundler and Rspec
92 | Gemfile.lock
93 | .rspec
94 |
95 | # temporary files of Vim
96 | .*.sw*
97 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 1.2.4
4 | ### Updated
5 | - Updated files to work with latest CI updates
6 |
7 | ## 1.2.3
8 | ### Updated
9 | - Updated py-zabbix module because the Zabbix API recently changed their login parameters
10 |
11 | ## 1.2.2
12 | ### Added
13 | - `actions/host_get_hostgroups` - Gets/Checks the hostgroups that a given Zabbix host is in
14 |
15 | ## 1.2.1
16 | ### Added
17 | - `actions/workflows/host_get_active_triggers` - Added parameter, 'priority', which allows active triggers to be filtered by severity (priority) level
18 |
19 | ## 1.2.0
20 | ### Added
21 | - `actions/workflows/host_get_active_triggers` - Gets active triggers for a given Zabbix host
22 |
23 | ## 1.1.0
24 |
25 | ### Added
26 | - `actions/host_get_alerts` - Gets alerts for a given Zabbix host
27 | - `actions/host_get_events` - Gets events for a given Zabbix host
28 | - `actions/host_get_triggers` - Gets triggers for a given Zabbix host
29 |
30 | ## 1.0.0
31 |
32 | - Drop python 2.7 support.
33 |
34 | ## 0.4.0
35 |
36 | ### Added
37 | - `actions/host_get_interfaces.yaml` - Gets the interfaces of one or more Zabbix Hosts
38 |
39 | ### Updated
40 | - `actions/update_host.yaml` - Added some of the default host properties from the following link to allow them to be changed as well
41 | https://www.zabbix.com/documentation/5.0/manual/api/reference/host/object
42 |
43 | ## 0.3.2
44 |
45 | - Extend `list_hosts` action to allow specifying a list of groupids (not possible in `filter`), as well as `output` to allow trimming of returned data.
46 |
47 | ## 0.3.1
48 |
49 | ### Added
50 |
51 | - Explicitly specify supported Python versions
52 |
53 | ### Changed
54 |
55 | - Fixups for Python 3
56 |
57 | ## 0.3.0
58 |
59 | ### Added
60 |
61 | - `actions/call_api.py` - A primitive pimplemenetation of `python-script` to send a request to specified API endpoint with other specified parameters.
62 | - `actions/list_host_groups.yaml` - List all host_groups objects which are registered in Zabbix
63 | - `actions/list_host_interfaces.yaml` - List all hostinterfaces objects which are registered in Zabbix
64 | - `actions/list_hosts.yaml` - List all host objects which are registered in Zabbix
65 | - `actions/list_templates.yaml` - List all templates objects which are registered in Zabbix
66 | - `actions/update_host.yaml` - A primitive action to update host information
67 |
68 | ## 0.2.0
69 |
70 | ### Added
71 |
72 | - `actions/create_host.yaml` - register a new host object to zabbix server.
73 |
74 | ## 0.1.11
75 |
76 | ### Changes
77 |
78 | - `triggers/event_handler.yaml` - `alert_message` type updated to include `array`, `object`
79 | with logic in `st2_dispatch.py` to handle the difference.
80 | - `st2_dispatch.py` flags: `--st2-api-url` and `--st2-auth-url` no longer have default values.
81 | See code comments for details.
82 |
83 | ### Additions
84 |
85 | - API Keys can now be used to authenticate to the ST2 API. Please see the 'Advanced Usage'
86 | section in the readme for more details.
87 | - `--api-key` flag can be passed to `st2_dispatch.py` to test usage with an API Key for authentication
88 | - `st2_dispatch.py` can now send to a user defined trigger, but defaults to `zabbix.event_handler`
89 | - The value of `alert_message` passed in from the Zabbix macro `{ALERT.MESSAGE}` will now be evaluated
90 | to determine if its a JSON valid List or Dict, or a string, and passed accordingly.
91 | - When using a JSON Dict to pass auth parameters, keys and values passed are parsed as is into options
92 | that can then be used in later logic.
93 | This means you can pass any valid option as a key:val pair (`st2_userid`, `st2_passwd`, `trigger`, etc)
94 | - This excludes `alert_sendto`, `alert_subject`, `alert_message` as they are parsed after the JSON Dict
95 |
96 | ### Fixes
97 |
98 | - Corrected a bug in `ack_event.yaml` where `enum:` was applied with `type: boolean`. Fixes #32
99 | - `host_get_multiple_ids` - `type:` is now `array` (was `string`). Fixes #22
100 |
101 | ## 0.1.10
102 |
103 | - The script that registers st2's configuration to Zabbix would be compatible with Zabbix v4.0.
104 | - An integration test for `register_st2_config_to_zabbix.py` with different version of Zabbix was added.
105 | - A configuration file of docker-compose was added for running integration test in local machine.
106 |
107 | ## 0.1.9
108 |
109 | - Version bump to fix tagging issue. No code change
110 |
111 | ## 0.1.8
112 |
113 | ### Added
114 |
115 | - New Action: `host_get_inventory` - Get the inventory of one or more Zabbix Hosts
116 |
117 | ## 0.1.7
118 |
119 | ### Changed
120 |
121 | - Changed MediaType parameter of StackStorm to access through the API endpoints instead of accessing
122 | directly to each service processes.
123 | - Changed `st2_dispatch.py` to conform to be able to communicate with up to date st2api (v2.8).
124 |
125 | **Note:** This change is backward incompatible -- the interface of `st2_dispatch.py` is changed from
126 | `--st2-host` to `--st2-api-url` and `--st2-auth-url` to be able to dispach trigger through proxy.
127 | To update from previous version to this, you should execute `register_st2_config_to_zabbix.py` command.
128 | This resets configuration of Zabbix for conforming to the interface of current `st2_dispatch.py`
129 | to dispatch `zabbix.event_handler`.
130 |
131 | ### Fixed
132 |
133 | - Fixed typos in README and ported images from persoanl repository of a contributor
134 | - Fixed to be able to overwrite Zabbix configuration for StackStorm by `register_st2_config_to_zabbix.py`
135 |
136 |
137 | ## 0.1.6
138 |
139 | - Fixed typo in `tools/scripts/st2_dispatch.py`.
140 | Contributed by Nick Maludy (Encore Technologies)
141 |
142 | ## 0.1.5
143 |
144 | - Added a new action `host_get_multiple_ids` that can retrieve 0, a single, or multiple zabbix hosts and
145 | return those as an array. This is for a race condition that exists when using several zabbix proxies.
146 | - Added a new action `host_delete_by_id` that allows a host to be deleted given the Host's ID instead of
147 | the Host's Name
148 | Contributed by Brad Bishop (Encore Technologies)
149 |
150 | ## 0.1.4
151 |
152 | - Added a new action `host_get_status` that retrieves the status of a host in Zabbix.
153 | Contributed by Brad Bishop (Encore Technologies)
154 |
155 | ## 0.1.3
156 |
157 | - Added a new action `test_credentials` that tests if the credentials in the config are valid.
158 | Contributed by Nick Maludy (Encore Technologies)
159 |
160 | ## 0.1.2
161 |
162 | - Added base action class with common code. Removed duplicate code from other actions
163 | - Added host_get_id action action to return the id of a Zabbix Host
164 | - Added host_update_status action to change the status of a Zabbix Host
165 | - Added host_delete action to delete a Zabbix Host
166 | - Added maintenance_create_or_update action to create a maintenance window or update one if it already exists
167 | - Added maintenance_delete action to delete a maintenance window
168 |
169 | Contributed by Brad Bishop (Encore Technologies)
170 |
171 | ## 0.1.1
172 |
173 | - Set a secret option to the password property in the config.schema.yaml
174 |
175 | ## 0.1.0
176 |
177 | - Initial release
178 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem 'docker-api'
4 | gem 'rake'
5 | gem 'serverspec'
6 | gem 'zbxapi'
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zabbix Integration Pack
2 | This pack provides capabilities for working with Zabbix - both receiving events and responding to them, and actions for querying Zabbix, managing hosts and maintenance, etc. This pack configures Zabbix to dispatch event to the Trigger `zabbix.event_handler` when Zabbix raises an alert.
3 |
4 | This README explains how this integration works, and how to configure it.
5 |
6 | 
7 |
8 | # Requirements
9 |
10 | * Zabbix >3.0. It has been tested with v3.0, v3.2 and v4.0.
11 |
12 | # Installation
13 | Install the pack:
14 |
15 | ```shell
16 | $ st2 pack install zabbix
17 | ```
18 |
19 | Configure Zabbix to dispatch the "zabbix.event_handler" trigger, using the `/opt/stackstorm/packs/zabbix/tools/register_st2_config_to_zabbix.py` command.
20 |
21 | Usage:
22 |
23 | ```shell
24 | Usage: register_st2_config_to_zabbix.py [options]
25 |
26 | Options:
27 | -h, --help show this help message and exit
28 | -z Z_URL, --zabbix-url=Z_URL
29 | The URL of Zabbix Server
30 | -u Z_USERID, --username=Z_USERID
31 | Login username to login Zabbix Server
32 | -p Z_PASSWD, --password=Z_PASSWD
33 | Password which is associated with the username
34 | -s Z_SENDTO, --sendto=Z_SENDTO
35 | Address, user name or other identifier of the
36 | recipient
37 | ```
38 |
39 | Example execution:
40 |
41 | ```shell
42 | $ /opt/stackstorm/virtualenvs/zabbix/bin/python /opt/stackstorm/packs/zabbix/tools/register_st2_config_to_zabbix.py -z http://zabbix-host/zabbix -u Admin -p zabbix
43 | ```
44 |
45 | NOTE: It's important you use ``python`` binary from the pack virtual environment (``/opt/stackstorm/virtualenvs/zabbix/bin/python``)
46 | and not the system one. If you use system Python binary you will see error similar to ``ImportError: No module named zabbix.api``.
47 |
48 | This will register a new MediaType (`StackStorm`) to dispatch events and add an associated action (`Dispatching to StackStorm`).
49 |
50 | When you create a new Zabbix-Trigger and link it to the Action, StackStorm will accept the message from Zabbix.
51 |
52 | ## Zabbix configuration
53 |
54 | ### MediaType for the StackStorm
55 | After executing the `register_st2_config_to_zabbix.py` command, you can notice that new MediaType `StackStorm` is added on `Media types` page (go to `Administration` > `MediaType`). You also have to this configuration to send a request for dispatching trigger to StackStorm when Zabbix server detect an alert. Please click the `StackStorm` mediatype.
56 | 
57 |
58 | You see following page, and you have to fill out with parameters for your st2 environment (the endpoint URLs of st2-api and st2-auth, and authentication information).
59 | 
60 |
61 | You can specify additional parameters and you can handle them from the payload of the StackStorm's Trigger(`zabbix.event_handler`).
62 |
63 | ### Deploy the AlertScript
64 |
65 | The script `st2_dispatch.py` sends Zabbix events to the StackStorm server. Copy this script to the directory which Zabbix MediaType refers to. The directory is specified by the parameter of `AlertScriptsPath` in the Zabbix configuration file on the node which zabbix was installed.
66 | ```shell
67 | $ grep 'AlertScriptsPath' /etc/zabbix/zabbix_server.conf
68 | ### Option: AlertScriptsPath
69 | # AlertScriptsPath=${datadir}/zabbix/alertscripts
70 | AlertScriptsPath=/usr/lib/zabbix/alertscripts
71 | ```
72 |
73 | This pack requires you to deploy this `st2_dispatch.py` in its directory (and setup executional environment if necessary) on the Zabbix installed node. Set it up depending on your environment as below:
74 |
75 | #### Case: single node
76 |
77 | Both of StackStorm and Zabbix are installed on the same system:
78 |
79 |
80 |
81 | This case is quite simple. All you have to do is copy `st2_dispatch.py` to the directory which AlertScripts should be located.
82 | ```shell
83 | $ sudo cp /opt/stackstorm/packs/zabbix/tools/scripts/st2_dispatch.py /usr/lib/zabbix/alertscripts/
84 | ```
85 |
86 | #### Case: multiple nodes
87 |
88 | Zabbix and StackStorm are installed on separate systems, with IP connectivity between them:
89 |
90 |
91 |
92 | In this case, you have to do two things (deploying and making executional environment) to set it up. First copy `st2_dispatch.py` from the StackStorm server to the AlertScript directory on the Zabbix node.
93 |
94 | ```shell
95 | ubuntu@zabbix-node:~$ scp st2-node:/opt/stackstorm/packs/zabbix/tools/scripts/st2_dispatch.py ./
96 | ubuntu@zabbix-node:~$ sudo mv st2_dispatch.py /usr/lib/zabbix/alertscripts/
97 | ```
98 |
99 | Then, you have to setup executional environment for this script. In an Ubuntu environment, you can do it as below (If you use some other GNU/Linux distribution, please substitute the proper commands to install Python and PIP which is the package manager of Python).
100 | ```shell
101 | ubuntu@zabbix-node:~$ sudo apt-get install python python-pip
102 | ```
103 |
104 | After installing Python and PIP, you should install dependent packages for this AlertScript by with `pip`:
105 | ```shell
106 | ubuntu@zabbix-node:~$ sudo pip install st2client
107 | ```
108 |
109 | Now verify the configuration. Please substibute described parameters with proper ones for your environment.
110 | ```shell
111 | ubuntu@zabbix-node:~$ /usr/lib/zabbix/alertscripts/st2_dispatch.py \
112 | > --st2-userid=st2admin \
113 | > --st2-passwd=passwd \
114 | > --st2-api-url=https://st2-node/api \
115 | > --st2-auth-url=https://st2-node/auth
116 | ```
117 |
118 | If it goes well, you can verify the Trigger `zabbix.event_handler` was dispatched on the st2-node.
119 | ```shell
120 | ubuntu@st2-node:~$ st2 trigger-instance list -n1
121 | +--------------------------+----------------------+-------------------------------+-----------+
122 | | id | trigger | occurrence_time | status |
123 | +--------------------------+----------------------+-------------------------------+-----------+
124 | | 5b8d1be547d0e404bffd99e3 | zabbix.event_handler | Mon, 03 Sep 2018 11:34:24 UTC | processed |
125 | +--------------------------+----------------------+-------------------------------+-----------+
126 | +---------------------------------------------------------------------------------------------+
127 | | Note: Only one triggerinstance is displayed. Use -n/--last flag for more results. |
128 | +---------------------------------------------------------------------------------------------+
129 | ubuntu@st2-node:~$ st2 trigger-instance get 5b8d1be547d0e404bffd99e3
130 | +-----------------+-----------------------------+
131 | | Property | Value |
132 | +-----------------+-----------------------------+
133 | | id | 5b8d1be547d0e404bffd99e3 |
134 | | trigger | zabbix.event_handler |
135 | | occurrence_time | 2018-09-03T11:32:53.943000Z |
136 | | payload | { |
137 | | | "alert_sendto": "", |
138 | | | "extra_args": [], |
139 | | | "alert_message": "", |
140 | | | "alert_subject": "" |
141 | | | } |
142 | | status | processed |
143 | +-----------------+-----------------------------+
144 | ```
145 |
146 | ### Action
147 | You can link arbitrary Trigger (of Zabbix) to the action (`Dispatching to StackStorm`) which is registered by the setup command like this.
148 | 
149 | 
150 |
151 | By this setting, Zabbix will dispatch event to StackStorm when the registered trigger makes an alert.
152 |
153 | # Triggers
154 |
155 | ## zabbix.event_handler
156 | This trigger has these parameters:
157 |
158 | | Parameter | Description of context |
159 | |:--------------|:-----------------------|
160 | | alert_sendto | describe value from user media configuration of Zabbix |
161 | | alert_subject | describe status and name of Zabbix Trigger which raises an alert |
162 | | alert_message | describe detail of alert (see following) |
163 | | extra_args | describe optional user-defined values (default is `[]`) |
164 |
165 | In the `alert_message` parameter, the value will be reflective of how it was structured in zabbix.
166 | With the default configuration of 'Default message' by `register_st2_config_to_zabbix.py`
167 |
168 | | Parameter of `alert_message` | Description of context |
169 | |:-----------------------------|:-----------------------|
170 | | ['event']['id'] | Numeric ID of the event that triggered an action of Zabbix |
171 | | ['event']['time'] | Time of the event that triggered an action of Zabbix |
172 | | ['trigger']['id'] | Numeric trigger ID which triggered this action of Zabbix |
173 | | ['trigger']['name'] | Name of the trigger of Zabbix |
174 | | ['trigger']['status'] | Current trigger value of Zabbix. Can be either PROBLEM or OK |
175 | | ['items'][0~9] | `Array` type value to have following `Dict` type informations, and the length of it is fixed to 10 by Zabbix |
176 | | ['items'][0~9]['name'] | Name of trigger setting which alert raises |
177 | | ['items'][0~9]['host'] | Hstname which alert raises |
178 | | ['items'][0~9]['key'] | Key name to retrieve value |
179 | | ['items'][0~9]['value'] | Value which make alert raises |
180 |
181 | (These configuration values are corresponding to [the Macros of Zabbix](https://www.zabbix.com/documentation/3.2/manual/appendix/macros/supported_by_location))
182 |
183 | You can also modify 'Default message' in the 'Operations' tab of your 'Action' to be structured as a JSON Array, JSON Dict, or a string, and the trigger will receive it that way.
184 |
185 | # StackStorm Configuration
186 | You need to set configure the Zabbix pack before running actions:
187 |
188 | | Configuration Param | Description | Default |
189 | |:--------------------|:------------|:--------|
190 | | url | Zabbix login URL | http://localhost/zabbix |
191 | | username | Login usernmae | Admin |
192 | | password | Password of `username` | zabbix |
193 |
194 | # Action
195 | | Reference of the Action | Description |
196 | |:--------------------------------------|:------------|
197 | | zabbix.ack_event | Send acknowledgement message for an event to Zabbix and if Zabbix may close it |
198 | | zabbix.host_delete | Delete a Zabbix Host |
199 | | zabbix.host_delete_by_id | Delete a Zabbix Host by it's Id |
200 | | zabbix.host_get_alerts | Get events for a given Zabbix host |
201 | | zabbix.host_get_events | Get events for a given Zabbix host |
202 | | zabbix.host_get_hostgroups | Get/Check the hostgroups of a Zabbix Host |
203 | | zabbix.host_get_id | Get the ID of a Zabbix Host |
204 | | zabbix.host_get_inventory | Get the inventory of one or more Zabbix Hosts |
205 | | zabbix.host_get_multiple_ids | Get the IDs of multiple Zabbix Hosts |
206 | | zabbix.host_get_status | Get the status of a Zabbix Host |
207 | | zabbix.host_get_triggers | Get triggers for a given Zabbix host |
208 | | zabbix.host_get_active_triggers | Get active triggers for a given Zabbix host |
209 | | zabbix.host_update_status | Update the status of a Zabbix Host |
210 | | zabbix.maintenance_create_or_update | Create or update Zabbix Maintenance Window |
211 | | zabbix.maintenance_delete | Delete Zabbix Maintenance Window |
212 | | zabbix.test_credentials | Tests if it credentials in the config are valid |
213 |
214 | # Running Test
215 | ## Unit Test
216 | You can run unit tests by `st2-run-pack-tests` command that is provided by [StackStorm](https://github.com/StackStorm/st2) as below.
217 |
218 | ```
219 | $ git clone git@github.com:StackStorm-Exchange/stackstorm-zabbix.git
220 | $ git clone git@github.com:StackStorm/st2.git
221 | $ st2/st2common/bin/st2-run-pack-tests -x -p ~/stackstorm-zabbix/
222 | ```
223 |
224 | For more detail on this topic, please see the [official document page](https://docs.stackstorm.com/development/pack_testing.html).
225 |
226 | ## Integration Test
227 | You can also run test with actual Zabbix server and Zabbix API server in your local environment using Zabbix Docker Images ([zabbix-server-mysql](https://hub.docker.com/r/zabbix/zabbix-server-mysql) and [zabbix-web-nginx-mysql](https://hub.docker.com/r/zabbix/zabbix-web-nginx-mysql)) and [Serverspec](https://serverspec.org/). This describes how to run the integration tests.
228 |
229 | ### 0. Preparing for running RSpec tests
230 | For the first time in your environment to run this test, it's necessary to make an environment for RSpec as below.
231 | ```
232 | $ cd stackstorm-zabbix
233 | $ gem install bundler
234 | $ bundle install
235 | ```
236 | To make this environment by this procedure, you have to install Ruby (`v2.4` or later).
237 |
238 | ### 1. Running Docker images for Zabbix
239 | You can run Zabbix services (Zabbix server and Zabbix Web API) for the integration test so quickly using Docker. To run these containers you should specify the environment variable of TAG which means Zabbix version of container to start.
240 | This command starts Docker containers of both Zabbix services which are `v3.2`.
241 |
242 | ```
243 | $ TAG=ubuntu-3.2-latest docker-compose up -d
244 | ```
245 |
246 | When you want to start Zabbix v4.0 containers, you can do it like this.
247 |
248 | ```
249 | $ TAG=ubuntu-4.0-latest docker-compose up -d
250 | ```
251 |
252 | All values you could specify in this variable is [here](https://hub.docker.com/r/zabbix/zabbix-server-mysql/tags).
253 |
254 | ### 2. Running tests
255 | Starting procedure to run the test is also simple, all you have to do is executing rspec as below.
256 |
257 | ```
258 | $ bundle exec rspec
259 | ```
260 |
261 | # Advanced Usage
262 | If you would prefer to use an API Key for auth in place of user/pass, you can do so by passing a JSON Dict as the first positional argument in your `Media Type` in place of:
263 | ```
264 | https://st2-node/api/v1
265 | https://st2-node/auth/v1
266 | st2user
267 | st2pass
268 | ```
269 | ### Valid Keys
270 | This dict has the following valid keys
271 | - `st2_userid`
272 | - `st2_passwd`
273 | - `api_url`
274 | - `auth_url`
275 | - `api_key`
276 | - `trigger`
277 | - `skip_config`
278 | - `config_file`
279 |
280 | `api_url` is always required
281 | `auth_url` is only required when using `st2_userid` and `st2_passwd`
282 | `api_key` will cause `st2_userid` and `st2_passwd` to be ignored (API Key prefered)
283 | `trigger` allows you to specify your own trigger on st2 to send messages to. Default is `zabbix.event_handler`
284 |
285 | ### JSON Examples
286 | API Key for Auth - `{"api_url":"https://stackstorm.yourdomain.com/api/v1", "api_key":"aaabbbccc111222333"}`
287 | User/Pass for auth - `{"api_url":"https://stackstorm.yourdomain.com/api/v1", "auth_url":"https://stackstorm.yourdomain.com/auth", "st2_userid":"st2admin", "st2_passwd":"st2pass"}`
288 | API Key and send to custom trigger - `{"api_url":"https://stackstorm.yourdomain.com/api/v1", "api_key":"aaabbbccc111222333", "trigger": "pack.my_custom_trigger"}`
289 |
290 | 
291 |
292 | # Zabbix Gotcha's
293 |
294 | #### Max 255 total parameter characters per Media Type
295 | (Zabbix 3.4) Zabbix has a default limitation of 255 characters that can be stored cumulatively for media type parameters. This is due to the default setting in the database column `exec_params` of `varchar(255)`. Modify this at your own risk.
296 |
297 | #### Media Type Parameter serialization
298 | (Zabbix 3.4) When you save the parameters for your media type, Zabbix serializes them into a single string, and stores them in the database under `exec_params`
299 |
300 | #### Media Type Parameter line endings
301 | (Zabbix 3.4) When parameters are serialized, they are delimited by a single newline (LF) character (\n). Specifically it is not CRLF (\r\n).
302 | This means when your parameters are serialized, there is +n characters against the 255 limit, where n = number of parameters. (one \n per parameter)
303 |
304 | #### Media Type Parameter de-serialization
305 | (Zabbix 3.4) When Zabbix calls and executes a script for a Media Type, it takes the serialized string of parameters, and passes them to the script as individual strings with newline characters at the end.
306 |
307 | ##### Literal representation
308 | ```shell
309 | $./st2_dispatch.py 'first parameter' \
310 | 'second parameter ' \
311 | 'third parameter'
312 | ```
313 |
314 | This is why you can't input `--flag value` as a parameters, because its passed literally as `'--flag value'\n`
315 |
316 | #### Relationship of Zabbix Functions
317 | Zabbix's dependencies for the various parts that go into doing something simple as "tell me when a device goes down" can be confusing, so here's a diagram.
318 |
319 | 
320 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake'
2 | require 'rspec/core/rake_task'
3 |
4 | task :spec => 'spec:all'
5 | task :default => :spec
6 |
7 | namespace :spec do
8 | targets = []
9 | Dir.glob('./spec/*').each do |dir|
10 | next unless File.directory?(dir)
11 | target = File.basename(dir)
12 | target = "_#{target}" if target == "default"
13 | targets << target
14 | end
15 |
16 | task :all => targets
17 | task :default => :all
18 |
19 | targets.each do |target|
20 | original_target = target == "_default" ? target[1..-1] : target
21 | desc "Run serverspec tests to #{original_target}"
22 | RSpec::Core::RakeTask.new(target.to_sym) do |t|
23 | ENV['TARGET_HOST'] = original_target
24 | t.pattern = "spec/#{original_target}/*_spec.rb"
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/actions/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/actions/.gitkeep
--------------------------------------------------------------------------------
/actions/ack_event.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: ack_event
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Send acknowledgement message for an event to Zabbix and if Zabbix may close it
6 | enabled: true
7 | entry_point: event_action_runner.py
8 | parameters:
9 | eventid:
10 | type: string
11 | required: True
12 | message:
13 | type: string
14 | required: True
15 | will_close:
16 | type: boolean
17 | required: False
18 | default: True
19 | action:
20 | type: string
21 | immutable: true
22 | default: event.acknowledge
23 |
--------------------------------------------------------------------------------
/actions/call_api.py:
--------------------------------------------------------------------------------
1 | from lib.actions import ZabbixBaseAction
2 | from zabbix.api import ZabbixAPI
3 |
4 |
5 | class CallAPI(ZabbixBaseAction):
6 | def run(self, api_method, token, **params):
7 | # Initialize client object to connect Zabbix server
8 |
9 | if token:
10 | self.client = ZabbixAPI(url=self.config['zabbix']['url'])
11 | self.auth = token
12 | else:
13 | self.connect()
14 |
15 | return self._call_api_method(self.client, api_method,
16 | {k: v for k, v in params.items() if v is not None}) # dont include param where v=None
17 |
18 | def _call_api_method(self, client, api_method, params):
19 | """
20 | Most of method of Zabbix API consist of a couple of attributes (e.g. "host.get").
21 | This method unties each attribute and validate it.
22 | """
23 | if '.' in api_method:
24 | return self._call_api_method(self._get_client_attr(client, api_method.split('.')[0]),
25 | '.'.join(api_method.split('.')[1:]), params)
26 |
27 | # This sends a request to Zabbix server
28 | return self._get_client_attr(client, api_method)(**params)
29 |
30 | def _get_client_attr(self, parent_object, attribute):
31 | if not hasattr(parent_object, attribute):
32 | raise RuntimeError("Zabbix client does not have a '%s' method", attribute)
33 |
34 | return getattr(parent_object, attribute)
35 |
--------------------------------------------------------------------------------
/actions/create_host.py:
--------------------------------------------------------------------------------
1 | from lib.actions import ZabbixBaseAction
2 | from zabbix.api import ZabbixAPI
3 |
4 |
5 | class CreateHost(ZabbixBaseAction):
6 | def get_interface_config(self, ipaddr='', domain='', port="10050", is_main=False):
7 | return {
8 | "type": 1,
9 | "main": 1 if is_main else 0,
10 | "useip": 1 if ipaddr else 0,
11 | "dns": domain,
12 | "ip": ipaddr,
13 | "port": port,
14 | }
15 |
16 | def get_interface_config_with_domain(self, domains, main_if):
17 | return [self.get_interface_config(domain=x, is_main=(x == main_if)) for x in domains]
18 |
19 | def get_interface_config_with_ipaddr(self, ipaddrs, main_if):
20 | return [self.get_interface_config(ipaddr=x, is_main=(x == main_if)) for x in ipaddrs]
21 |
22 | def set_proxy_for_host(self, proxy_name, new_hosts):
23 | for proxy in self.client.proxy.get(filter={'host': proxy_name}):
24 | current_hosts = [x['hostid'] for x in self.client.host.get(proxyids=[proxy['proxyid']])]
25 |
26 | return self.client.proxy.update(**{
27 | 'proxyid': proxy['proxyid'],
28 | 'hosts': current_hosts + new_hosts,
29 | })
30 |
31 | def run(self, name, groups, ipaddrs=[], domains=[], proxy_host=None, token=None, main_if=''):
32 | # Initialize client object to connect Zabbix server
33 | if token:
34 | self.client = ZabbixAPI(url=self.config['zabbix']['url'])
35 | self.auth = token
36 | else:
37 | self.connect()
38 |
39 | # retrieve hostgroup-ids to be set to creating host object
40 | hostgroups = [x['groupid'] for x in self.client.hostgroup.get(filter={'name': groups})]
41 |
42 | # make interface configurations to be set to creating host object
43 | interfaces = (self.get_interface_config_with_ipaddr(ipaddrs, main_if) +
44 | self.get_interface_config_with_domain(domains, main_if))
45 |
46 | # Zabbix server requires one interface value at least
47 | if not interfaces:
48 | return (False, "You have to IP address or domain value at least one.")
49 |
50 | # If there is no main interface, set it for the first one.
51 | if not any([x['main'] > 0 for x in interfaces]):
52 | interfaces[0]['main'] = 1
53 |
54 | # register a host object
55 | new_host = self.client.host.create(**{
56 | 'host': name,
57 | 'groups': [{'groupid': x} for x in hostgroups],
58 | 'interfaces': interfaces,
59 | })
60 |
61 | # register ZabbixProxy if it is necessary
62 | if proxy_host:
63 | self.set_proxy_for_host(proxy_host, new_host['hostids'])
64 |
65 | return (True, new_host)
66 |
--------------------------------------------------------------------------------
/actions/create_host.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: create_host
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Create a new host to Zabbix Server
6 | enabled: true
7 | entry_point: create_host.py
8 | parameters:
9 | name:
10 | type: string
11 | description: Hostname to be created
12 | required: True
13 | groups:
14 | type: array
15 | description: HostGroups to be registered to creating host
16 | required: True
17 | ipaddrs:
18 | type: array
19 | description: IP addresses of the host in which ZabbixAgent is installed
20 | default: []
21 | domains:
22 | type: array
23 | description: Domain names of the host in which ZabbixAgent is installed
24 | default: []
25 | main_if:
26 | type: string
27 | description: Default ZabbixAgent interface of IP address or domain-name
28 | proxy_host:
29 | type: string
30 | description: Proxy host's name which is registered in ZabbixServer
31 | token:
32 | type: string
33 | description: Encrypted access token to authenticate to ZabbixServer
34 | default: |
35 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
36 | secret: true
37 |
--------------------------------------------------------------------------------
/actions/event_action_runner.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 |
18 |
19 | class EventActionRunner(ZabbixBaseAction):
20 | def run(self, action, *args, **kwargs):
21 | self.connect()
22 |
23 | if action == 'event.acknowledge':
24 | kwargs = self.reconstruct_args_for_ack_event(*args, **kwargs)
25 |
26 | try:
27 | api_handler = self.client
28 | for obj in action.split('.'):
29 | api_handler = getattr(api_handler, obj)
30 |
31 | return (True, api_handler(*args, **kwargs))
32 | except AttributeError:
33 | return (False, "Specified action(%s) is invalid" % action)
34 |
--------------------------------------------------------------------------------
/actions/host_delete.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class HostDelete(ZabbixBaseAction):
21 | def run(self, host=None, host_id=None):
22 | """ Deletes a Zabbix Host.
23 | """
24 | self.connect()
25 |
26 | if not host_id:
27 | host_id = self.find_host(host)
28 |
29 | try:
30 | self.client.host.delete(host_id)
31 | return True
32 | except ZabbixAPIException as e:
33 | raise ZabbixAPIException("There was a problem deleting the host: {0}".format(e))
34 |
--------------------------------------------------------------------------------
/actions/host_delete.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_delete
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Delete a Zabbix Host
6 | enabled: true
7 | entry_point: host_delete.py
8 | parameters:
9 | host:
10 | type: string
11 | description: "Name of the Zabbix Host"
12 | required: True
13 |
--------------------------------------------------------------------------------
/actions/host_delete_by_id.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_delete_by_id
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Delete a Zabbix Host by it's Id
6 | enabled: true
7 | entry_point: host_delete.py
8 | parameters:
9 | host_id:
10 | type: string
11 | description: "Id of the Zabbix Host"
12 | required: True
13 |
--------------------------------------------------------------------------------
/actions/host_get_active_triggers.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | description: "List all active triggers for a given host"
3 | enabled: true
4 | runner_type: orquesta
5 | entry_point: workflows/host_get_active_triggers.yaml
6 | name: host_get_active_triggers
7 | pack: zabbix
8 | parameters:
9 | host:
10 | type: string
11 | description: "Name of the Zabbix Host"
12 | required: True
13 | priority:
14 | type: array
15 | description: "List of priority numbers (severity) to get triggers for"
16 | required: False
17 | default: []
--------------------------------------------------------------------------------
/actions/host_get_alerts.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_alerts
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all alerts for a given host ID in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | expandDescription:
10 | type: string
11 | description: "test flag"
12 | default: ""
13 | immutable: true
14 | filter:
15 | type: object
16 | description: Condition to filter the result
17 | token:
18 | type: string
19 | description: Encrypted access token to authenticate to ZabbixServer
20 | default: |
21 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
22 | secret: true
23 | api_method:
24 | default: alert.get
25 | immutable: true
26 |
--------------------------------------------------------------------------------
/actions/host_get_events.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_events
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all events for a given host in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | expandDescription:
10 | type: string
11 | description: "test flag"
12 | default: ""
13 | immutable: true
14 | filter:
15 | type: object
16 | description: Condition to filter the result
17 | token:
18 | type: string
19 | description: Encrypted access token to authenticate to ZabbixServer
20 | default: |
21 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
22 | secret: true
23 | api_method:
24 | default: event.get
25 | immutable: true
26 |
--------------------------------------------------------------------------------
/actions/host_get_hostgroups.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class HostGetHostGroups(ZabbixBaseAction):
21 |
22 | def run(self, host_id, group_id):
23 | """ Gets the hostgroups of one or more Zabbix Hosts.
24 | """
25 | self.connect()
26 |
27 | # Find hostgroups by host ids
28 | try:
29 | hostgroups = self.client.host.get(
30 | hostids=host_id, selectGroups='extend', output=['hostid', 'groups'])
31 | except ZabbixAPIException as e:
32 | raise ZabbixAPIException(("There was a problem searching for the host: "
33 | "{0}".format(e)))
34 |
35 | # if group ids are passed in we check to see if the host is a part of said groups
36 | if group_id:
37 | for group in hostgroups[0]["groups"]:
38 | if group["groupid"] == group_id:
39 | return hostgroups
40 |
41 | return (False, hostgroups)
42 | # otherwise just return the groups the host is in
43 | else:
44 | return hostgroups
45 |
--------------------------------------------------------------------------------
/actions/host_get_hostgroups.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_hostgroups
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Gets/Checks the hostgroups of one or more Zabbix Hosts
6 | enabled: true
7 | entry_point: host_get_hostgroups.py
8 | parameters:
9 | group_id:
10 | type: string
11 | description: "Optional Group ID to check if host is in"
12 | required: False
13 | host_id:
14 | type: string
15 | description: "Host ID to find hostgroups for"
16 | required: True
--------------------------------------------------------------------------------
/actions/host_get_id.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 |
18 |
19 | class HostGetID(ZabbixBaseAction):
20 | def run(self, host=None):
21 | """ Gets the ID of the Zabbix host given the Hostname or FQDN
22 | of the Zabbix host.
23 | """
24 | self.connect()
25 |
26 | host_id = self.find_host(host)
27 |
28 | return host_id
29 |
--------------------------------------------------------------------------------
/actions/host_get_id.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_id
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Get the ID of a Zabbix Host
6 | enabled: true
7 | entry_point: host_get_id.py
8 | parameters:
9 | host:
10 | type: string
11 | description: "Name of the Zabbix Host"
12 | required: True
13 |
--------------------------------------------------------------------------------
/actions/host_get_interfaces.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class HostGetInterfaces(ZabbixBaseAction):
21 |
22 | def run(self, host_ids=None):
23 | """ Gets the interfaces of one or more Zabbix Hosts.
24 | """
25 | self.connect()
26 |
27 | # Find interfaces by host ids
28 | try:
29 | interfaces = self.client.host.get(
30 | hostids=host_ids, selectInterfaces='extend', output=['hostid', 'interfaces'])
31 | except ZabbixAPIException as e:
32 | raise ZabbixAPIException(("There was a problem searching for the host: "
33 | "{0}".format(e)))
34 |
35 | return interfaces
36 |
--------------------------------------------------------------------------------
/actions/host_get_interfaces.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_interfaces
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Get the interfaces of one or more Zabbix Hosts
6 | enabled: true
7 | entry_point: host_get_interfaces.py
8 | parameters:
9 | host_ids:
10 | type: array
11 | description: "List of Host IDs to find inventory items for"
12 | required: True
--------------------------------------------------------------------------------
/actions/host_get_inventory.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class HostGetInventory(ZabbixBaseAction):
21 |
22 | def run(self, host_ids=None):
23 | """ Gets the inventory of one or more Zabbix Hosts.
24 | """
25 | self.connect()
26 |
27 | # Find inventory by host ids
28 | try:
29 | inventory = self.client.host.get(
30 | hostids=host_ids, selectInventory='extend', output=['hostid', 'inventory'])
31 | except ZabbixAPIException as e:
32 | raise ZabbixAPIException(("There was a problem searching for the host: "
33 | "{0}".format(e)))
34 |
35 | return inventory
36 |
--------------------------------------------------------------------------------
/actions/host_get_inventory.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_inventory
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Get the inventory of one or more Zabbix Hosts
6 | enabled: true
7 | entry_point: host_get_inventory.py
8 | parameters:
9 | host_ids:
10 | type: array
11 | description: "List of Host IDs to find inventory items for"
12 | required: True
--------------------------------------------------------------------------------
/actions/host_get_multiple_ids.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class ZabbixGetMultipleHostID(ZabbixBaseAction):
21 | def find_hosts(self, host_name):
22 | """ Queries the zabbix api for a host and returns just the
23 | ids of the hosts as a list. If a host could not be found it
24 | returns an empty list.
25 | """
26 | try:
27 | zabbix_hosts = self.client.host.get(filter={"host": host_name})
28 | except ZabbixAPIException as e:
29 | raise ZabbixAPIException(("There was a problem searching for the host: "
30 | "{0}".format(e)))
31 |
32 | zabbix_hosts_return = []
33 | if len(zabbix_hosts) > 0:
34 | for host in zabbix_hosts:
35 | zabbix_hosts_return.append(host['hostid'])
36 |
37 | return zabbix_hosts_return
38 |
39 | def run(self, host=None):
40 | """ Gets the IDs of the Zabbix host given the Hostname or FQDN
41 | of the Zabbix host.
42 | """
43 | self.connect()
44 |
45 | zabbix_hosts = self.find_hosts(host)
46 |
47 | return {'host_ids': zabbix_hosts}
48 |
--------------------------------------------------------------------------------
/actions/host_get_multiple_ids.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_multiple_ids
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Get the IDs of multiple Zabbix Hosts
6 | enabled: true
7 | entry_point: host_get_multiple_ids.py
8 | parameters:
9 | host:
10 | type: array
11 | description: "Name of the Zabbix Hosts to retreive an ID for"
12 | required: True
13 |
--------------------------------------------------------------------------------
/actions/host_get_status.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 |
18 |
19 | class HostGetStatus(ZabbixBaseAction):
20 | def run(self, host=None, status=None):
21 | """ Gets the status of a Zabbix Host.
22 | """
23 | self.connect()
24 |
25 | # Find current host and populate self.zabbix_host
26 | self.find_host(host)
27 |
28 | # Get status from self.zabbix_host
29 | host_status = self.zabbix_host['status']
30 |
31 | return host_status
32 |
--------------------------------------------------------------------------------
/actions/host_get_status.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_status
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Get the status of a Zabbix Host
6 | enabled: true
7 | entry_point: host_get_status.py
8 | parameters:
9 | host:
10 | type: string
11 | description: "Name of the Zabbix Host"
12 | required: True
13 |
--------------------------------------------------------------------------------
/actions/host_get_triggers.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_get_triggers
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all triggers for a given host in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | expandDescription:
10 | type: string
11 | description: "test flag"
12 | default: ""
13 | immutable: true
14 | filter:
15 | type: object
16 | description: Condition to filter the result
17 | token:
18 | type: string
19 | description: Encrypted access token to authenticate to ZabbixServer
20 | default: |
21 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
22 | secret: true
23 | api_method:
24 | default: trigger.get
25 | immutable: true
26 |
--------------------------------------------------------------------------------
/actions/host_update_status.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class HostUpdateStatus(ZabbixBaseAction):
21 | def run(self, host=None, status=None):
22 | """ Updates the status of a Zabbix Host. Status needs to be
23 | 1 or 0 for the call to succeed.
24 | """
25 | self.connect()
26 |
27 | host_id = self.find_host(host)
28 |
29 | try:
30 | self.client.host.update(hostid=host_id, status=status)
31 | return True
32 | except ZabbixAPIException as e:
33 | raise ZabbixAPIException("There was a problem updating the host: {0}".format(e))
34 |
--------------------------------------------------------------------------------
/actions/host_update_status.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: host_update_status
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Update the status of a Zabbix Host
6 | enabled: true
7 | entry_point: host_update_status.py
8 | parameters:
9 | host:
10 | type: string
11 | description: "Name of the Zabbix Host"
12 | required: True
13 | status:
14 | type: integer
15 | description: "Status to set the Zabbix Host to valid values: 0 - monitored host 1 - unmonitored host"
16 | required: True
17 |
--------------------------------------------------------------------------------
/actions/lib/__init__.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
--------------------------------------------------------------------------------
/actions/lib/actions.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from pyzabbix.api import ZabbixAPIException
17 | from st2common.runners.base_action import Action
18 | from six.moves.urllib.error import URLError
19 | from zabbix.api import ZabbixAPI
20 |
21 |
22 | class ZabbixBaseAction(Action):
23 | def __init__(self, config):
24 | super(ZabbixBaseAction, self).__init__(config)
25 |
26 | self.config = config
27 | self.client = None
28 |
29 | if self.config is not None and "zabbix" in self.config:
30 | if "url" not in self.config['zabbix']:
31 | raise ValueError("Zabbix url details not in the config.yaml")
32 | elif "username" not in self.config['zabbix']:
33 | raise ValueError("Zabbix user details not in the config.yaml")
34 | elif "password" not in self.config['zabbix']:
35 | raise ValueError("Zabbix password details not in the config.yaml")
36 | else:
37 | raise ValueError("Zabbix details not in the config.yaml")
38 |
39 | def connect(self):
40 | try:
41 | self.client = ZabbixAPI(url=self.config['zabbix']['url'],
42 | user=self.config['zabbix']['username'],
43 | password=self.config['zabbix']['password'])
44 | except ZabbixAPIException as e:
45 | raise ZabbixAPIException("Failed to authenticate with Zabbix (%s)" % str(e))
46 | except URLError as e:
47 | raise URLError("Failed to connect to Zabbix Server (%s)" % str(e))
48 | except KeyError:
49 | raise KeyError("Configuration for Zabbix pack is not set yet")
50 |
51 | def reconstruct_args_for_ack_event(self, eventid, message, will_close):
52 | return {
53 | 'eventids': eventid,
54 | 'message': message,
55 | 'action': 1 if will_close else 0,
56 | }
57 |
58 | def find_host(self, host_name):
59 | try:
60 | zabbix_host = self.client.host.get(filter={"host": host_name})
61 | except ZabbixAPIException as e:
62 | raise ZabbixAPIException(("There was a problem searching for the host: "
63 | "{0}".format(e)))
64 |
65 | if len(zabbix_host) == 0:
66 | raise ValueError("Could not find any hosts named {0}".format(host_name))
67 | elif len(zabbix_host) >= 2:
68 | raise ValueError("Multiple hosts found with the name: {0}".format(host_name))
69 |
70 | self.zabbix_host = zabbix_host[0]
71 |
72 | return self.zabbix_host['hostid']
73 |
74 | def maintenance_get(self, maintenance_name):
75 | try:
76 | result = self.client.maintenance.get(filter={"name": maintenance_name})
77 | return result
78 | except ZabbixAPIException as e:
79 | raise ZabbixAPIException(("There was a problem searching for the maintenance window: "
80 | "{0}".format(e)))
81 |
82 | def maintenance_create_or_update(self, maintenance_params):
83 | maintenance_result = self.maintenance_get(maintenance_params['name'])
84 | if len(maintenance_result) == 0:
85 | try:
86 | create_result = self.client.maintenance.create(**maintenance_params)
87 | return create_result
88 | except ZabbixAPIException as e:
89 | raise ZabbixAPIException(("There was a problem creating the "
90 | "maintenance window: {0}".format(e)))
91 | elif len(maintenance_result) == 1:
92 | try:
93 | maintenance_id = maintenance_result[0]['maintenanceid']
94 | update_result = self.client.maintenance.update(maintenanceid=maintenance_id,
95 | **maintenance_params)
96 | return update_result
97 | except ZabbixAPIException as e:
98 | raise ZabbixAPIException(("There was a problem updating the "
99 | "maintenance window: {0}".format(e)))
100 | elif len(maintenance_result) >= 2:
101 | raise ValueError(("There are multiple maintenance windows with the "
102 | "name: {0}").format(maintenance_params['name']))
103 |
--------------------------------------------------------------------------------
/actions/list_host_groups.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: list_host_groups
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all host_groups objects which are registered in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | filter:
10 | type: object
11 | description: Condition to filter the result
12 | token:
13 | type: string
14 | description: Encrypted access token to authenticate to ZabbixServer
15 | default: |
16 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
17 | secret: true
18 | api_method:
19 | default: hostgroup.get
20 | immutable: true
21 |
--------------------------------------------------------------------------------
/actions/list_host_interfaces.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: list_host_interfaces
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all hostinterfaces objects which are registered in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | filter:
10 | type: object
11 | description: Condition to filter the result
12 | token:
13 | type: string
14 | description: Encrypted access token to authenticate to ZabbixServer
15 | default: |
16 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
17 | secret: true
18 | api_method:
19 | default: hostinterface.get
20 | immutable: true
21 |
--------------------------------------------------------------------------------
/actions/list_hosts.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: list_hosts
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all host objects which are registered in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | filter:
10 | type: object
11 | description: 'Condition to filter the result. Example - {"hostid": "12345"}'
12 | output:
13 | description: A list of key names that limit the response data. 'hostid' is always present. Example - ["maintenance_status", "name"]
14 | type: array
15 | groupids:
16 | description: list of groupids to limit the results to. Example - ["123", "456"]
17 | type: array
18 | token:
19 | type: string
20 | description: Encrypted access token to authenticate to ZabbixServer
21 | default: |
22 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
23 | secret: true
24 | api_method:
25 | default: host.get
26 | immutable: true
27 |
--------------------------------------------------------------------------------
/actions/list_templates.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: list_templates
3 | pack: zabbix
4 | runner_type: python-script
5 | description: List all templates objects which are registered in Zabbix
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | filter:
10 | type: object
11 | description: Condition to filter the result
12 | token:
13 | type: string
14 | description: Encrypted access token to authenticate to ZabbixServer
15 | default: |
16 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
17 | secret: true
18 | api_method:
19 | default: template.get
20 | immutable: true
21 |
--------------------------------------------------------------------------------
/actions/maintenance_create_or_update.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from datetime import datetime
17 | from tzlocal import get_localzone
18 | from lib.actions import ZabbixBaseAction
19 |
20 |
21 | class MaintenanceCreateOrUpdate(ZabbixBaseAction):
22 | def run(self,
23 | host=None,
24 | time_type=None,
25 | maintenance_window_name=None,
26 | maintenance_type=None,
27 | start_date=None,
28 | end_date=None):
29 | """ Creates or updates a Zabbix maintenance window by looking
30 | for the supplied maintenance_window_name and creating the mainenance window if it
31 | does not exist or updating the mainenance window if it already exists.
32 | """
33 | self.connect()
34 |
35 | host_id = self.find_host(host)
36 |
37 | start_time = None
38 | end_time = None
39 | period = None
40 | if start_date is not None and end_date is not None:
41 | local_tz = get_localzone()
42 |
43 | start_local = datetime.strptime(start_date, "%Y-%m-%d %H:%M")
44 | start_local = start_local.replace(tzinfo=local_tz)
45 | start_time = int(start_local.strftime('%s'))
46 |
47 | end_local = datetime.strptime(end_date, "%Y-%m-%d %H:%M")
48 | end_local = end_local.replace(tzinfo=local_tz)
49 | end_time = int(end_local.strftime('%s'))
50 |
51 | period = end_time - start_time
52 | else:
53 | raise ValueError("Must supply a start_date and end_date")
54 |
55 | time_period = [{'start_date': start_time,
56 | 'timeperiod_type': time_type,
57 | 'period': period}]
58 |
59 | maintenance_params = {
60 | 'hostids': [host_id],
61 | 'name': maintenance_window_name,
62 | 'active_since': start_time,
63 | 'active_till': end_time,
64 | 'maintenance_type': maintenance_type,
65 | 'timeperiods': time_period
66 | }
67 |
68 | maintenance_result = self.maintenance_create_or_update(maintenance_params)
69 |
70 | return {'maintenance_id': maintenance_result['maintenanceids'][0]}
71 |
--------------------------------------------------------------------------------
/actions/maintenance_create_or_update.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: maintenance_create_or_update
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Create or update Zabbix Maintenance Window
6 | enabled: true
7 | entry_point: maintenance_create_or_update.py
8 | parameters:
9 | end_date:
10 | type: string
11 | description: "Date and time to end maintenance window ex. 2017-10-13 20:00 (Y-m-d H:M)"
12 | required: True
13 | host:
14 | type: string
15 | description: "Name of the Zabbix Host"
16 | required: True
17 | maintenance_window_name:
18 | type: string
19 | description: "Name that of the maintenance window"
20 | required: True
21 | maintenance_type:
22 | type: integer
23 | description: "Type of maintenance valid values: 0 - with data collection 1 - without data collection"
24 | default: 0
25 | start_date:
26 | type: string
27 | description: "Date and time to start maintenance window ex. 2017-10-13 11:00 (Y-m-d H:M)"
28 | required: True
29 | time_type:
30 | type: integer
31 | description: "Type of time period valid values: 0 - one time only; 2 - daily; 3 - weekly; 4 - monthly"
32 | default: 0
33 |
--------------------------------------------------------------------------------
/actions/maintenance_delete.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 | from pyzabbix.api import ZabbixAPIException
18 |
19 |
20 | class MaintenanceDelete(ZabbixBaseAction):
21 | def run(self, maintenance_id=None, maintenance_window_name=None):
22 | """ Delete a maintenance window base on the given maintenance_window_name
23 | or a maintenance_id
24 | """
25 | self.connect()
26 |
27 | if maintenance_window_name is not None:
28 | maintenance_result = self.maintenance_get(maintenance_window_name)
29 |
30 | if len(maintenance_result) == 0:
31 | raise ValueError(("Could not find maintenance windows with name: "
32 | "{0}").format(maintenance_window_name))
33 | elif len(maintenance_result) == 1:
34 | maintenance_id = maintenance_result[0]['maintenanceid']
35 | elif len(maintenance_result) >= 2:
36 | raise ValueError(("There are multiple maintenance windows with the "
37 | "name: {0}").format(maintenance_window_name))
38 | elif maintenance_window_name is None and maintenance_id is None:
39 | raise ValueError("Must provide either a maintenance_window_name or a maintenance_id")
40 |
41 | try:
42 | self.client.maintenance.delete(maintenance_id)
43 | except ZabbixAPIException as e:
44 | raise ZabbixAPIException(("There was a problem deleting the "
45 | "maintenance window: {0}").format(e))
46 |
47 | return True
48 |
--------------------------------------------------------------------------------
/actions/maintenance_delete.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: maintenance_delete
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Delete Zabbix Maintenance Window
6 | enabled: true
7 | entry_point: maintenance_delete.py
8 | parameters:
9 | maintenance_id:
10 | type: string
11 | description: "ID of the maintenance window"
12 | maintenance_window_name:
13 | type: string
14 | description: "Name that of the maintenance window"
15 |
--------------------------------------------------------------------------------
/actions/test_credentials.py:
--------------------------------------------------------------------------------
1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 | # contributor license agreements. See the NOTICE file distributed with
3 | # this work for additional information regarding copyright ownership.
4 | # The ASF licenses this file to You under the Apache License, Version 2.0
5 | # (the "License"); you may not use this file except in compliance with
6 | # the License. You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 |
16 | from lib.actions import ZabbixBaseAction
17 |
18 |
19 | class TestCredentials(ZabbixBaseAction):
20 |
21 | def run(self, host=None):
22 | """ Attempt to login to the Zabbix server given the credentials in the
23 | config
24 | """
25 | self.connect()
26 | return True
27 |
--------------------------------------------------------------------------------
/actions/test_credentials.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: test_credentials
3 | pack: zabbix
4 | runner_type: python-script
5 | description: Attempts to login to Zabbix given the credentials in the config
6 | enabled: true
7 | entry_point: test_credentials.py
8 |
--------------------------------------------------------------------------------
/actions/update_host.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: update_host
3 | pack: zabbix
4 | runner_type: python-script
5 | description: A primitive action to update host information
6 | enabled: true
7 | entry_point: call_api.py
8 | parameters:
9 | hostid:
10 | type: string
11 | description: ID of Host to be updated
12 | description:
13 | type: string
14 | description: Description of the host.
15 | groups:
16 | type: array
17 | description: Host groups to replace the current host groups the host belongs to.
18 | host:
19 | type: string
20 | description: Technical name of the host.
21 | interfaces:
22 | type: array
23 | description: Host interfaces to replace the current host interfaces.
24 | inventory:
25 | type: object
26 | description: Host inventory properties.
27 | macros:
28 | type: array
29 | description: User macros to replace the current user macros.
30 | name:
31 | type: string
32 | description: Visible name of the host.
33 | templates:
34 | type: array
35 | description: Templates to replace the currently linked templates.
36 | token:
37 | type: string
38 | description: Encrypted access token to authenticate to ZabbixServer
39 | default: |
40 | {% if st2kv.user.zabbix.secret_token|string != '' %}{{ st2kv.user.zabbix.secret_token | decrypt_kv }}{% endif %}
41 | secret: true
42 | api_method:
43 | default: host.update
44 | immutable: true
45 |
--------------------------------------------------------------------------------
/actions/workflows/host_get_active_triggers.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 1.0
3 |
4 | description: List all active triggers for a given host
5 |
6 | input:
7 | - host
8 | - priority
9 |
10 | vars:
11 | - error: ""
12 | - triggers: []
13 | - host_id: ""
14 |
15 |
16 | output:
17 | - triggers: "{{ ctx().triggers }}"
18 |
19 | tasks:
20 | get_zabbix_id:
21 | action: zabbix.host_get_id
22 | input:
23 | host: "{{ ctx().host }}"
24 | next:
25 | - when: "{{ succeeded() }}"
26 | publish:
27 | - host_id: "{{ result().result }}"
28 | do:
29 | - get_triggers
30 | - when: "{{ failed() }}"
31 | publish:
32 | - error: "{{ result().stderr }}"
33 | do:
34 | - fail
35 |
36 | get_triggers:
37 | action: zabbix.host_get_triggers
38 | input:
39 | filter:
40 | hostid: "{{ ctx().host_id }}"
41 | value: 1
42 | priority: "{{ ctx().priority }}"
43 | next:
44 | - when: "{{ succeeded() }}"
45 | publish:
46 | - triggers: "{{ result().result }}"
47 | do:
48 | - noop
49 | - when: "{{ failed() }}"
50 | publish:
51 | - error: "{{ result().stderr }}"
52 | do:
53 | - fail
54 |
--------------------------------------------------------------------------------
/config.schema.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | zabbix:
3 | description: Configuration to authenticate with Zabbix Server
4 | type: object
5 | required: true
6 | additionalProperties: false
7 | properties:
8 | url:
9 | type: string
10 | description: The zabbix login URL
11 | default: http://localhost/zabbix
12 | username:
13 | type: string
14 | default: Admin
15 | password:
16 | type: string
17 | default: zabbix
18 | secret: true
19 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | mysql:
4 | image: mysql:5.7
5 | ports:
6 | - "3306:3306"
7 | environment:
8 | MYSQL_DATABASE: zabbix
9 | MYSQL_USER: zabbix
10 | MYSQL_PASSWORD: zabbix
11 | MYSQL_ROOT_PASSWORD: passwd
12 |
13 | zabbix-server:
14 | image: zabbix/zabbix-server-mysql:${TAG}
15 | environment:
16 | DB_SERVER_HOST: mysql
17 | MYSQL_ROOT_PASSWORD: passwd
18 | depends_on:
19 | - mysql
20 | volumes:
21 | - ./tools/scripts/st2_dispatch.py:/usr/lib/zabbix/alertscripts/st2_dispatch.py
22 | ports:
23 | - "10051:10051"
24 |
25 | zabbix-web:
26 | image: zabbix/zabbix-web-nginx-mysql:${TAG}
27 | restart: always
28 | environment:
29 | DB_SERVER_HOST: mysql
30 | MYSQL_ROOT_PASSWORD: passwd
31 | ports:
32 | - 3033:80
33 | depends_on:
34 | - mysql
35 | - zabbix-server
36 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/icon.png
--------------------------------------------------------------------------------
/images/apikey_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/apikey_example.png
--------------------------------------------------------------------------------
/images/configuration_for_action1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/configuration_for_action1.png
--------------------------------------------------------------------------------
/images/configuration_for_action2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/configuration_for_action2.png
--------------------------------------------------------------------------------
/images/configuration_for_mediatype1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/configuration_for_mediatype1.png
--------------------------------------------------------------------------------
/images/configuration_for_mediatype2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/configuration_for_mediatype2.png
--------------------------------------------------------------------------------
/images/description_alertscript1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/description_alertscript1.png
--------------------------------------------------------------------------------
/images/description_alertscript2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/description_alertscript2.png
--------------------------------------------------------------------------------
/images/internal_construction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/internal_construction.png
--------------------------------------------------------------------------------
/images/zabbix_dependency_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/StackStorm-Exchange/stackstorm-zabbix/949c564ab0c72f9c0cd5f8bf11b055f40f97dc82/images/zabbix_dependency_flow.png
--------------------------------------------------------------------------------
/pack.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | ref: zabbix
3 | name: zabbix
4 | description: Zabbix Monitoring System
5 | keywords:
6 | - zabbix
7 | - monitoring
8 | version: 1.2.4
9 | author: Hiroyasu OHYAMA
10 | email: user.localhost2000@gmail.com
11 | python_versions:
12 | - "3"
13 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | git+https://github.com/EncoreTechnologies/py-zabbix.git
2 | pytz
3 | tzlocal
4 |
--------------------------------------------------------------------------------
/spec/localhost/tools_register_config_for_st2_spec.rb:
--------------------------------------------------------------------------------
1 | require 'zbxapi'
2 | require 'spec_helper'
3 |
4 | ZABBIX_USER = ENV['ZABBIX_USER'] || 'admin'
5 | ZABBIX_SENDTO = ENV['ZABBIX_SENDTO'] || 'admin'
6 | ZABBIX_PASSWORD = ENV['ZABBIX_PASSWORD'] || 'zabbix'
7 | ZABBIX_API_ENDPOINT = ENV['ZABBIX_API'] || 'http://localhost/'
8 |
9 | describe 'Tests for registering Zabbix for StackStorm' do
10 | before(:all) do
11 | @client = ZabbixAPI.new(ZABBIX_API_ENDPOINT)
12 |
13 | expect(try_to_login).not_to be_a(RuntimeError)
14 | end
15 |
16 | # run script to register configurations for StackStorm to the Zabbix
17 | describe command("tools/register_st2_config_to_zabbix.py " \
18 | "-u #{ ZABBIX_USER } " \
19 | "-s #{ ZABBIX_SENDTO } " \
20 | "-p #{ ZABBIX_PASSWORD } " \
21 | "-z #{ ZABBIX_API_ENDPOINT }") do
22 | its(:exit_status) { should eq 0 }
23 | its(:stdout) do
24 | should match /^Success to register the configurations for StackStorm to the Zabbix Server./
25 | end
26 |
27 | describe 'Check each configurations are actually set in the Zabbix' do
28 | it 'MediaType configuration is set' do
29 | expect(@client.mediatype.get.find {|x| x['description'] == 'StackStorm'}).to_not be_nil
30 | end
31 |
32 | it 'Action configuration is set' do
33 | expect(@client.action.get.find { |x| x['name'].include?('StackStorm')}).to_not be_nil
34 | end
35 | end
36 | end
37 |
38 | # This method wait to start and initialize Zabbix-server and Zabbix-Web
39 | def try_to_login(retry_count = 0)
40 | begin
41 | return @client.login('admin', 'zabbix')
42 | rescue => e
43 | if retry_count < 60
44 | # make a delay before retrying
45 | sleep 1
46 | return try_to_login(retry_count + 1)
47 | else
48 | return e
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'serverspec'
2 |
3 | set :backend, :exec
4 |
5 |
--------------------------------------------------------------------------------
/tests/fixtures/blank.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
--------------------------------------------------------------------------------
/tests/fixtures/full.yaml:
--------------------------------------------------------------------------------
1 | ../../zabbix.yaml.example
--------------------------------------------------------------------------------
/tests/test_action_base.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from event_action_runner import EventActionRunner
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class EventActionTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = EventActionRunner
13 |
14 | def test_run_action_without_configuration(self):
15 | self.assertRaises(ValueError, self.action_cls, self.blank_config)
16 |
17 | @mock.patch('lib.actions.ZabbixAPI')
18 | def test_run_action_with_invalid_config_of_endpoint(self, mock_client):
19 | # make an exception that means failure to connect server.
20 | mock_client.side_effect = URLError('connection error')
21 |
22 | action = self.get_action_instance(self.full_config)
23 |
24 | with self.assertRaises(URLError):
25 | action.run(action='something')
26 |
27 | @mock.patch('lib.actions.ZabbixAPI')
28 | def test_run_action_with_invalid_config_of_account(self, mock_client):
29 | # make an exception that means failure to authenticate with Zabbix-server.
30 | mock_client.side_effect = ZabbixAPIException('auth error')
31 |
32 | action = self.get_action_instance(self.full_config)
33 |
34 | with self.assertRaises(ZabbixAPIException):
35 | action.run(action='something')
36 |
37 | @mock.patch('lib.actions.ZabbixAPI')
38 | def test_run_action_with_invalid_config_of_action(self, mock_client):
39 | mock_obj = mock.Mock()
40 | mock_obj.invalid = []
41 |
42 | mock_client.return_value = mock_obj
43 |
44 | action = self.get_action_instance(self.full_config)
45 | result = action.run(action='invalid.action')
46 |
47 | self.assertFalse(result[0])
48 | self.assertEqual(result[1], "Specified action(invalid.action) is invalid")
49 |
50 | @mock.patch('lib.actions.ZabbixAPI')
51 | def test_run_action_with_valid_config(self, mock_client):
52 | def mock_double(param):
53 | return param * 2
54 |
55 | mock_handler = mock.Mock()
56 | mock_handler.double = mock.Mock(side_effect=mock_double)
57 |
58 | mock_obj = mock.Mock()
59 | mock_obj.action = mock_handler
60 |
61 | mock_client.return_value = mock_obj
62 |
63 | action = self.get_action_instance(self.full_config)
64 | result = action.run(action='action.double', param=4)
65 |
66 | self.assertTrue(result[0])
67 | self.assertEqual(result[1], 8)
68 |
69 | @mock.patch('lib.actions.ZabbixAPI')
70 | def test_find_host(self, mock_client):
71 | action = self.get_action_instance(self.full_config)
72 | test_dict = {'host_name': "test", 'hostid': "1"}
73 | mock_client.host.get.return_value = [test_dict]
74 | action.client = mock_client
75 |
76 | result = action.find_host(test_dict['host_name'])
77 | self.assertEqual(result, test_dict['hostid'])
78 |
79 | @mock.patch('lib.actions.ZabbixAPI')
80 | def test_find_host_no_host(self, mock_client):
81 | action = self.get_action_instance(self.full_config)
82 | test_dict = {'host_name': "test", 'host_id': "1"}
83 | mock_client.host.get.return_value = []
84 | action.client = mock_client
85 |
86 | with self.assertRaises(ValueError):
87 | action.find_host(test_dict['host_name'])
88 |
89 | @mock.patch('lib.actions.ZabbixAPI')
90 | def test_find_host_too_many_host(self, mock_client):
91 | action = self.get_action_instance(self.full_config)
92 | test_dict = [{'host_name': "test", 'hostid': "1"},
93 | {'host_name': "test", 'hostid': "2"}]
94 | mock_client.host.get.return_value = test_dict
95 | action.client = mock_client
96 |
97 | with self.assertRaises(ValueError):
98 | action.find_host(test_dict[0]['host_name'])
99 |
100 | @mock.patch('lib.actions.ZabbixAPI')
101 | def test_find_host_fail(self, mock_client):
102 | action = self.get_action_instance(self.full_config)
103 | test_dict = {'host_name': "test", 'hostid': "1"}
104 | mock_client.host.get.side_effect = ZabbixAPIException('host error')
105 | mock_client.host.get.return_value = [test_dict]
106 | action.client = mock_client
107 |
108 | with self.assertRaises(ZabbixAPIException):
109 | action.find_host(test_dict['host_name'])
110 |
111 | @mock.patch('lib.actions.ZabbixAPI')
112 | def test_maintenance_get(self, mock_client):
113 | action = self.get_action_instance(self.full_config)
114 | test_dict = {'maintenance_name': "test", 'maintenanceid': "1"}
115 | mock_client.maintenance.get.return_value = [test_dict]
116 | action.client = mock_client
117 |
118 | result = action.maintenance_get(test_dict['maintenance_name'])
119 | self.assertEqual(result, [test_dict])
120 |
121 | @mock.patch('lib.actions.ZabbixAPI')
122 | def test_maintenance_get_fail(self, mock_client):
123 | action = self.get_action_instance(self.full_config)
124 | test_dict = {'maintenance_name': "test", 'maintenanceid': "1"}
125 | mock_client.maintenance.get.side_effect = ZabbixAPIException('maintenance error')
126 | mock_client.maintenance.get.return_value = [test_dict]
127 | action.client = mock_client
128 |
129 | with self.assertRaises(ZabbixAPIException):
130 | action.maintenance_get(test_dict['maintenance_name'])
131 |
132 | @mock.patch('lib.actions.ZabbixBaseAction.maintenance_get')
133 | @mock.patch('lib.actions.ZabbixAPI')
134 | def test_maintenance_create_or_update_update(self, mock_client, mock_maintenance_get):
135 | action = self.get_action_instance(self.full_config)
136 | test_dict = {'name': "test"}
137 | maintenance_dict = {'maintenance_name': "test", 'maintenanceid': "1"}
138 | mock_maintenance_get.return_value = [maintenance_dict]
139 | mock_client.maintenance.update.return_value = [maintenance_dict['maintenanceid']]
140 | action.client = mock_client
141 |
142 | result = action.maintenance_create_or_update(test_dict)
143 | self.assertEqual(result, [maintenance_dict['maintenanceid']])
144 |
145 | @mock.patch('lib.actions.ZabbixBaseAction.maintenance_get')
146 | @mock.patch('lib.actions.ZabbixAPI')
147 | def test_maintenance_create_or_update_update_fail(self, mock_client, mock_maintenance_get):
148 | action = self.get_action_instance(self.full_config)
149 | test_dict = {'name': "test"}
150 | maintenance_dict = {'maintenance_name': "test", 'maintenanceid': "1"}
151 | mock_maintenance_get.return_value = [maintenance_dict]
152 | mock_client.maintenance.update.return_value = [maintenance_dict['maintenanceid']]
153 | mock_client.maintenance.update.side_effect = ZabbixAPIException('maintenance error')
154 | action.client = mock_client
155 |
156 | with self.assertRaises(ZabbixAPIException):
157 | action.maintenance_create_or_update(test_dict)
158 |
159 | @mock.patch('lib.actions.ZabbixBaseAction.maintenance_get')
160 | @mock.patch('lib.actions.ZabbixAPI')
161 | def test_maintenance_create_or_update_create(self, mock_client, mock_maintenance_get):
162 | action = self.get_action_instance(self.full_config)
163 | test_dict = {'name': "test"}
164 | maintenance_dict = {'maintenance_name': "test", 'maintenanceid': "1"}
165 | mock_maintenance_get.return_value = []
166 | mock_client.maintenance.create.return_value = [maintenance_dict['maintenanceid']]
167 | action.client = mock_client
168 |
169 | result = action.maintenance_create_or_update(test_dict)
170 | self.assertEqual(result, [maintenance_dict['maintenanceid']])
171 |
172 | @mock.patch('lib.actions.ZabbixBaseAction.maintenance_get')
173 | @mock.patch('lib.actions.ZabbixAPI')
174 | def test_maintenance_create_or_update_create_fail(self, mock_client, mock_maintenance_get):
175 | action = self.get_action_instance(self.full_config)
176 | test_dict = {'name': "test"}
177 | maintenance_dict = {'maintenance_name': "test", 'maintenanceid': "1"}
178 | mock_maintenance_get.return_value = []
179 | mock_client.maintenance.create.return_value = [maintenance_dict['maintenanceid']]
180 | mock_client.maintenance.create.side_effect = ZabbixAPIException('maintenance error')
181 | action.client = mock_client
182 |
183 | with self.assertRaises(ZabbixAPIException):
184 | action.maintenance_create_or_update(test_dict)
185 |
186 | @mock.patch('lib.actions.ZabbixBaseAction.maintenance_get')
187 | @mock.patch('lib.actions.ZabbixAPI')
188 | def test_maintenance_create_or_update_too_many_maintenance_windows(self,
189 | mock_client,
190 | mock_maintenance_get):
191 | action = self.get_action_instance(self.full_config)
192 | test_dict = {'name': "test"}
193 | maintenance_dict = [{'maintenance_name': "test", 'maintenanceid': "1"},
194 | {'maintenance_name': "test", 'maintenanceid': "2"}]
195 | mock_maintenance_get.return_value = maintenance_dict
196 | mock_client.maintenance.create.return_value = maintenance_dict[0]['maintenanceid']
197 | action.client = mock_client
198 |
199 | with self.assertRaises(ValueError):
200 | action.maintenance_create_or_update(test_dict)
201 |
--------------------------------------------------------------------------------
/tests/test_call_api.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from call_api import CallAPI
5 |
6 |
7 | class CallAPITest(ZabbixBaseActionTestCase):
8 | __test__ = True
9 | action_cls = CallAPI
10 |
11 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
12 | def test_run_action_without_token(self, mock_conn):
13 | action = self.get_action_instance(self.full_config)
14 |
15 | # This is a mock of calling API 'hoge'
16 | action.client = mock.Mock()
17 | action.client.hoge.return_value = 'result'
18 |
19 | # This checks that a method which is specified in the api_method parameter would be called
20 | self.assertEqual(action.run(api_method='hoge', token=None, param='foo'), 'result')
21 |
22 | @mock.patch('call_api.ZabbixAPI')
23 | def test_run_action_with_token(self, mock_client):
24 | action = self.get_action_instance(self.full_config)
25 |
26 | # This is a mock of calling API 'hoge' to confirm that
27 | # specified parameters would be passed correctly.
28 | def side_effect(*args, **kwargs):
29 | return (args, kwargs)
30 |
31 | _mock_client = mock.Mock()
32 | _mock_client.hoge.side_effect = side_effect
33 | mock_client.return_value = _mock_client
34 |
35 | # This checks that specified parameter and access token would be set expectedly
36 | result = action.run(api_method='hoge', token='test_token', param='foo')
37 | self.assertEqual(result, ((), {'param': 'foo'}))
38 | self.assertEqual(action.auth, 'test_token')
39 |
40 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
41 | def test_call_hierarchized_method(self, mock_conn):
42 | action = self.get_action_instance(self.full_config)
43 |
44 | # Initialize client object that only accepts request to 'foo.bar' method.
45 | action.client = mock.Mock(spec=['foo'])
46 | action.client.foo = mock.Mock(spec=['bar'])
47 | action.client.foo.bar.return_value = 'result'
48 |
49 | # Send request with proper parameter
50 | self.assertEqual(action.run(api_method='foo.bar', token=None, param='hoge'), 'result')
51 |
52 | # Send request with invalid api_method
53 | with self.assertRaises(RuntimeError):
54 | action.run(api_method='foo.hoge', token=None, param='hoge')
55 |
56 | @mock.patch('call_api.ZabbixAPI')
57 | def test_run_action_with_empty_parameters(self, mock_client):
58 | action = self.get_action_instance(self.full_config)
59 |
60 | # This is a mock of calling API 'hoge' to confirm that
61 | # params with a value of None (p0) are removed prior to execution
62 | # Should not remove [ '123', False, {}, [], 0 ]
63 | def side_effect(*args, **kwargs):
64 | return (args, kwargs)
65 |
66 | _mock_client = mock.Mock()
67 | _mock_client.hoge.side_effect = side_effect
68 | mock_client.return_value = _mock_client
69 |
70 | result = action.run(api_method='hoge', token='test_token',
71 | **{'p0': None, 'p1': '123', 'p2': False, 'p3': {}, 'p4': [], 'p5': 0})
72 | self.assertEqual(result, ((),
73 | {'p1': '123', 'p2': False, 'p3': {}, 'p4': [], 'p5': 0}))
74 | _mock_client.hoge.assert_called_with(
75 | **{'p1': '123', 'p2': False, 'p3': {}, 'p4': [], 'p5': 0})
76 |
--------------------------------------------------------------------------------
/tests/test_create_host.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from create_host import CreateHost
5 |
6 |
7 | class CreateHostTest(ZabbixBaseActionTestCase):
8 | __test__ = True
9 | action_cls = CreateHost
10 |
11 | def setUp(self):
12 | self._check_data = {}
13 |
14 | def side_effect_update_proxy(*args, **kwargs):
15 | self._check_data['is_set_proxy'] = True
16 |
17 | def side_effect_create_host(host, groups, interfaces):
18 | self._check_data['interfaces'] = interfaces
19 |
20 | return {'hostids': ['1234']}
21 |
22 | # initialize mock client
23 | self._mock_client = mock.Mock()
24 | self._mock_client.hostgroup.get.return_value = []
25 | self._mock_client.host.create.side_effect = side_effect_create_host
26 | self._mock_client.host.get.return_value = []
27 | self._mock_client.proxy.update.side_effect = side_effect_update_proxy
28 | self._mock_client.proxy.get.return_value = [{'proxyid': 1234}]
29 |
30 | super(CreateHostTest, self).setUp()
31 |
32 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
33 | def test_create_host(self, mock_connect):
34 | def side_effect_connect():
35 | self._check_data['password_authentication'] = True
36 | mock_connect.side_effect = side_effect_connect
37 |
38 | # set mock client to AirOne
39 | action = self.get_action_instance(self.full_config)
40 | action.client = self._mock_client
41 |
42 | (result, data) = action.run(name='test-host', groups=[], domains=['example.com'])
43 | self.assertTrue(result)
44 | self.assertEqual(data, {'hostids': ['1234']})
45 | self.assertTrue(self._check_data['password_authentication'])
46 | self.assertFalse('is_set_proxy' in self._check_data)
47 | self.assertEqual(self._check_data['interfaces'], [{
48 | 'type': 1,
49 | 'main': 1,
50 | 'useip': 0,
51 | 'dns': 'example.com',
52 | 'ip': '',
53 | 'port': '10050'
54 | }])
55 |
56 | # This tests a case when main_if parameter is set
57 | action.run(name='test', groups=[], main_if='bar.test', domains=['foo.test', 'bar.test'])
58 |
59 | ifdata = self._check_data['interfaces']
60 | self.assertEqual(len(ifdata), 2)
61 | self.assertEqual([x['main'] for x in ifdata if x['dns'] == 'foo.test'], [0])
62 | self.assertEqual([x['main'] for x in ifdata if x['dns'] == 'bar.test'], [1])
63 |
64 | @mock.patch('create_host.ZabbixAPI')
65 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
66 | def test_create_host_with_token_and_proxy(self, mock_connect, mock_client):
67 | def side_effect():
68 | self._check_data['password_authentication'] = True
69 | mock_connect.side_effect = side_effect
70 |
71 | # set mock client to AirOne
72 | mock_client.return_value = self._mock_client
73 | action = self.get_action_instance(self.full_config)
74 |
75 | (result, data) = action.run(name='test-host', groups=[], domains=['example.com'],
76 | token='token', proxy_host='proxy')
77 | self.assertTrue(result)
78 | self.assertEqual(data, {'hostids': ['1234']})
79 | self.assertFalse('password_authentication' in self._check_data)
80 | self.assertTrue(self._check_data['is_set_proxy'])
81 |
82 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
83 | def test_create_host_without_interface_information(self, mock_connect):
84 | action = self.get_action_instance(self.full_config)
85 | action.client = self._mock_client
86 | (result, data) = action.run(name='test-host', groups=[])
87 |
88 | self.assertFalse(result)
89 | self.assertEqual(data, 'You have to IP address or domain value at least one.')
90 |
--------------------------------------------------------------------------------
/tests/test_host_delete.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_delete import HostDelete
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class HostDeleteTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = HostDelete
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'host': "test"}
19 | host_dict = {'name': "test", 'hostid': '1'}
20 | mock.MagicMock(return_value=host_dict['hostid'])
21 |
22 | with self.assertRaises(URLError):
23 | action.run(**test_dict)
24 |
25 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
26 | def test_run_host_error(self, mock_connect):
27 | action = self.get_action_instance(self.full_config)
28 | mock_connect.return_vaue = "connect return"
29 | test_dict = {'host': "test"}
30 | host_dict = {'name': "test", 'hostid': '1'}
31 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'],
32 | side_effect=ZabbixAPIException('host error'))
33 | action.connect = mock_connect
34 |
35 | with self.assertRaises(ZabbixAPIException):
36 | action.run(**test_dict)
37 |
38 | @mock.patch('lib.actions.ZabbixAPI')
39 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
40 | def test_run(self, mock_connect, mock_client):
41 | action = self.get_action_instance(self.full_config)
42 | mock_connect.return_vaue = "connect return"
43 | test_dict = {'host': "test"}
44 | host_dict = {'name': "test", 'hostid': '1'}
45 | action.connect = mock_connect
46 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
47 | mock_client.host.delete.return_value = "delete return"
48 | action.client = mock_client
49 |
50 | result = action.run(**test_dict)
51 | mock_client.host.delete.assert_called_with(host_dict['hostid'])
52 | self.assertEqual(result, True)
53 |
54 | @mock.patch('lib.actions.ZabbixAPI')
55 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
56 | def test_run_id(self, mock_connect, mock_client):
57 | action = self.get_action_instance(self.full_config)
58 | mock_connect.return_vaue = "connect return"
59 | test_dict = {'host_id': "1"}
60 | action.connect = mock_connect
61 | mock_client.host.delete.return_value = "delete return"
62 | action.client = mock_client
63 |
64 | result = action.run(**test_dict)
65 | mock_client.host.delete.assert_called_with(test_dict['host_id'])
66 | self.assertEqual(result, True)
67 |
68 | @mock.patch('lib.actions.ZabbixAPI')
69 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
70 | def test_run_delete_error(self, mock_connect, mock_client):
71 | action = self.get_action_instance(self.full_config)
72 | mock_connect.return_vaue = "connect return"
73 | test_dict = {'host': "test"}
74 | host_dict = {'name': "test", 'hostid': '1'}
75 | action.connect = mock_connect
76 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
77 | mock_client.host.delete.side_effect = ZabbixAPIException('host error')
78 | mock_client.host.delete.return_value = "delete return"
79 | action.client = mock_client
80 |
81 | with self.assertRaises(ZabbixAPIException):
82 | action.run(**test_dict)
83 |
--------------------------------------------------------------------------------
/tests/test_host_get_id.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_get_id import HostGetID
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class HostGetIDTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = HostGetID
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'host': "test"}
19 | host_dict = {'name': "test", 'hostid': '1'}
20 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
21 |
22 | with self.assertRaises(URLError):
23 | action.run(**test_dict)
24 |
25 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
26 | def test_run_host_error(self, mock_connect):
27 | action = self.get_action_instance(self.full_config)
28 | mock_connect.return_vaue = "connect return"
29 | test_dict = {'host': "test"}
30 | host_dict = {'name': "test", 'hostid': '1'}
31 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'],
32 | side_effect=ZabbixAPIException('host error'))
33 | action.connect = mock_connect
34 | with self.assertRaises(ZabbixAPIException):
35 | action.run(**test_dict)
36 |
37 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
38 | def test_run(self, mock_connect):
39 | action = self.get_action_instance(self.full_config)
40 | mock_connect.return_vaue = "connect return"
41 | test_dict = {'host': "test"}
42 | host_dict = {'name': "test", 'hostid': '1'}
43 | action.connect = mock_connect
44 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
45 |
46 | result = action.run(**test_dict)
47 | self.assertEqual(result, host_dict['hostid'])
48 |
--------------------------------------------------------------------------------
/tests/test_host_get_interfaces.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_get_interfaces import HostGetInterfaces
5 |
6 | from six.moves.urllib.error import URLError
7 |
8 |
9 | class HostGetInterfacesTestCase(ZabbixBaseActionTestCase):
10 | __test__ = True
11 | action_cls = HostGetInterfaces
12 |
13 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
14 | def test_run_connection_error(self, mock_connect):
15 | action = self.get_action_instance(self.full_config)
16 | mock_connect.side_effect = URLError('connection error')
17 | test_dict = {'host_ids': ["12345"]}
18 |
19 | with self.assertRaises(URLError):
20 | action.run(**test_dict)
21 |
22 | @mock.patch('lib.actions.ZabbixAPI')
23 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
24 | def test_run_host_error(self, mock_connect, mock_client):
25 | action = self.get_action_instance(self.full_config)
26 | mock_connect.return_vaue = "connect return"
27 | test_dict = {'host_ids': ["12345"]}
28 | interfaces_list = [{'hostid': "12345", 'interfaces': {
29 | 'name': "test"}}]
30 | action.connect = mock_connect
31 | mock_client.host.get.return_value = interfaces_list
32 | action.client = mock_client
33 |
34 | result = action.run(**test_dict)
35 | mock_client.host.get.assert_called_with(
36 | hostids=test_dict['host_ids'],
37 | selectInterfaces='extend',
38 | output=['hostid', 'interfaces'])
39 | self.assertEqual(result, interfaces_list)
40 |
41 | @mock.patch('lib.actions.ZabbixAPI')
42 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
43 | def test_run_none(self, mock_connect, mock_client):
44 | action = self.get_action_instance(self.full_config)
45 | mock_connect.return_vaue = "connect return"
46 | test_dict = {'host_ids': ["12345"]}
47 | interfaces_list = []
48 | action.connect = mock_connect
49 | mock_client.host.get.return_value = interfaces_list
50 | action.client = mock_client
51 |
52 | result = action.run(**test_dict)
53 | self.assertEqual(result, [])
54 |
55 | @mock.patch('lib.actions.ZabbixAPI')
56 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
57 | def test_run_single(self, mock_connect, mock_client):
58 | action = self.get_action_instance(self.full_config)
59 | mock_connect.return_vaue = "connect return"
60 | test_dict = {'host_ids': ["12345"]}
61 | interfaces_list = [{'hostid': "12345", 'interfaces': {
62 | 'name': "test"}}]
63 | action.connect = mock_connect
64 | mock_client.host.get.return_value = interfaces_list
65 | action.client = mock_client
66 | expected_return = [{'hostid': interfaces_list[0][
67 | 'hostid'], 'interfaces': interfaces_list[0]['interfaces']}]
68 |
69 | result = action.run(**test_dict)
70 | mock_client.host.get.assert_called_with(
71 | hostids=test_dict['host_ids'],
72 | selectInterfaces='extend',
73 | output=['hostid', 'interfaces'])
74 | self.assertEqual(result, expected_return)
75 |
76 | @mock.patch('lib.actions.ZabbixAPI')
77 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
78 | def test_run_multiple(self, mock_connect, mock_client):
79 | action = self.get_action_instance(self.full_config)
80 | mock_connect.return_vaue = "connect return"
81 | test_dict = {'host_ids': ["12345", "98765"]}
82 | interfaces_list = [{'hostid': "12345", 'interfaces':
83 | {'name': "test"}},
84 | {'hostid': "98765", 'interfaces':
85 | {'name': "test2"}}]
86 | action.connect = mock_connect
87 | mock_client.host.get.return_value = interfaces_list
88 | action.client = mock_client
89 | expected_return = [{'hostid': interfaces_list[0]['hostid'],
90 | 'interfaces': interfaces_list[0]['interfaces']},
91 | {'hostid': interfaces_list[1]['hostid'],
92 | 'interfaces': interfaces_list[1]['interfaces']}]
93 |
94 | result = action.run(**test_dict)
95 | mock_client.host.get.assert_called_with(
96 | hostids=test_dict['host_ids'],
97 | selectInterfaces='extend',
98 | output=['hostid', 'interfaces'])
99 | self.assertEqual(result, expected_return)
100 |
--------------------------------------------------------------------------------
/tests/test_host_get_inventory.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_get_inventory import HostGetInventory
5 |
6 | from six.moves.urllib.error import URLError
7 |
8 |
9 | class HostGetInventoryTestCase(ZabbixBaseActionTestCase):
10 | __test__ = True
11 | action_cls = HostGetInventory
12 |
13 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
14 | def test_run_connection_error(self, mock_connect):
15 | action = self.get_action_instance(self.full_config)
16 | mock_connect.side_effect = URLError('connection error')
17 | test_dict = {'host_ids': ["12345"]}
18 |
19 | with self.assertRaises(URLError):
20 | action.run(**test_dict)
21 |
22 | @mock.patch('lib.actions.ZabbixAPI')
23 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
24 | def test_run_host_error(self, mock_connect, mock_client):
25 | action = self.get_action_instance(self.full_config)
26 | mock_connect.return_vaue = "connect return"
27 | test_dict = {'host_ids': ["12345"]}
28 | inventory_list = [{'hostid': "12345", 'inventory': {
29 | 'serialno_a': "abcd1234", 'name': "test"}}]
30 | action.connect = mock_connect
31 | mock_client.host.get.return_value = inventory_list
32 | action.client = mock_client
33 |
34 | result = action.run(**test_dict)
35 | mock_client.host.get.assert_called_with(
36 | hostids=test_dict['host_ids'],
37 | selectInventory='extend',
38 | output=['hostid', 'inventory'])
39 | self.assertEqual(result, inventory_list)
40 |
41 | @mock.patch('lib.actions.ZabbixAPI')
42 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
43 | def test_run_none(self, mock_connect, mock_client):
44 | action = self.get_action_instance(self.full_config)
45 | mock_connect.return_vaue = "connect return"
46 | test_dict = {'host_ids': ["12345"]}
47 | inventory_list = []
48 | action.connect = mock_connect
49 | mock_client.host.get.return_value = inventory_list
50 | action.client = mock_client
51 |
52 | result = action.run(**test_dict)
53 | self.assertEqual(result, [])
54 |
55 | @mock.patch('lib.actions.ZabbixAPI')
56 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
57 | def test_run_single(self, mock_connect, mock_client):
58 | action = self.get_action_instance(self.full_config)
59 | mock_connect.return_vaue = "connect return"
60 | test_dict = {'host_ids': ["12345"]}
61 | inventory_list = [{'hostid': "12345", 'inventory': {
62 | 'serialno_a': "abcd1234", 'name': "test"}}]
63 | action.connect = mock_connect
64 | mock_client.host.get.return_value = inventory_list
65 | action.client = mock_client
66 | expected_return = [{'hostid': inventory_list[0][
67 | 'hostid'], 'inventory': inventory_list[0]['inventory']}]
68 |
69 | result = action.run(**test_dict)
70 | mock_client.host.get.assert_called_with(
71 | hostids=test_dict['host_ids'],
72 | selectInventory='extend',
73 | output=['hostid', 'inventory'])
74 | self.assertEqual(result, expected_return)
75 |
76 | @mock.patch('lib.actions.ZabbixAPI')
77 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
78 | def test_run_multiple(self, mock_connect, mock_client):
79 | action = self.get_action_instance(self.full_config)
80 | mock_connect.return_vaue = "connect return"
81 | test_dict = {'host_ids': ["12345", "98765"]}
82 | inventory_list = [{'hostid': "12345", 'inventory':
83 | {'serialno_a': "abcd1234", 'name': "test"}},
84 | {'hostid': "98765", 'inventory':
85 | {'serialno_a': "efgh5678", 'name': "test2"}}]
86 | action.connect = mock_connect
87 | mock_client.host.get.return_value = inventory_list
88 | action.client = mock_client
89 | expected_return = [{'hostid': inventory_list[0]['hostid'],
90 | 'inventory': inventory_list[0]['inventory']},
91 | {'hostid': inventory_list[1]['hostid'],
92 | 'inventory': inventory_list[1]['inventory']}]
93 |
94 | result = action.run(**test_dict)
95 | mock_client.host.get.assert_called_with(
96 | hostids=test_dict['host_ids'],
97 | selectInventory='extend',
98 | output=['hostid', 'inventory'])
99 | self.assertEqual(result, expected_return)
100 |
--------------------------------------------------------------------------------
/tests/test_host_get_multiple_ids.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_get_multiple_ids import ZabbixGetMultipleHostID
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class GetMultipleHostIDTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = ZabbixGetMultipleHostID
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'host': "test"}
19 | host_dict = {'name': "test", 'hostid': '1'}
20 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
21 |
22 | with self.assertRaises(URLError):
23 | action.run(**test_dict)
24 |
25 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
26 | def test_run_host_error(self, mock_connect):
27 | action = self.get_action_instance(self.full_config)
28 | mock_connect.return_vaue = "connect return"
29 | test_dict = {'host': "test"}
30 | host_dict = {'name': "test", 'hostid': '1'}
31 | action.find_hosts = mock.MagicMock(return_value=host_dict['hostid'],
32 | side_effect=ZabbixAPIException('host error'))
33 | action.connect = mock_connect
34 | with self.assertRaises(ZabbixAPIException):
35 | action.run(**test_dict)
36 |
37 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
38 | def test_run_none(self, mock_connect):
39 | action = self.get_action_instance(self.full_config)
40 | mock_connect.return_vaue = "connect return"
41 | test_dict = {'host': "test"}
42 | action.connect = mock_connect
43 | action.find_hosts = mock.MagicMock(return_value=[])
44 | expected_return = {'host_ids': []}
45 |
46 | result = action.run(**test_dict)
47 | self.assertEqual(result, expected_return)
48 |
49 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
50 | def test_run_single(self, mock_connect):
51 | action = self.get_action_instance(self.full_config)
52 | mock_connect.return_vaue = "connect return"
53 | test_dict = {'host': "test"}
54 | host_dict = {'name': "test", 'hostid': '1'}
55 | action.connect = mock_connect
56 | action.find_hosts = mock.MagicMock(return_value=[host_dict['hostid']])
57 | expected_return = {'host_ids': [host_dict['hostid']]}
58 |
59 | result = action.run(**test_dict)
60 | self.assertEqual(result, expected_return)
61 |
62 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
63 | def test_run_multiple(self, mock_connect):
64 | action = self.get_action_instance(self.full_config)
65 | mock_connect.return_vaue = "connect return"
66 | test_dict = {'host': "test"}
67 | host_dict = [{'name': "test", 'hostid': '1'}, {'name': "test", 'hostid': '2'}]
68 | action.connect = mock_connect
69 | action.find_hosts = mock.MagicMock(return_value=[host_dict[0]['hostid'],
70 | host_dict[1]['hostid']])
71 | expected_return = {'host_ids': [host_dict[0]['hostid'], host_dict[1]['hostid']]}
72 |
73 | result = action.run(**test_dict)
74 | self.assertEqual(result, expected_return)
75 |
--------------------------------------------------------------------------------
/tests/test_host_get_status.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_get_status import HostGetStatus
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class HostGetStatusTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = HostGetStatus
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'host': "test"}
19 | host_dict = {'name': "test", 'hostid': '1'}
20 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
21 |
22 | with self.assertRaises(URLError):
23 | action.run(**test_dict)
24 |
25 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
26 | def test_run_host_error(self, mock_connect):
27 | action = self.get_action_instance(self.full_config)
28 | mock_connect.return_vaue = "connect return"
29 | test_dict = {'host': "test"}
30 | host_dict = {'name': "test", 'hostid': '1'}
31 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'],
32 | side_effect=ZabbixAPIException('host error'))
33 | action.connect = mock_connect
34 | with self.assertRaises(ZabbixAPIException):
35 | action.run(**test_dict)
36 |
37 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
38 | @mock.patch('lib.actions.ZabbixBaseAction.find_host')
39 | def test_run(self, mock_connect, mock_find_host):
40 | action = self.get_action_instance(self.full_config)
41 | mock_connect.return_vaue = "connect return"
42 | test_dict = {'host': "test"}
43 | host_dict = {'name': "test", 'hostid': '1', 'status': '0'}
44 | action.connect = mock_connect
45 | action.find_host = mock_find_host
46 | action.zabbix_host = host_dict
47 |
48 | result = action.run(**test_dict)
49 | self.assertEqual(result, host_dict['status'])
50 |
--------------------------------------------------------------------------------
/tests/test_host_update_status.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from host_update_status import HostUpdateStatus
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class HostUpdateStatusTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = HostUpdateStatus
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'host': "test", 'status': 1}
19 | host_dict = {'name': "test", 'hostid': '1'}
20 | mock.MagicMock(return_value=host_dict['hostid'])
21 |
22 | with self.assertRaises(URLError):
23 | action.run(**test_dict)
24 |
25 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
26 | def test_run_host_error(self, mock_connect):
27 | action = self.get_action_instance(self.full_config)
28 | mock_connect.return_vaue = "connect return"
29 | test_dict = {'host': "test", 'status': 1}
30 | host_dict = {'name': "test", 'hostid': '1'}
31 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'],
32 | side_effect=ZabbixAPIException('host error'))
33 | action.connect = mock_connect
34 |
35 | with self.assertRaises(ZabbixAPIException):
36 | action.run(**test_dict)
37 |
38 | @mock.patch('lib.actions.ZabbixAPI')
39 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
40 | def test_run(self, mock_connect, mock_client):
41 | action = self.get_action_instance(self.full_config)
42 | mock_connect.return_vaue = "connect return"
43 | test_dict = {'host': "test", 'status': 1}
44 | host_dict = {'name': "test", 'hostid': '1'}
45 | action.connect = mock_connect
46 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
47 | mock_client.host.update.return_value = "update return"
48 | action.client = mock_client
49 |
50 | result = action.run(**test_dict)
51 | mock_client.host.update.assert_called_with(hostid=host_dict['hostid'],
52 | status=test_dict['status'])
53 | self.assertEqual(result, True)
54 |
55 | @mock.patch('lib.actions.ZabbixAPI')
56 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
57 | def test_run_update_error(self, mock_connect, mock_client):
58 | action = self.get_action_instance(self.full_config)
59 | mock_connect.return_vaue = "connect return"
60 | test_dict = {'host': "test", 'status': 1}
61 | host_dict = {'name': "test", 'hostid': '1'}
62 | action.connect = mock_connect
63 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
64 | mock_client.host.update.side_effect = ZabbixAPIException('host error')
65 | mock_client.host.update.return_value = "update return"
66 | action.client = mock_client
67 |
68 | with self.assertRaises(ZabbixAPIException):
69 | action.run(**test_dict)
70 |
--------------------------------------------------------------------------------
/tests/test_maintenance_create_or_update.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from maintenance_create_or_update import MaintenanceCreateOrUpdate
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class MaintenanceCreateOrUpdateTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = MaintenanceCreateOrUpdate
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'host': "test",
19 | 'time_type': 0,
20 | 'maintenance_window_name': "test",
21 | 'maintenance_type': 0,
22 | 'start_date': "2017-11-14 10:40",
23 | 'end_date': "2017-11-14 10:45"}
24 | host_dict = {'name': "test", 'hostid': '1'}
25 | mock.MagicMock(return_value=host_dict['hostid'])
26 |
27 | with self.assertRaises(URLError):
28 | action.run(**test_dict)
29 |
30 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
31 | def test_run_host_error(self, mock_connect):
32 | action = self.get_action_instance(self.full_config)
33 | mock_connect.return_vaue = "connect return"
34 | test_dict = {'host': "test",
35 | 'time_type': 0,
36 | 'maintenance_window_name': "test",
37 | 'maintenance_type': 0,
38 | 'start_date': "2017-11-14 10:40",
39 | 'end_date': "2017-11-14 10:45"}
40 | host_dict = {'name': "test", 'hostid': '1'}
41 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'],
42 | side_effect=ZabbixAPIException('host error'))
43 | action.connect = mock_connect
44 |
45 | with self.assertRaises(ZabbixAPIException):
46 | action.run(**test_dict)
47 |
48 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
49 | def test_run_maintenance_error(self, mock_connect):
50 | action = self.get_action_instance(self.full_config)
51 | mock_connect.return_vaue = "connect return"
52 | test_dict = {'host': "test",
53 | 'time_type': 0,
54 | 'maintenance_window_name': "test",
55 | 'maintenance_type': 0,
56 | 'start_date': "2017-11-14 10:40",
57 | 'end_date': "2017-11-14 10:45"}
58 | host_dict = {'name': "test", 'hostid': '1'}
59 | maintenance_dict = {'maintenanceids': ['1']}
60 | action.connect = mock_connect
61 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
62 | action.maintenance_create_or_update = mock.MagicMock(return_value=maintenance_dict,
63 | side_effect=ZabbixAPIException('maintenance error'))
64 |
65 | with self.assertRaises(ZabbixAPIException):
66 | action.run(**test_dict)
67 |
68 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
69 | def test_run(self, mock_connect):
70 | action = self.get_action_instance(self.full_config)
71 | mock_connect.return_vaue = "connect return"
72 | test_dict = {'host': "test",
73 | 'time_type': 0,
74 | 'maintenance_window_name': "test",
75 | 'maintenance_type': 0,
76 | 'start_date': "2017-11-14 10:40",
77 | 'end_date': "2017-11-14 10:45"}
78 | host_dict = {'name': "test", 'hostid': '1'}
79 | maintenance_dict = {'maintenanceids': ['1']}
80 | expected_return = {'maintenance_id': maintenance_dict['maintenanceids'][0]}
81 | action.connect = mock_connect
82 | action.find_host = mock.MagicMock(return_value=host_dict['hostid'])
83 | action.maintenance_create_or_update = mock.MagicMock(return_value=maintenance_dict)
84 |
85 | result = action.run(**test_dict)
86 | self.assertEqual(result, expected_return)
87 |
--------------------------------------------------------------------------------
/tests/test_maintenance_delete.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from maintenance_delete import MaintenanceDelete
5 |
6 | from six.moves.urllib.error import URLError
7 | from pyzabbix.api import ZabbixAPIException
8 |
9 |
10 | class MaintenanceDeleteTestCase(ZabbixBaseActionTestCase):
11 | __test__ = True
12 | action_cls = MaintenanceDelete
13 |
14 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
15 | def test_run_connection_error(self, mock_connect):
16 | action = self.get_action_instance(self.full_config)
17 | mock_connect.side_effect = URLError('connection error')
18 | test_dict = {'maintenance_window_name': None, 'maintenance_id': '1'}
19 |
20 | with self.assertRaises(URLError):
21 | action.run(**test_dict)
22 |
23 | @mock.patch('lib.actions.ZabbixAPI')
24 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
25 | def test_run_by_id(self, mock_connect, mock_client):
26 | action = self.get_action_instance(self.full_config)
27 | mock_connect.return_vaue = "connect return"
28 | test_dict = {'maintenance_window_name': None, 'maintenance_id': '1'}
29 | action.connect = mock_connect
30 | mock_client.maintenance.delete.return_value = "delete return"
31 | action.client = mock_client
32 |
33 | result = action.run(**test_dict)
34 | mock_client.maintenance.delete.assert_called_with(test_dict['maintenance_id'])
35 | self.assertEqual(result, True)
36 |
37 | @mock.patch('lib.actions.ZabbixAPI')
38 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
39 | def test_run_by_name(self, mock_connect, mock_client):
40 | action = self.get_action_instance(self.full_config)
41 | mock_connect.return_vaue = "connect return"
42 | test_dict = {'maintenance_window_name': "test", 'maintenance_id': None}
43 | maintenance_dict = {'name': "test", 'maintenanceid': 1}
44 | action.connect = mock_connect
45 | action.maintenance_get = mock.MagicMock(return_value=[maintenance_dict])
46 | mock_client.maintenance.delete.return_value = "delete return"
47 | action.client = mock_client
48 |
49 | result = action.run(**test_dict)
50 | mock_client.maintenance.delete.assert_called_with(maintenance_dict['maintenanceid'])
51 | self.assertEqual(result, True)
52 |
53 | @mock.patch('lib.actions.ZabbixAPI')
54 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
55 | def test_run_by_name_no_return_error(self, mock_connect, mock_client):
56 | action = self.get_action_instance(self.full_config)
57 | mock_connect.return_vaue = "connect return"
58 | test_dict = {'maintenance_window_name': "test", 'maintenance_id': None}
59 | action.connect = mock_connect
60 | action.maintenance_get = mock.MagicMock(return_value=[])
61 | mock_client.maintenance.delete.return_value = "delete return"
62 | action.client = mock_client
63 |
64 | with self.assertRaises(ValueError):
65 | action.run(**test_dict)
66 |
67 | @mock.patch('lib.actions.ZabbixAPI')
68 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
69 | def test_run_by_name_to_many_return_error(self, mock_connect, mock_client):
70 | action = self.get_action_instance(self.full_config)
71 | mock_connect.return_vaue = "connect return"
72 | test_dict = {'maintenance_window_name': "test", 'maintenance_id': None}
73 | maintenance_dict = [{'name': "test", 'maintenanceid': 1},
74 | {'name': "test", 'maintenanceid': 2}]
75 | action.connect = mock_connect
76 | action.maintenance_get = mock.MagicMock(return_value=maintenance_dict)
77 | mock_client.maintenance.delete.return_value = "delete return"
78 | action.client = mock_client
79 |
80 | with self.assertRaises(ValueError):
81 | action.run(**test_dict)
82 |
83 | @mock.patch('lib.actions.ZabbixAPI')
84 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
85 | def test_run_value_error(self, mock_connect, mock_client):
86 | action = self.get_action_instance(self.full_config)
87 | mock_connect.return_vaue = "connect return"
88 | test_dict = {'maintenance_window_name': None, 'maintenance_id': None}
89 | action.connect = mock_connect
90 | mock_client.maintenance.delete.return_value = "delete return"
91 | action.client = mock_client
92 |
93 | with self.assertRaises(ValueError):
94 | action.run(**test_dict)
95 |
96 | @mock.patch('lib.actions.ZabbixAPI')
97 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
98 | def test_run_delete_error(self, mock_connect, mock_client):
99 | action = self.get_action_instance(self.full_config)
100 | mock_connect.return_vaue = "connect return"
101 | test_dict = {'maintenance_window_name': None, 'maintenance_id': '1'}
102 | action.connect = mock_connect
103 | mock_client.maintenance.delete.side_effect = ZabbixAPIException('maintenance error')
104 | mock_client.maintenance.delete.return_value = "delete return"
105 | action.client = mock_client
106 |
107 | with self.assertRaises(ZabbixAPIException):
108 | action.run(**test_dict)
109 |
--------------------------------------------------------------------------------
/tests/test_test_credentials.py:
--------------------------------------------------------------------------------
1 | import mock
2 |
3 | from zabbix_base_action_test_case import ZabbixBaseActionTestCase
4 | from test_credentials import TestCredentials
5 |
6 | from pyzabbix.api import ZabbixAPIException
7 |
8 |
9 | class TestCredentialsTestCase(ZabbixBaseActionTestCase):
10 | __test__ = True
11 | action_cls = TestCredentials
12 |
13 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
14 | def test_run(self, mock_connect):
15 | action = self.get_action_instance(self.full_config)
16 | result = action.run()
17 | self.assertEqual(result, True)
18 |
19 | @mock.patch('lib.actions.ZabbixBaseAction.connect')
20 | def test_run_connection_error(self, mock_connect):
21 | action = self.get_action_instance(self.full_config)
22 | mock_connect.side_effect = ZabbixAPIException('login error')
23 | with self.assertRaises(ZabbixAPIException):
24 | action.run()
25 |
--------------------------------------------------------------------------------
/tests/test_tool_register_st2_config_to_zabbix.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import sys
4 | import mock
5 |
6 | from six import StringIO
7 | from unittest import TestCase
8 |
9 | from six.moves.urllib.error import URLError
10 | from pyzabbix.api import ZabbixAPIException
11 |
12 | sys.path.append(os.path.dirname(os.path.realpath(__file__)) + '/../tools/')
13 | import register_st2_config_to_zabbix
14 |
15 |
16 | class TestRegisterMediaType(TestCase):
17 | def setUp(self):
18 | sys.argv = ['register_st2_config_to_zabbix.py']
19 | self.io_stdout = StringIO()
20 | self.io_stderr = StringIO()
21 | sys.stdout = self.io_stdout
22 | sys.stderr = self.io_stderr
23 |
24 | def tearDown(self):
25 | sys.stdout = sys.__stdout__
26 | sys.stderr = sys.__stderr__
27 |
28 | def test_register_mediatype_without_argument(self):
29 | with self.assertRaises(SystemExit):
30 | register_st2_config_to_zabbix.main()
31 |
32 | self.assertTrue(re.match(r".*Zabbix Server URL is not given",
33 | self.io_stderr.getvalue(),
34 | flags=(re.MULTILINE | re.DOTALL)))
35 |
36 | @mock.patch('register_st2_config_to_zabbix.ZabbixAPI')
37 | def test_register_mediatype_to_invalid_zabbix_server(self, mock_client):
38 | sys.argv += ['-z', 'http://invalid-zabbix-host']
39 |
40 | # make an exception that means failure to connect server.
41 | mock_client.side_effect = URLError('connection error')
42 |
43 | with self.assertRaises(SystemExit):
44 | register_st2_config_to_zabbix.main()
45 | self.assertTrue(re.match(r"Failed to connect Zabbix server", self.io_stderr.getvalue()))
46 |
47 | @mock.patch('register_st2_config_to_zabbix.ZabbixAPI')
48 | def test_register_mediatype_with_invalid_authentication(self, mock_client):
49 | sys.argv += ['-z', 'http://invalid-zabbix-host', '-u', 'user', '-p', 'passwd']
50 |
51 | # make an exception that means failure to authenticate with Zabbix-server.
52 | mock_client.side_effect = ZabbixAPIException('auth error')
53 |
54 | with self.assertRaises(SystemExit):
55 | register_st2_config_to_zabbix.main()
56 | self.assertTrue(re.match(r"Failed to authenticate Zabbix", self.io_stderr.getvalue()))
57 |
58 | @mock.patch('register_st2_config_to_zabbix.ZabbixAPI')
59 | def test_register_duplicate_mediatype(self, mock_client):
60 | sys.argv += ['-z', 'http://zabbix-host']
61 | self.is_registered_media = False
62 | self.is_registered_action = False
63 | self.is_called_delete = False
64 |
65 | def side_effect_media(*args, **kwargs):
66 | self.is_registered_media = True
67 |
68 | def side_effect_action(*args, **kwargs):
69 | self.is_registered_action = True
70 |
71 | def side_effect_delete(*args, **kwargs):
72 | self.is_called_delete = True
73 |
74 | # make mock to get target mediatype
75 | mock_obj = mock.Mock()
76 | mock_obj.apiinfo.version.return_value = '3.x'
77 | mock_obj.mediatype.get.return_value = [{
78 | 'type': register_st2_config_to_zabbix.SCRIPT_MEDIA_TYPE,
79 | 'exec_path': register_st2_config_to_zabbix.ST2_DISPATCHER_SCRIPT,
80 | 'mediatypeid': '1',
81 | }]
82 |
83 | # make mock to return no action
84 | mock_obj.action.get.return_value = []
85 | mock_obj.mediatype.update.return_value = {'mediatypeids': ['1']}
86 | mock_client.return_value = mock_obj
87 |
88 | mock_obj.user.addmedia.side_effect = side_effect_media
89 | mock_obj.action.create.side_effect = side_effect_action
90 | mock_obj.action.delete.side_effect = side_effect_delete
91 |
92 | register_st2_config_to_zabbix.main()
93 | self.assertTrue(re.match(r"Success to register the configurations",
94 | self.io_stdout.getvalue()))
95 | self.assertTrue(self.is_registered_media)
96 | self.assertTrue(self.is_registered_action)
97 | self.assertFalse(self.is_called_delete)
98 |
99 | # make mock to return action which is alredy registered
100 | mock_obj.action.get.return_value = [{
101 | 'name': register_st2_config_to_zabbix.ST2_ACTION_NAME,
102 | 'actionid': 1,
103 | }]
104 |
105 | register_st2_config_to_zabbix.main()
106 | self.assertTrue(re.match(r"Success to register the configurations",
107 | self.io_stdout.getvalue()))
108 | self.assertTrue(self.is_registered_media)
109 | self.assertTrue(self.is_registered_action)
110 | self.assertTrue(self.is_called_delete)
111 |
112 | @mock.patch('register_st2_config_to_zabbix.ZabbixAPI')
113 | def test_register_mediatype_successfully(self, mock_client):
114 | sys.argv += ['-z', 'http://zabbix-host']
115 | self.is_registered_media = False
116 | self.is_registered_action = False
117 |
118 | def side_effect_media(*args, **kwargs):
119 | self.is_registered_media = True
120 |
121 | def side_effect_action(*args, **kwargs):
122 | self.is_registered_action = True
123 |
124 | mock_obj = mock.Mock()
125 | mock_obj.apiinfo.version.return_value = '3.x'
126 | mock_obj.mediatype.get.return_value = [
127 | {'type': register_st2_config_to_zabbix.SCRIPT_MEDIA_TYPE,
128 | 'exec_path': 'other-script.sh'},
129 | {'type': 0}
130 | ]
131 | mock_obj.mediatype.create.return_value = {'mediatypeids': ['1']}
132 | mock_obj.action.get.return_value = []
133 | mock_obj.user.addmedia.side_effect = side_effect_media
134 | mock_obj.action.create.side_effect = side_effect_action
135 | mock_client.return_value = mock_obj
136 |
137 | register_st2_config_to_zabbix.main()
138 |
139 | self.assertTrue(re.match(r"Success to register the configurations",
140 | self.io_stdout.getvalue()))
141 | self.assertTrue(self.is_registered_media)
142 | self.assertTrue(self.is_registered_action)
143 |
144 | def test_register_mediatype_with_different_zabbix_version(self):
145 | mock_client = mock.Mock()
146 |
147 | def side_effect_addmedia(*args, **kwargs):
148 | return 'user.addmedia is called'
149 |
150 | def side_effect_userupdate(*args, **kwargs):
151 | return 'user.update is called'
152 |
153 | # set side_effect of caling user.update and user.addmedia API
154 | mock_client.user.addmedia.side_effect = side_effect_addmedia
155 | mock_client.user.update.side_effect = side_effect_userupdate
156 |
157 | # When sending request that changes MediaType to Zabbix3.x, this calls user.addmedia API
158 | mock_client.apiinfo.version.return_value = '3.x.y'
159 | ret = register_st2_config_to_zabbix.register_media_to_admin(mock_client, 1, mock.Mock())
160 | self.assertEqual(ret, 'user.addmedia is called')
161 |
162 | # When sending request that changes MediaType to Zabbix3.x, this calls user.update API
163 | mock_client.apiinfo.version.return_value = '4.x.y'
164 | ret = register_st2_config_to_zabbix.register_media_to_admin(mock_client, 1, mock.Mock())
165 | self.assertEqual(ret, 'user.update is called')
166 |
--------------------------------------------------------------------------------
/tests/test_tool_st2_dispatch.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import mock
4 | import json
5 | import requests
6 | from optparse import OptionParser
7 |
8 | from unittest import TestCase
9 |
10 | sys.path.append(os.path.dirname(os.path.realpath(__file__)) + '/../tools/scripts')
11 |
12 | import st2_dispatch
13 |
14 |
15 | class FakeResponse(object):
16 |
17 | def __init__(self, text, status_code, reason, *args):
18 | self.text = text
19 | self.content = text.encode('utf-8')
20 | self.status_code = status_code
21 | self.reason = reason
22 | if args:
23 | self.headers = args[0]
24 |
25 | def json(self):
26 | return json.loads(self.text)
27 |
28 | def raise_for_status(self):
29 | raise Exception(self.reason)
30 |
31 |
32 | class TestZabbixDispatcher(TestCase):
33 | TOKEN = {
34 | 'user': 'st2admin',
35 | 'token': '44583f15945b4095afbf57058535ca64',
36 | 'expiry': '2017-02-12T00:53:09.632783Z',
37 | 'id': '589e607532ed3535707f10eb',
38 | 'metadata': {}
39 | }
40 |
41 | def setUp(self):
42 | self.parser = OptionParser()
43 |
44 | self.parser.add_option('--userid', dest='st2_userid')
45 | self.parser.add_option('--passwd', dest='st2_passwd')
46 | self.parser.add_option('--api-url', dest='api_url')
47 | self.parser.add_option('--auth-url', dest='auth_url')
48 | self.parser.add_option('--api_key', dest='api_key')
49 | self.parser.add_option('--trigger', dest='trigger', default="zabbix.event_handler")
50 | self.parser.add_option('--alert-sendto', dest="alert_sendto", default="")
51 | self.parser.add_option('--alert-subject', dest="alert_subject", default="")
52 | self.parser.add_option('--alert-message', dest="alert_message", default="")
53 | self.parser.add_option('--skip-config', dest='skip_config', default=True)
54 | self.parser.add_option('--config-file', dest='config_file')
55 |
56 | @mock.patch.object(
57 | requests, 'post',
58 | mock.MagicMock(return_value=FakeResponse(json.dumps(TOKEN), 200, 'OK')))
59 | def test_dispatch_trigger(self):
60 | (options, _) = self.parser.parse_args([
61 | '--userid', 'foo',
62 | '--passwd', 'bar',
63 | '--api-url', 'https://localhost/api/v1',
64 | '--auth-url', 'https://localhost/auth/v1',
65 | ])
66 |
67 | dispatcher = st2_dispatch.ZabbixDispatcher(options)
68 | self.assertEqual(dispatcher.client.token, self.TOKEN['token'])
69 |
70 | resp = dispatcher.dispatch_trigger(args=[
71 | options.api_url,
72 | options.auth_url,
73 | options.st2_userid,
74 | options.st2_passwd,
75 | 'foo', 'bar', 'baz'
76 | ])
77 | self.assertEqual(resp.status_code, 200)
78 |
--------------------------------------------------------------------------------
/tests/zabbix_base_action_test_case.py:
--------------------------------------------------------------------------------
1 | import yaml
2 | import json
3 |
4 | from st2tests.base import BaseActionTestCase
5 |
6 |
7 | class ZabbixBaseActionTestCase(BaseActionTestCase):
8 | __test__ = False
9 |
10 | def setUp(self):
11 | super(ZabbixBaseActionTestCase, self).setUp()
12 |
13 | self._full_config = self.load_yaml('full.yaml')
14 | self._blank_config = self.load_yaml('blank.yaml')
15 |
16 | def load_yaml(self, filename):
17 | return yaml.safe_load(self.get_fixture_content(filename))
18 |
19 | def load_json(self, filename):
20 | return json.loads(self.get_fixture_content(filename))
21 |
22 | @property
23 | def full_config(self):
24 | return self._full_config
25 |
26 | @property
27 | def blank_config(self):
28 | return self._blank_config
29 |
--------------------------------------------------------------------------------
/tools/register_st2_config_to_zabbix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import json
3 | import sys
4 |
5 | from optparse import OptionParser
6 | from zabbix.api import ZabbixAPI
7 | from pyzabbix.api import ZabbixAPIException
8 | from six.moves.urllib.error import URLError
9 |
10 | # This constant describes 'script' value of 'type' property in the MediaType,
11 | # which is specified in the Zabbix API specification.
12 | SCRIPT_MEDIA_TYPE = '1'
13 |
14 | # This is a constant for the metadata of MediaType to be registered
15 | ST2_DISPATCHER_SCRIPT = 'st2_dispatch.py'
16 | ST2_ACTION_NAME = 'Dispatching to StackStorm'
17 |
18 |
19 | def get_options():
20 | parser = OptionParser()
21 |
22 | parser.add_option('-z', '--zabbix-url', dest="z_url",
23 | help="The URL of Zabbix Server")
24 | parser.add_option('-u', '--username', dest="z_userid", default='Admin',
25 | help="Login username to login Zabbix Server")
26 | parser.add_option('-p', '--password', dest="z_passwd", default='zabbix',
27 | help="Password which is associated with the username")
28 | parser.add_option('-s', '--sendto', dest="z_sendto", default='Admin',
29 | help="Address, user name or other identifier of the recipient")
30 |
31 | (options, args) = parser.parse_args()
32 |
33 | if not options.z_url:
34 | parser.error('Zabbix Server URL is not given')
35 |
36 | return (options, args)
37 |
38 |
39 | def is_already_registered_mediatype(client, options):
40 | """
41 | This method checks target MediaType has already been registered, or not.
42 | """
43 | for mtype in client.mediatype.get():
44 | if mtype['type'] == SCRIPT_MEDIA_TYPE and mtype['exec_path'] == ST2_DISPATCHER_SCRIPT:
45 | return mtype['mediatypeid']
46 |
47 |
48 | def is_already_registered_action(client, options):
49 | """
50 | This method checks target Action has already been registered, or not.
51 | """
52 | for action in client.action.get():
53 | if action['name'] == ST2_ACTION_NAME:
54 | return action['actionid']
55 |
56 |
57 | def register_media_type(client, options, mediatype_id=None):
58 | """
59 | This method registers a MediaType which dispatches alert to the StackStorm.
60 | """
61 | mediatype_args = [
62 | '-- CHANGE ME : api_url (e.g. https://st2-node/api/v1)',
63 | '-- CHANGE ME : auth_url (e.g. https://st2-node/auth/v1)',
64 | '-- CHANGE ME : login uername of StackStorm --',
65 | '-- CHANGE ME : login password of StackStorm --',
66 | '{ALERT.SENDTO}',
67 | '{ALERT.SUBJECT}',
68 | '{ALERT.MESSAGE}',
69 | ]
70 |
71 | # send request to register a new MediaType for StackStorm
72 | params = {
73 | 'description': 'StackStorm',
74 | 'type': SCRIPT_MEDIA_TYPE,
75 | 'exec_path': ST2_DISPATCHER_SCRIPT,
76 | 'exec_params': "\n".join(mediatype_args) + "\n",
77 | }
78 | if mediatype_id:
79 | params['mediatypeid'] = mediatype_id
80 |
81 | ret = client.mediatype.update(**params)
82 | else:
83 | ret = client.mediatype.create(**params)
84 |
85 | return ret['mediatypeids'][0]
86 |
87 |
88 | def register_action(client, mediatype_id, options, action_id=None):
89 |
90 | if action_id:
91 | client.action.delete(action_id)
92 |
93 | return client.action.create(**{
94 | 'name': ST2_ACTION_NAME,
95 | 'esc_period': 360,
96 | 'eventsource': 0, # means event created by a trigger
97 | 'def_shortdata': '{TRIGGER.STATUS}: {TRIGGER.NAME}',
98 | 'def_longdata': json.dumps({
99 | 'event': {
100 | 'id': '{EVENT.ID}',
101 | 'time': '{EVENT.TIME}',
102 | },
103 | 'trigger': {
104 | 'id': '{TRIGGER.ID}',
105 | 'name': '{TRIGGER.NAME}',
106 | 'status': '{TRIGGER.STATUS}',
107 | },
108 | 'items': [{
109 | 'name': '{ITEM.NAME%s}' % index,
110 | 'host': '{HOST.NAME%s}' % index,
111 | 'key': '{ITEM.KEY%s}' % index,
112 | 'value': '{ITEM.VALUE%s}' % index
113 | } for index in range(1, 9)],
114 | }),
115 | 'operations': [{
116 | "operationtype": 0,
117 | "esc_period": 0,
118 | "esc_step_from": 1,
119 | "esc_step_to": 1,
120 | "evaltype": 0,
121 | "opmessage_usr": [{"userid": "1"}],
122 | "opmessage": {
123 | "default_msg": 1,
124 | "mediatypeid": mediatype_id,
125 | }
126 | }]
127 | })
128 |
129 |
130 | def register_media_to_admin(client, mediatype_id, options):
131 | major_version = int(client.apiinfo.version()[0])
132 | if major_version >= 4:
133 | # This is because user.addmedia api was removed from Zabbix 4.0.
134 | return client.user.update(**{
135 | "userid": "1",
136 | "user_medias": [{
137 | "mediatypeid": mediatype_id,
138 | "sendto": options.z_sendto,
139 | "active": "0",
140 | "severity": "63",
141 | "period": "1-7,00:00-24:00",
142 | }]
143 | })
144 | else:
145 | return client.user.addmedia(**{
146 | "users": [
147 | {"userid": "1"},
148 | ],
149 | "medias": {
150 | "mediatypeid": mediatype_id,
151 | "sendto": options.z_sendto,
152 | "active": "0",
153 | "severity": "63",
154 | "period": "1-7,00:00-24:00",
155 | }
156 | })
157 |
158 |
159 | def main():
160 | (options, _) = get_options()
161 |
162 | try:
163 | client = ZabbixAPI(url=options.z_url,
164 | user=options.z_userid,
165 | password=options.z_passwd)
166 | except URLError as e:
167 | sys.exit('Failed to connect Zabbix server (%s)' % e)
168 | except ZabbixAPIException as e:
169 | sys.exit('Failed to authenticate Zabbix (%s)' % e)
170 |
171 | # get ID of MediaType for StackStorm if it exists, or None.
172 | mediatype_id = is_already_registered_mediatype(client, options)
173 |
174 | # register a new MediaType or update one which is already registered to dispatch events
175 | # to the StackStorm
176 | mediatype_id = register_media_type(client, options, mediatype_id)
177 |
178 | # get ID of Action for StackStorm if it exists, or None.
179 | action_id = is_already_registered_action(client, options)
180 |
181 | # register a Action which is associated with the registered MediaType
182 | register_action(client, mediatype_id, options, action_id)
183 |
184 | # register a Media to the Admin user
185 | register_media_to_admin(client, mediatype_id, options)
186 |
187 | print('Success to register the configurations for StackStorm to the Zabbix Server.')
188 |
189 |
190 | if __name__ == '__main__':
191 | main()
192 |
--------------------------------------------------------------------------------
/tools/scripts/st2_dispatch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from st2client.base import BaseCLIApp
4 |
5 | from optparse import OptionParser
6 |
7 | import json
8 |
9 |
10 | class ZabbixDispatcher(BaseCLIApp):
11 |
12 | def __init__(self, options):
13 | self.options = options
14 |
15 | # make a client object to connect st2api
16 | self.client = self.get_client(args=options)
17 |
18 | # If no API key was passed, get a token using user/pass
19 | if not self.options.api_key:
20 | self.client.token = self._get_auth_token(client=self.client,
21 | username=options.st2_userid,
22 | password=options.st2_passwd,
23 | cache_token=False)
24 |
25 | def dispatch_trigger(self, args):
26 |
27 | # Validate if the Alert message is a valid JSON List or Dict and replace
28 | # alert_message (string)with an object so that its correctly formatted.
29 | try:
30 | json_alert = json.loads(self.options.alert_message)
31 | except:
32 | pass
33 | else:
34 | setattr(self.options, 'alert_message', json_alert)
35 |
36 | body = {
37 | 'trigger': self.options.trigger,
38 | 'payload': {
39 | 'alert_sendto': self.options.alert_sendto,
40 | 'alert_subject': self.options.alert_subject,
41 | 'alert_message': self.options.alert_message,
42 | 'extra_args': args,
43 | },
44 | }
45 |
46 | # API Key is preferred over User/Pass when both are present.
47 | if self.options.api_key:
48 | print('ST2 Auth Method: API Key')
49 | auth_method = 'St2-Api-Key'
50 | auth_value = self.options.api_key
51 | else:
52 | print('ST2 Auth Method: Auth Token')
53 | auth_method = 'X-Auth-Token'
54 | auth_value = self.client.token
55 |
56 | # send request to st2api to dispatch trigger of Zabbix
57 | return self.client.managers['Webhook'].client.post('/webhooks/st2', body, headers={
58 | 'Content-Type': 'application/json', auth_method: auth_value})
59 |
60 |
61 | def get_options():
62 | parser = OptionParser()
63 |
64 | # Default values will be overridden by JSON Dict or poitional args.
65 | # If a default is defined but its not a required opt (API key Vs User/Pass)
66 | # it can cause issues.
67 | parser.add_option('--st2-userid', dest="st2_userid", default="st2admin",
68 | help="Login username of StackStorm")
69 | parser.add_option('--st2-passwd', dest="st2_passwd", default="",
70 | help="Login password associated with the user")
71 | parser.add_option('--st2-api-url', dest="api_url",
72 | help="Endpoint URL for API")
73 | parser.add_option('--st2-auth-url', dest="auth_url",
74 | help="Endpoint URL for auth")
75 | parser.add_option('--api-key', dest="api_key",
76 | help="ST2 API Key to be used when no user/pass defined")
77 | parser.add_option('--alert-sendto', dest="alert_sendto", default="",
78 | help="'Send to' value from user media configuration of Zabbix")
79 | parser.add_option('--alert-subject', dest="alert_subject", default="",
80 | help="'Default subject' value from action configuration of Zabbix")
81 | parser.add_option('--alert-message', dest="alert_message", default="",
82 | help="'Default message' value from action configuration of Zabbix")
83 | parser.add_option('--trigger', dest="trigger", default="zabbix.event_handler",
84 | help='Set the trigger name that dispatch should send to on St2')
85 | parser.add_option('--skip-config', dest="skip_config", default=False, action='store_true',
86 | help='Do NOT parse and use the CLI config file')
87 | parser.add_option('--config-file', dest="config_file",
88 | help='Path to the CLI config file')
89 |
90 | # Zabbix send argument as one string even though it includes whitespace
91 | # (like $ st2_dispatch.py "foo bar" "hoge fuga" ...).
92 | # And we can't specify keyward argument, we can only specify args.
93 | #
94 | # So it's hard for us to parse the argument of zabbix mediatype using optparse.
95 | # Then, I decided to fix the order of the CLI arguemnts.
96 | #
97 | # See am_prepare_mediatype_exec_command in alert_manager.c in Zabbix src
98 |
99 | (options, args) = parser.parse_args()
100 |
101 | # Check if the very first positional argument is a valid JSON Dict.
102 | try:
103 | param_object = json.loads(args[0])
104 | except:
105 | # First arg is not a JSON dict, assuming user/pass configuration
106 | arg_list = ['api_url', 'auth_url', 'st2_userid', 'st2_passwd',
107 | 'alert_sendto', 'alert_subject', 'alert_message']
108 | # Parse remaining positional args based on arg_list
109 | for index, param in enumerate(arg_list):
110 | if len(args) > index and args[index]:
111 | setattr(options, param, args[index])
112 |
113 | return (options, args[len(arg_list):])
114 |
115 | else:
116 | # First arg is a JSON dict, assuming apikey only
117 | arg_list = ['alert_sendto', 'alert_subject', 'alert_message']
118 | # Since arg[0] is a JSON dict and we are handling it specifically,
119 | # remove it from the list
120 | args.pop(0)
121 | # Assign all key/val in param_object to options
122 | for k, v in param_object.items():
123 | setattr(options, k, v)
124 | # Parse remaining positional args based on arg_list
125 | for index, param in enumerate(arg_list):
126 | if len(args) > index and args[index]:
127 | setattr(options, param, args[index])
128 |
129 | return (options, args[len(arg_list):])
130 |
131 |
132 | def main():
133 | # Parse and get arguments
134 | print('Parsing Options')
135 | (options, args) = get_options()
136 |
137 | # Instantiate st2 client and prepare data for dispatch to st2
138 | print('Preparing Dispatcher')
139 | dispatcher = ZabbixDispatcher(options)
140 |
141 | # Dispatch data to trigger on st2 (default zabbix.event_handler)
142 | print('Dispatching to ST2')
143 | dispatcher.dispatch_trigger(args)
144 |
145 |
146 | if __name__ == '__main__':
147 | main()
148 |
--------------------------------------------------------------------------------
/triggers/event_handler.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: event_handler
3 | pack: zabbix
4 | description: 'Trigger type for zabbix event handler.'
5 | payload_schema:
6 | type: object
7 | properties:
8 | alert_sendto:
9 | type: string
10 | alert_subject:
11 | type: string
12 | alert_message:
13 | anyOf:
14 | - type: array
15 | - type: object
16 | - type: string
17 | extra_args:
18 | type: array
19 | items:
20 | type: string
21 |
--------------------------------------------------------------------------------
/zabbix.yaml.example:
--------------------------------------------------------------------------------
1 | ---
2 | zabbix:
3 | url: http://localhost/zabbix
4 | username: Admin
5 | password: zabbix
6 |
--------------------------------------------------------------------------------