├── requirements-doc.txt ├── setup.cfg ├── setup.py ├── CHANGELOG.md ├── .github └── workflows │ ├── publish-api-docs.yml │ ├── build-and-publish-package.yml │ └── run-e2e-tests.yml ├── check_mk_web_api ├── exception.py ├── test_WebApi.py └── __init__.py ├── LICENSE ├── doc └── pydocmd.yml ├── .gitignore └── README.md /requirements-doc.txt: -------------------------------------------------------------------------------- 1 | pydoc-markdown==2.0.1 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [metadata] 5 | description-file = README.md 6 | 7 | [tool:pytest] 8 | addopts = --ignore=setup.py --verbose 9 | python_files = check_mk_web_api/*.py 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='check_mk_web_api', 5 | packages=['check_mk_web_api'], 6 | version='1.9.0', 7 | description='Library to talk to Check_Mk Web API', 8 | author='Max Brenner', 9 | author_email='xamrennerb@gmail.com', 10 | url='https://github.com/brennerm/check-mk-web-api', 11 | download_url='https://github.com/brennerm/check-mk-web-api/archive/1.9.0.tar.gz', 12 | install_requires=['enum34;python_version<"3.4"', 'six'], 13 | setup_requires=['pytest-runner'], 14 | tests_require=['pytest'], 15 | keywords=['check_mk', 'api', 'monitoring'] 16 | ) 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [1.9.0] 9 | 10 | ### Added 11 | 12 | - add support for `get_all_sites` method [#37](https://github.com/brennerm/check-mk-web-api/pull/37) 13 | - add support for `test_bulk_discovery_start` ´ and `test_bulk_discovery_status` method [#34](https://github.com/brennerm/check-mk-web-api/pull/34) 14 | 15 | ## [1.8.0] 16 | 17 | - beginning of changelog 18 | -------------------------------------------------------------------------------- /.github/workflows/publish-api-docs.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: setup Python 12 | uses: actions/setup-python@v2 13 | with: 14 | python-version: '3.6' 15 | - name: install doc requirements 16 | run: pip install -r requirements-doc.txt 17 | - name: build docs 18 | working-directory: doc 19 | run: pydocmd build 20 | - name: publish docs 21 | uses: JamesIves/github-pages-deploy-action@4.0.0 22 | with: 23 | branch: gh-pages 24 | folder: doc/_build/site 25 | -------------------------------------------------------------------------------- /check_mk_web_api/exception.py: -------------------------------------------------------------------------------- 1 | class CheckMkWebApiException(Exception): 2 | """ 3 | Exception for result_code != 0 in Check_Mk Web API response 4 | """ 5 | 6 | 7 | class CheckMkWebApiResponseException(Exception): 8 | """ 9 | This exception is being thrown when the Check_Mk Web API responds with a HTTP status code != 200 10 | 11 | # Arguments 12 | response (http.client.HTTPResponse): response that we received from Check_Mk Web API 13 | """ 14 | def __init__(self, response): 15 | self.response = response 16 | 17 | 18 | class CheckMkWebApiAuthenticationException(Exception): 19 | """ 20 | This exception is being thrown when the Check_Mk Web API responds with an authentication error 21 | """ 22 | pass 23 | -------------------------------------------------------------------------------- /.github/workflows/build-and-publish-package.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | branches: 4 | - master 5 | push: 6 | branches: 7 | - master 8 | tags: 9 | - "*" 10 | 11 | jobs: 12 | publish: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: setup Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: '3.6' 20 | - name: install wheel 21 | run: pip install wheel 22 | - name: build package 23 | run: python setup.py bdist_wheel 24 | - name: publish package 25 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 26 | uses: pypa/gh-action-pypi-publish@v1.4.2 27 | with: 28 | user: __token__ 29 | password: ${{ secrets.PYPI_API_TOKEN }} 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Max 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc/pydocmd.yml: -------------------------------------------------------------------------------- 1 | site_name: "Check_MK Web API" 2 | repo_url: https://github.com/brennerm/check-mk-web-api 3 | 4 | # This tells pydocmd which pages to generate from which Python modules, 5 | # functions and classes. At the first level is the page name, below that 6 | # is a tree of Python member names (modules, classes, etc.) that should be 7 | # documented. Higher indentation leads to smaller header size. 8 | generate: 9 | - check_mk_web_api.md: 10 | - check_mk_web_api.WebApi+ 11 | - exceptions.md: 12 | - check_mk_web_api.exception+ 13 | 14 | # MkDocs pages configuration. The `<<` operator is sugar added by pydocmd 15 | # that allows you to use an external Markdown file (eg. your project's README) 16 | # in the documentation. The path must be relative to current working directory. 17 | pages: 18 | - Home: index.md << ../README.md 19 | - API Documentation: 20 | - WebAPI: check_mk_web_api.md 21 | - Exceptions: exceptions.md 22 | 23 | # These options all show off their default values. You don't have to add 24 | # them to your configuration if you're fine with the default. 25 | 26 | # Additional search path for your Python module. If you use Pydocmd from a 27 | # subdirectory of your project (eg. docs/), you may want to add the parent 28 | # directory here. 29 | additional_search_paths: 30 | - .. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # MkDocs documentation 69 | docs/__build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # IPython Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # check-mk-web-api [![Build Status](https://travis-ci.org/brennerm/check-mk-web-api.svg?branch=master)](https://travis-ci.org/brennerm/check-mk-web-api) 2 | ## supported Checkmk versions 3 | - 1.6.0 4 | - 1.5.0 5 | 6 | ## Installation 7 | - From source code 8 | ``` 9 | git clone https://github.com/brennerm/check-mk-web-api 10 | cd check-mk-web-api 11 | sudo python setup.py install 12 | ``` 13 | 14 | - With pip 15 | ``` 16 | pip install check_mk_web_api 17 | ``` 18 | 19 | ## [API Documentation](https://brennerm.github.io/check-mk-web-api/check_mk_web_api/) 20 | 21 | ## Quickstart 22 | #### Initialization 23 | ``` 24 | import check_mk_web_api 25 | api = check_mk_web_api.WebApi('http://checkmk.company.com/check_mk/webapi.py', username='automation', secret='123456') 26 | ``` 27 | 28 | #### Add Host 29 | ``` 30 | >>> api.add_host('webserver00.com') 31 | ``` 32 | 33 | #### Add Host in an existing WATO folder 34 | ``` 35 | >>> api.add_host('webserver00.com', folder='webservers') 36 | ``` 37 | Note there is no leading '/' on the folder name. 38 | 39 | #### Edit Host 40 | ``` 41 | >>> api.edit_host('webserver00.com', ipaddress='192.168.0.100') 42 | ``` 43 | 44 | #### Delete Host 45 | ``` 46 | >>> api.delete_host('webserver00.com') 47 | ``` 48 | 49 | #### Delete Hosts 50 | ``` 51 | >>> api.delete_hosts(['webserver00.com', 'webserver01.com' ]) 52 | ``` 53 | 54 | #### Get Host 55 | ``` 56 | >>> api.get_host('webserver00.com') 57 | { 58 | 'hostname': 'webserver00.com', 59 | 'attributes': { 60 | 'ipaddress': '192.168.0.100' 61 | }, 62 | 'path': '' 63 | } 64 | ``` 65 | 66 | #### Get All Hosts 67 | ``` 68 | >>> api.get_all_hosts() 69 | { 70 | 'webserver00.com': { 71 | 'hostname': 'webserver00.com', 72 | 'attributes': { 73 | 'ipaddress': '192.168.0.100' 74 | }, 75 | 'path': '' 76 | }, 77 | 'webserver01.com': { 78 | 'hostname': 'webserver01.com', 79 | 'attributes': { 80 | 'ipaddress': '192.168.0.101' 81 | }, 82 | 'path': '' 83 | } 84 | } 85 | ``` 86 | 87 | #### Discover Services 88 | ``` 89 | >>> api.discover_services('webserver00.com') 90 | {'removed': '0', 'new_count': '16', 'added': '16', 'kept': '0'} 91 | ``` 92 | 93 | #### Bake Agents 94 | ``` 95 | >>> api.bake_agents() 96 | ``` 97 | 98 | #### Activate Changes 99 | ``` 100 | >>> api.activate_changes() 101 | ``` 102 | -------------------------------------------------------------------------------- /.github/workflows/run-e2e-tests.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | branches: 4 | - master 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | compile_code: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python_version: 15 | - 2.7 16 | - 3.6 17 | - 3.7 18 | - 3.8 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: setup Python 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python_version }} 25 | - name: compile code 26 | run: python -m py_compile check_mk_web_api/*.py 27 | 28 | run_e2e: 29 | runs-on: ubuntu-latest 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | checkmk_version: 34 | - "1.5" 35 | - "1.6" 36 | - "2.0" 37 | steps: 38 | - uses: actions/checkout@v2 39 | - name: setup Python 40 | uses: actions/setup-python@v2 41 | with: 42 | python-version: '3.6' 43 | - name: start and preconfigure checkmk 44 | run: | 45 | docker run -d --rm -p 127.0.0.1:8080:5000 --name check-mk checkmk/check-mk-raw:${{ matrix.checkmk_version }}.0-latest 46 | set +e 47 | 48 | max_retry=300 49 | counter=0 50 | until docker exec -u cmk check-mk bash -c "echo automation-secret > /omd/sites/cmk/var/check_mk/web/automation/automation.secret" > /dev/null 51 | do 52 | sleep 1 53 | [[ counter -eq $max_retry ]] && echo "failed to set password after $counter tries...giving up" && exit 1 54 | echo "#$counter: failed to set password. trying again..." 55 | ((counter++)) 56 | done 57 | echo Successfully set automation user password 58 | 59 | - name: execute E2E tests 60 | run: | 61 | CHECK_MK_URL=http://localhost:8080/cmk/check_mk CHECK_MK_USER=automation CHECK_MK_SECRET=automation-secret CHECK_MK_VERSION="${{ matrix.checkmk_version }}" python setup.py test 62 | -------------------------------------------------------------------------------- /check_mk_web_api/test_WebApi.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | import string 4 | import time 5 | 6 | import pytest 7 | 8 | from check_mk_web_api import WebApi, CheckMkWebApiException 9 | 10 | api = WebApi( 11 | os.environ['CHECK_MK_URL'], 12 | os.environ['CHECK_MK_USER'], 13 | os.environ['CHECK_MK_SECRET'] 14 | ) 15 | 16 | 17 | def setup(): 18 | api.delete_all_hosts() 19 | api.delete_all_hostgroups() 20 | api.delete_all_servicegroups() 21 | 22 | for group in api.get_all_contactgroups(): 23 | if group != 'all': 24 | api.delete_contactgroup(group) 25 | 26 | for user_id in api.get_all_users(): 27 | if user_id != 'cmkadmin' and user_id != os.environ['CHECK_MK_USER']: 28 | api.delete_user(user_id) 29 | 30 | for folder in api.get_all_folders(): 31 | if folder != '': 32 | api.delete_folder(folder) 33 | 34 | 35 | def test_add_host(): 36 | api.add_host('host00') 37 | assert 'host00' in api.get_all_hosts() 38 | 39 | 40 | def test_add_duplicate_host(): 41 | with pytest.raises(CheckMkWebApiException): 42 | api.add_host('host00') 43 | api.add_host('host00') 44 | 45 | 46 | def test_edit_host(): 47 | api.add_host('host00', ipaddress='192.168.0.100') 48 | assert api.get_host('host00')['attributes']['ipaddress'] == '192.168.0.100' 49 | 50 | api.edit_host('host00', ipaddress='192.168.0.101') 51 | assert api.get_host('host00')['attributes']['ipaddress'] == '192.168.0.101' 52 | 53 | 54 | def test_unset_host_attribute(): 55 | api.add_host('host00', ipaddress='192.168.0.100') 56 | assert api.get_host('host00')['attributes']['ipaddress'] == '192.168.0.100' 57 | api.edit_host('host00', unset_attributes=['ipaddress']) 58 | assert 'ipaddress' not in api.get_host('host00')['attributes'] 59 | 60 | 61 | def test_edit_nonexistent_host(): 62 | with pytest.raises(CheckMkWebApiException): 63 | api.edit_host('host00', ipaddress='192.168.0.101') 64 | 65 | 66 | def test_get_host(): 67 | api.add_host('host00') 68 | assert api.get_host('host00')['hostname'] == 'host00' 69 | 70 | 71 | def test_get_nonexistent_host(): 72 | with pytest.raises(CheckMkWebApiException): 73 | api.get_host('host00') 74 | 75 | 76 | def test_get_all_hosts(): 77 | api.add_host('host00') 78 | api.add_host('host01') 79 | 80 | all_hosts = api.get_all_hosts() 81 | assert len(all_hosts) == 2 82 | assert 'host00' in all_hosts 83 | assert 'host01' in all_hosts 84 | 85 | 86 | def test_get_hosts_by_folder(): 87 | api.add_folder('test') 88 | api.add_host('host00', 'test') 89 | api.add_host('host01', 'test') 90 | 91 | hosts = api.get_hosts_by_folder('test') 92 | assert len(hosts) == 2 93 | assert 'host00' in hosts 94 | assert 'host01' in hosts 95 | 96 | 97 | def test_delete_host(): 98 | api.add_host('host00') 99 | assert len(api.get_all_hosts()) == 1 100 | 101 | api.delete_host('host00') 102 | assert len(api.get_all_hosts()) == 0 103 | 104 | 105 | def test_delete_nonexistent_host(): 106 | with pytest.raises(CheckMkWebApiException): 107 | api.delete_host('host00') 108 | 109 | 110 | def test_delete_all_hosts(): 111 | api.add_host('host00') 112 | api.add_host('host01') 113 | assert len(api.get_all_hosts()) == 2 114 | 115 | api.delete_all_hosts() 116 | assert len(api.get_all_hosts()) == 0 117 | 118 | 119 | def test_delete_hosts(): 120 | api.add_host('host00') 121 | api.add_host('host01') 122 | api.add_host('host02') 123 | assert len(api.get_all_hosts()) == 3 124 | 125 | api.delete_hosts(['host00', 'host01']) 126 | assert len(api.get_all_hosts()) == 1 127 | 128 | 129 | def test_discover_services(): 130 | api.add_host('localhost') 131 | result = api.discover_services('localhost') 132 | 133 | assert int(result['added']) >= 0 134 | assert int(result['removed']) >= 0 135 | assert int(result['kept']) >= 0 136 | assert int(result['new_count']) >= 0 137 | 138 | 139 | def test_discover_services_for_nonexistent_host(): 140 | with pytest.raises(CheckMkWebApiException): 141 | api.discover_services('localhost') 142 | 143 | 144 | def test_get_user(): 145 | api.add_user('user00', 'User 00', 'p4ssw0rd') 146 | assert api.get_user('user00')['alias'] == 'User 00' 147 | 148 | 149 | def test_get_all_users(): 150 | api.add_user('user00', 'User 00', 'p4ssw0rd') 151 | api.add_user('user01', 'User 01', 'p4ssw0rd') 152 | 153 | users = api.get_all_users() 154 | assert 'user00' in users 155 | assert 'user01' in users 156 | 157 | 158 | def test_add_user(): 159 | api.add_user('user00', 'User 00', 'p4ssw0rd') 160 | assert 'user00' in api.get_all_users() 161 | 162 | 163 | def test_add_automation_user(): 164 | api.add_automation_user('automation00', 'Automation 00', 's3cr3t1234') 165 | assert 'automation00' in api.get_all_users() 166 | 167 | 168 | def test_add_duplicate_user(): 169 | with pytest.raises(CheckMkWebApiException): 170 | api.add_user('user00', 'User 00', 'p4ssw0rd') 171 | api.add_user('user00', 'User 00', 'p4ssw0rd') 172 | 173 | 174 | def test_add_duplicate_automation_user(): 175 | with pytest.raises(CheckMkWebApiException): 176 | api.add_automation_user('automation00', 'Automation 00', 's3cr3t1234') 177 | api.add_automation_user('automation00', 'Automation 00', 's3cr3t1234') 178 | 179 | 180 | def test_edit_user(): 181 | api.add_user('user00', 'User 00', 'p4ssw0rd') 182 | assert api.get_all_users()['user00']['alias'] == 'User 00' 183 | 184 | api.edit_user('user00', {'alias': 'User 0'}) 185 | assert api.get_all_users()['user00']['alias'] == 'User 0' 186 | 187 | 188 | def test_unset_user_attribute(): 189 | api.add_user('user00', 'User 00', 'p4ssw0rd', pager='49123456789') 190 | assert api.get_all_users()['user00']['pager'] == '49123456789' 191 | api.edit_user('user00', {}, unset_attributes=['pager']) 192 | assert 'pager' not in api.get_all_users()['user00'] 193 | 194 | 195 | def test_edit_nonexistent_user(): 196 | with pytest.raises(CheckMkWebApiException): 197 | api.edit_user('user00', {}) 198 | 199 | 200 | def test_delete_user(): 201 | api.add_user('user00', 'User 00', 'p4ssw0rd') 202 | assert 'user00' in api.get_all_users() 203 | 204 | api.delete_user('user00') 205 | assert 'user00' not in api.get_all_users() 206 | 207 | 208 | def test_delete_nonexistent_user(): 209 | with pytest.raises(CheckMkWebApiException): 210 | api.delete_user('user00') 211 | 212 | 213 | def test_get_folder(): 214 | api.add_folder('productive') 215 | assert api.get_folder('productive') 216 | 217 | 218 | def test_get_nonexistent_folder(): 219 | with pytest.raises(CheckMkWebApiException): 220 | assert api.get_folder('productive') 221 | 222 | 223 | def test_get_all_folders(): 224 | api.add_folder('productive') 225 | api.add_folder('testing') 226 | 227 | folders = api.get_all_folders() 228 | assert 'productive' in folders 229 | assert 'testing' in folders 230 | 231 | 232 | def test_add_folder(): 233 | api.add_folder('productive') 234 | assert 'productive' in api.get_all_folders() 235 | 236 | 237 | def test_edit_folder(): 238 | api.add_folder('productive', snmp_community='public') 239 | assert api.get_folder('productive')[ 240 | 'attributes']['snmp_community'] == 'public' 241 | 242 | api.edit_folder('productive', snmp_community='private') 243 | assert api.get_folder('productive')[ 244 | 'attributes']['snmp_community'] == 'private' 245 | 246 | 247 | def test_edit_nonexistent_folder(): 248 | with pytest.raises(CheckMkWebApiException): 249 | assert api.edit_folder('productive') 250 | 251 | 252 | def test_delete_folder(): 253 | api.add_folder('productive') 254 | assert 'productive' in api.get_all_folders() 255 | 256 | api.delete_folder('productive') 257 | assert 'productive' not in api.get_all_folders() 258 | 259 | 260 | def test_delete_nonexistent_folder(): 261 | with pytest.raises(CheckMkWebApiException): 262 | api.delete_folder('productive') 263 | 264 | 265 | def test_get_contactgroup(): 266 | api.add_contactgroup('user', 'User') 267 | assert api.get_contactgroup('user') 268 | 269 | 270 | def test_get_all_contactgroups(): 271 | api.add_contactgroup('user', 'User') 272 | api.add_contactgroup('admin', 'Admin') 273 | groups = api.get_all_contactgroups() 274 | assert 'user' in groups 275 | assert 'admin' in groups 276 | 277 | 278 | def test_get_nonexistent_contactgroup(): 279 | with pytest.raises(KeyError): 280 | api.get_contactgroup('user') 281 | 282 | 283 | def test_add_contactgroup(): 284 | api.add_contactgroup('user', 'User') 285 | assert api.get_contactgroup('user')['alias'] == 'User' 286 | 287 | 288 | def test_add_duplicate_contactgroup(): 289 | with pytest.raises(CheckMkWebApiException): 290 | api.add_contactgroup('user', 'User') 291 | api.add_contactgroup('user', 'User') 292 | 293 | 294 | def test_edit_contactgroup(): 295 | api.add_contactgroup('user', 'User') 296 | assert api.get_contactgroup('user')['alias'] == 'User' 297 | api.edit_contactgroup('user', 'Users') 298 | assert api.get_contactgroup('user')['alias'] == 'Users' 299 | 300 | 301 | def test_edit_nonexisting_contactgroup(): 302 | with pytest.raises(CheckMkWebApiException): 303 | api.edit_contactgroup('user', 'Users') 304 | 305 | 306 | def test_delete_contactgroup(): 307 | api.add_contactgroup('user', 'User') 308 | assert 'user' in api.get_all_contactgroups() 309 | api.delete_contactgroup('user') 310 | assert 'user' not in api.get_all_contactgroups() 311 | 312 | 313 | def test_delete_nonexistent_contactgroup(): 314 | with pytest.raises(CheckMkWebApiException): 315 | api.delete_contactgroup('user') 316 | 317 | 318 | def test_get_hostgroup(): 319 | api.add_hostgroup('vm', 'VM') 320 | api.get_hostgroup('vm') 321 | 322 | 323 | def test_get_all_hostgroups(): 324 | api.add_hostgroup('vm', 'VM') 325 | api.add_hostgroup('physical', 'Physical') 326 | groups = api.get_all_hostgroups() 327 | assert 'vm' in groups 328 | assert 'physical' in groups 329 | 330 | 331 | def test_get_nonexistent_hostgroup(): 332 | with pytest.raises(KeyError): 333 | api.get_hostgroup('vm') 334 | 335 | 336 | def test_add_hostgroup(): 337 | api.add_hostgroup('vm', 'VM') 338 | assert api.get_hostgroup('vm')['alias'] == 'VM' 339 | 340 | 341 | def test_add_duplicate_hostgroup(): 342 | with pytest.raises(CheckMkWebApiException): 343 | api.add_hostgroup('vm', 'VM') 344 | api.add_hostgroup('vm', 'VM') 345 | 346 | 347 | def test_edit_hostgroup(): 348 | api.add_hostgroup('vm', 'VM') 349 | assert api.get_hostgroup('vm')['alias'] == 'VM' 350 | api.edit_hostgroup('vm', 'VMs') 351 | assert api.get_hostgroup('vm')['alias'] == 'VMs' 352 | 353 | 354 | def test_edit_nonexisting_hostgroup(): 355 | with pytest.raises(CheckMkWebApiException): 356 | api.edit_hostgroup('vm', 'VM') 357 | 358 | 359 | def test_delete_hostgroup(): 360 | api.add_hostgroup('vm', 'VM') 361 | assert 'vm' in api.get_all_hostgroups() 362 | api.delete_hostgroup('vm') 363 | assert 'vm' not in api.get_all_hostgroups() 364 | 365 | 366 | def test_delete_nonexistent_hostgroup(): 367 | with pytest.raises(CheckMkWebApiException): 368 | api.delete_hostgroup('vm') 369 | 370 | 371 | def test_get_servicegroup(): 372 | api.add_servicegroup('db', 'Database') 373 | assert api.get_servicegroup('db') 374 | 375 | 376 | def test_get_all_servicegroups(): 377 | api.add_servicegroup('db', 'Database') 378 | api.add_servicegroup('web', 'Webserver') 379 | groups = api.get_all_servicegroups() 380 | assert 'db' in groups 381 | assert 'web' in groups 382 | 383 | 384 | def test_get_nonexistent_servicegroup(): 385 | with pytest.raises(KeyError): 386 | api.get_servicegroup('db') 387 | 388 | 389 | def test_add_servicegroup(): 390 | api.add_servicegroup('db', 'Database') 391 | assert api.get_servicegroup('db')['alias'] == 'Database' 392 | 393 | 394 | def test_add_duplicate_servicegroup(): 395 | with pytest.raises(CheckMkWebApiException): 396 | api.add_servicegroup('db', 'Database') 397 | api.add_servicegroup('db', 'Database') 398 | 399 | 400 | def test_edit_servicegroup(): 401 | api.add_servicegroup('db', 'Database') 402 | assert api.get_servicegroup('db')['alias'] == 'Database' 403 | api.edit_servicegroup('db', 'Databases') 404 | assert api.get_servicegroup('db')['alias'] == 'Databases' 405 | 406 | 407 | def test_edit_nonexisting_servicegroup(): 408 | with pytest.raises(CheckMkWebApiException): 409 | api.edit_servicegroup('db', 'Database') 410 | 411 | 412 | def test_delete_servicegroup(): 413 | api.add_servicegroup('db', 'Database') 414 | assert 'db' in api.get_all_servicegroups() 415 | api.delete_servicegroup('db') 416 | assert 'db' not in api.get_all_servicegroups() 417 | 418 | 419 | def test_delete_nonexistent_servicegroup(): 420 | with pytest.raises(CheckMkWebApiException): 421 | api.delete_servicegroup('db') 422 | 423 | 424 | def test_get_hosttags(): 425 | assert api.get_hosttags() 426 | 427 | 428 | def test_set_hosttags(): 429 | current_tags = api.get_hosttags() 430 | current_tags["tag_groups"].append({ 431 | "id": ''.join(random.choice(string.ascii_lowercase) for i in range(10)), 432 | "title": "Test", 433 | "tags": [ 434 | { 435 | "aux_tags": [], 436 | "id": ''.join(random.choice(string.ascii_lowercase) for i in range(10)), 437 | "title": "Test & test" 438 | } 439 | ] 440 | }) 441 | api.set_hosttags(current_tags) 442 | 443 | 444 | def test_add_aux_tag(): 445 | current_tags = api.get_hosttags()['aux_tags'] 446 | api.add_aux_tag(''.join(random.choice(string.ascii_lowercase) 447 | for i in range(10)), "Web Server", "Service Type") 448 | new_tags = api.get_hosttags()['aux_tags'] 449 | assert (len(new_tags) - len(current_tags)) == 1 450 | 451 | 452 | def test_add_duplicate_aux_tag(): 453 | identifier = ''.join(random.choice(string.ascii_lowercase) 454 | for i in range(10)) 455 | api.add_aux_tag(identifier, "Web Server", "Service Type") 456 | with pytest.raises(CheckMkWebApiException): 457 | api.add_aux_tag(identifier, "Web Server", "Service Type") 458 | 459 | 460 | def test_add_tag_group(): 461 | current_groups = api.get_hosttags()['tag_groups'] 462 | api.add_tag_group(''.join(random.choice(string.ascii_lowercase) for i in range(10)), "Web Server", [{ 463 | "aux_tags": [], 464 | "id": ''.join(random.choice(string.ascii_lowercase) for i in range(10)), 465 | "title": "Test & test" 466 | }]) 467 | new_groups = api.get_hosttags()['tag_groups'] 468 | assert (len(new_groups) - len(current_groups)) == 1 469 | 470 | 471 | def test_add_duplicate_tag_group(): 472 | identifier = ''.join(random.choice(string.ascii_lowercase) 473 | for i in range(10)) 474 | 475 | api.add_tag_group(identifier, "Web Server", [{ 476 | "aux_tags": [], 477 | "id": ''.join(random.choice(string.ascii_lowercase) for i in range(10)), 478 | "title": "Test & test" 479 | }]) 480 | with pytest.raises(CheckMkWebApiException): 481 | api.add_tag_group(identifier, "Web Server", [{ 482 | "aux_tags": [], 483 | "id": ''.join(random.choice(string.ascii_lowercase) for i in range(10)), 484 | "title": "Test & test" 485 | }]) 486 | 487 | 488 | def test_get_ruleset(): 489 | assert api.get_ruleset('checkgroup_parameters:hw_fans_perc') 490 | 491 | 492 | def test_get_nonexistent_rulesets(): 493 | with pytest.raises(CheckMkWebApiException): 494 | api.get_ruleset('nonexistent') 495 | 496 | 497 | def test_set_nonexistent_rulesets(): 498 | with pytest.raises(CheckMkWebApiException): 499 | api.set_ruleset('nonexistent', {}) 500 | 501 | 502 | def test_get_rulesets(): 503 | assert api.get_rulesets() 504 | 505 | 506 | def test_get_site(): 507 | assert api.get_site('cmk') 508 | 509 | 510 | def test_set_site(): 511 | random_alias = 'alias_' + \ 512 | ''.join(random.choices(string.ascii_uppercase + string.digits, k=10)) 513 | config = api.get_site('cmk')['site_config'] 514 | config['alias'] = random_alias 515 | 516 | api.set_site('cmk', config) 517 | assert api.get_site('cmk')['site_config']['alias'] == random_alias 518 | 519 | 520 | @pytest.mark.skip(reason="bug in Check_Mk") 521 | def test_login_site(): 522 | api.add_user('user00', 'User 00', 'p4ssw0rd') 523 | api.login_site('cmk', 'user00', 'p4ssw0rd') 524 | 525 | 526 | @pytest.mark.skip(reason="bug in Check_Mk") 527 | def test_logout_site(): 528 | api.add_user('user00', 'User 00', 'p4ssw0rd') 529 | api.login_site('cmk', 'user00', 'p4ssw0rd') 530 | api.logout_site('cmk') 531 | 532 | def test_bulk_discovery_start(): 533 | if 'CHECK_MK_VERSION' in os.environ and os.environ['CHECK_MK_VERSION'] == "1.5": 534 | pytest.skip("only supported since version 1.6") 535 | while api.bulk_discovery_status()['is_active'] == True: 536 | time.sleep(5) 537 | api.add_host('host00') 538 | api.bulk_discovery_start(['host00'], mode=WebApi.DiscoverMode.NEW) 539 | 540 | def test_bulk_discovery_all_hosts(): 541 | if 'CHECK_MK_VERSION' in os.environ and os.environ['CHECK_MK_VERSION'] == "1.5": 542 | pytest.skip("only supported since version 1.6") 543 | while api.bulk_discovery_status()['is_active'] == True: 544 | time.sleep(5) 545 | api.add_host('host00') 546 | api.add_host('host01') 547 | api.add_host('host02') 548 | api.add_host('host03') 549 | api.bulk_discovery_all_hosts(bulk_size=3) 550 | 551 | def test_bulk_discovery_status(): 552 | if 'CHECK_MK_VERSION' in os.environ and os.environ['CHECK_MK_VERSION'] == "1.5": 553 | pytest.skip("only supported since version 1.6") 554 | bulk_status = api.bulk_discovery_status() 555 | assert 'job' in bulk_status 556 | assert 'is_active' in bulk_status 557 | assert 'output' in bulk_status['job'] 558 | assert 'state' in bulk_status['job'] 559 | assert 'result_msg' in bulk_status['job'] 560 | -------------------------------------------------------------------------------- /check_mk_web_api/__init__.py: -------------------------------------------------------------------------------- 1 | import enum 2 | import json 3 | import os.path 4 | import re 5 | import ast 6 | 7 | from six.moves import urllib 8 | 9 | from check_mk_web_api.exception import CheckMkWebApiResponseException, CheckMkWebApiException, CheckMkWebApiAuthenticationException 10 | 11 | 12 | class NoNoneValueDict(dict): 13 | """Dictionary that does not allow items with None as value""" 14 | def __init__(self, dictionary=None): 15 | super(NoNoneValueDict, self).__init__() 16 | if dictionary: 17 | for k, v in dictionary.items(): 18 | self.__setitem__(k, v) 19 | 20 | def __setitem__(self, key, value): 21 | if value is not None: 22 | super(NoNoneValueDict, self).__setitem__(key, value) 23 | 24 | 25 | class WebApi: 26 | """ 27 | Abstraction for Check_Mk Web API 28 | 29 | # Arguments 30 | check_mk_url (str): URL to Check_Mk web application, multiple formats are supported 31 | username (str): Name of user to connect as. Make sure this is an automation user. 32 | secret (str): Secret for automation user. This is different from the password! 33 | 34 | # Examples 35 | ```python 36 | WebApi('http://checkmk.company.com/monitor/check_mk/webapi.py', 'automation', 'secret') 37 | ``` 38 | ```python 39 | WebApi('http://checkmk.company.com/monitor/check_mk', 'automation', 'secret') 40 | ``` 41 | ```python 42 | WebApi('http://checkmk.company.com/monitor', 'automation', 'secret') 43 | ``` 44 | """ 45 | 46 | __DISCOVERY_REGEX = { 47 | 'added': [re.compile(r'.*Added (\d+),.*')], 48 | 'removed': [re.compile(r'.*[Rr]emoved (\d+),.*')], 49 | 'kept': [re.compile(r'.*[Kk]ept (\d+),.*')], 50 | 'new_count': [re.compile(r'.*New Count (\d+)$'), re.compile(r'.*(\d+) new.*')] # output changed in 1.6 so we have to try multiple patterns 51 | } 52 | 53 | class DiscoverMode(enum.Enum): 54 | """ 55 | # Members 56 | NEW: Only discover new services 57 | REMOVE: Remove exceeding services 58 | FIXALL: Remove exceeding services and discover new services (Tabula Rasa) 59 | REFRESH: Start from scratch 60 | """ 61 | NEW = 'new' 62 | REMOVE = 'remove' 63 | FIXALL = 'fixall' 64 | REFRESH = 'refresh' 65 | 66 | class ActivateMode(enum.Enum): 67 | """ 68 | # Members 69 | DIRTY: Update sites with changes 70 | ALL: Update all slave sites 71 | SPECIFIC: Only update specified sites 72 | """ 73 | DIRTY = 'dirty' 74 | ALL = 'all' 75 | SPECIFIC = 'specific' 76 | 77 | def __init__(self, check_mk_url, username, secret): 78 | check_mk_url = check_mk_url.rstrip('/') 79 | 80 | if check_mk_url.endswith('.py'): # ends with /webapi.py 81 | self.web_api_base = check_mk_url 82 | elif check_mk_url.endswith('check_mk'): # ends with /$SITE_NAME/check_mk 83 | self.web_api_base = os.path.join(check_mk_url, 'webapi.py') 84 | else: # ends with /$SITE_NAME 85 | self.web_api_base = os.path.join(check_mk_url, 'check_mk', 'webapi.py') 86 | 87 | self.username = username 88 | self.secret = secret 89 | 90 | @staticmethod 91 | def __build_request_data(data, request_format): 92 | if not data: 93 | return None 94 | 95 | if request_format == 'json': 96 | request_string = 'request=' + json.dumps(data) 97 | elif request_format == 'python': 98 | request_string = 'request=' + str(data) 99 | 100 | request_string = urllib.parse.quote(request_string, safe="{[]}\"=, :") 101 | 102 | return request_string.encode() 103 | 104 | def __build_request_path(self, query_params=None): 105 | path = self.web_api_base + '?' 106 | 107 | if not query_params: 108 | query_params = {} 109 | 110 | query_params.update({ 111 | '_username': self.username, 112 | '_secret': self.secret 113 | }) 114 | 115 | query_string = urllib.parse.urlencode(query_params) 116 | 117 | path += query_string 118 | return path 119 | 120 | def make_request(self, action, query_params=None, data=None): 121 | """ 122 | Make arbitrary request to Check_Mk Web API 123 | 124 | # Arguments 125 | action (str): Action request, e.g. add_host 126 | query_params (dict): dict of path parameters 127 | data (dict): dict that will be sent as request body 128 | 129 | # Raises 130 | CheckMkWebApiResponseException: Raised when the HTTP status code != 200 131 | CheckMkWebApiException: Raised when the action's result_code != 0 132 | """ 133 | if not query_params: 134 | query_params = {} 135 | else: 136 | query_params = dict(query_params) # work on copy 137 | 138 | query_params.update({'action': action}) 139 | 140 | request_format = query_params.get('request_format', 'json') 141 | 142 | response = urllib.request.urlopen( 143 | self.__build_request_path(query_params), 144 | WebApi.__build_request_data(data, request_format) 145 | ) 146 | 147 | if response.code != 200: 148 | raise CheckMkWebApiResponseException(response) 149 | 150 | body = response.read().decode() 151 | 152 | if body.startswith('Authentication error:'): 153 | raise CheckMkWebApiAuthenticationException(body) 154 | 155 | if 'output_format' in query_params and query_params['output_format'] == 'python': 156 | body_dict = ast.literal_eval(body) 157 | else: 158 | body_dict = json.loads(body) 159 | 160 | result = body_dict['result'] 161 | if body_dict['result_code'] == 0: 162 | return result 163 | 164 | raise CheckMkWebApiException(result) 165 | 166 | def add_host(self, hostname, folder='/', ipaddress=None, 167 | alias=None, tags=None, **custom_attrs): 168 | """ 169 | Adds a nonexistent host to the Check_MK inventory 170 | 171 | # Arguments 172 | hostname (str): Name of host to add 173 | folder (str): Name of folder to add the host to 174 | ipaddress (str): IP address of host 175 | alias (str): Alias for host 176 | tags (dict): Dictionary of tags, prefix tag_ can be omitted 177 | custom_attrs (dict): dict that will get merged with generated attributes, mainly for compatibility reasons 178 | """ 179 | data = NoNoneValueDict({ 180 | 'hostname': hostname, 181 | 'folder': folder, 182 | }) 183 | 184 | attributes = NoNoneValueDict() 185 | 186 | attributes['ipaddress'] = ipaddress 187 | attributes['alias'] = alias 188 | 189 | if tags: 190 | for tag, value in tags.items(): 191 | prefix = 'tag_' 192 | if tag.startswith(prefix): 193 | attributes[tag] = value 194 | else: 195 | attributes[prefix + tag] = value 196 | 197 | attributes.update(custom_attrs) 198 | data['attributes'] = attributes 199 | 200 | return self.make_request('add_host', data=data) 201 | 202 | def edit_host(self, hostname, unset_attributes=None, **custom_attrs): 203 | """ 204 | Edits the properties of an existing host 205 | 206 | # Arguments 207 | hostname (str): Name of host to edit 208 | unset_attributes (list): List of attributes to unset 209 | custom_attrs (dict): dict that will get merged with generated attributes, mainly for compatibility reasons 210 | """ 211 | data = NoNoneValueDict( 212 | { 213 | 'hostname': hostname, 214 | 'unset_attributes': unset_attributes, 215 | 'attributes': custom_attrs 216 | } 217 | ) 218 | 219 | return self.make_request('edit_host', data=data) 220 | 221 | def delete_host(self, hostname): 222 | """ 223 | Deletes a host from the Check_MK inventory 224 | 225 | # Arguments 226 | hostname (str): Name of host to delete 227 | """ 228 | data = NoNoneValueDict({ 229 | 'hostname': hostname 230 | }) 231 | 232 | return self.make_request('delete_host', data=data) 233 | 234 | def delete_hosts(self, hostnames): 235 | """ 236 | Deletes multiple hosts from the Check_MK inventory 237 | 238 | # Arguments 239 | hostnames (list): Name of hosts to delete 240 | """ 241 | data = NoNoneValueDict({ 242 | 'hostnames': hostnames 243 | }) 244 | 245 | return self.make_request('delete_hosts', data=data) 246 | 247 | 248 | def delete_all_hosts(self): 249 | """ 250 | Deletes all hosts from the Check_MK inventory 251 | """ 252 | all_hosts = self.get_all_hosts() 253 | 254 | for hostname in all_hosts: 255 | self.delete_host(hostname) 256 | 257 | def get_host(self, hostname, effective_attributes=False): 258 | """ 259 | Gets one host 260 | 261 | # Arguments 262 | hostname (str): Name of host to get 263 | effective_attributes (bool): If True attributes with default values will be returned 264 | """ 265 | data = NoNoneValueDict({ 266 | 'hostname': hostname, 267 | }) 268 | 269 | query_params = { 270 | 'effective_attributes': 1 if effective_attributes else 0 271 | } 272 | 273 | return self.make_request('get_host', query_params=query_params, data=data) 274 | 275 | def get_all_hosts(self, effective_attributes=False): 276 | """ 277 | Gets all hosts 278 | 279 | # Arguments 280 | effective_attributes (bool): If True attributes with default values will be returned 281 | """ 282 | query_params = { 283 | 'effective_attributes': 1 if effective_attributes else 0 284 | } 285 | 286 | return self.make_request('get_all_hosts', query_params=query_params) 287 | 288 | def get_hosts_by_folder(self, folder, effective_attributes=False): 289 | """ 290 | Gets hosts in folder 291 | 292 | # Arguments 293 | folder (str): folder to get hosts for 294 | effective_attributes (bool): If True attributes with default values will be returned 295 | """ 296 | hosts = {} 297 | 298 | for host, attr in self.get_all_hosts(effective_attributes).items(): 299 | if attr['path'] == folder: 300 | hosts[host] = attr 301 | 302 | return hosts 303 | 304 | def discover_services(self, hostname, mode=DiscoverMode.NEW): 305 | """ 306 | Discovers the services of a specific host 307 | 308 | # Arguments 309 | hostname (str): Name of host to discover services for 310 | mode (DiscoverMode): see #WebApi.DiscoverMode 311 | """ 312 | data = NoNoneValueDict({ 313 | 'hostname': hostname, 314 | }) 315 | 316 | query_params = { 317 | 'mode': mode.value 318 | } 319 | 320 | result = self.make_request('discover_services', data=data, query_params=query_params) 321 | 322 | counters = {} 323 | for k, patterns in WebApi.__DISCOVERY_REGEX.items(): 324 | for pattern in patterns: 325 | try: 326 | counters[k] = pattern.match(result).group(1) 327 | except AttributeError: 328 | continue 329 | 330 | return counters 331 | 332 | def discover_services_for_all_hosts(self, mode=DiscoverMode.NEW): 333 | """ 334 | Discovers the services of all hosts 335 | 336 | # Arguments 337 | mode (DiscoverMode): see #WebApi.DiscoverMode 338 | """ 339 | for host in self.get_all_hosts(): 340 | self.discover_services(host, mode) 341 | 342 | def bulk_discovery_all_hosts(self, mode=DiscoverMode.NEW, use_cache=True, do_scan=True, bulk_size=10, 343 | ignore_single_check_errors=True): 344 | """ 345 | Bulk discovers the services of all hosts 346 | 347 | # Arguments 348 | mode (DiscoverMode): see #WebApi.DiscoverMode 349 | use_cache (bool): use cache for discovery 350 | do_scan (bool): do scan 351 | bulk_size (int): number of hosts to handle at once 352 | ignore_single_check_errors (bool): Ignore errors in single check plugins 353 | """ 354 | hostnames = [] 355 | for host in self.get_all_hosts(): 356 | hostnames.append(host) 357 | self.bulk_discovery_start(hostnames, mode, use_cache, do_scan, bulk_size, ignore_single_check_errors) 358 | 359 | def bulk_discovery_start(self, hostnames, mode=DiscoverMode.NEW, use_cache=True, do_scan=True, bulk_size=10, 360 | ignore_single_check_errors=True): 361 | """ 362 | Start bulk discovery for multiple hosts 363 | 364 | # Arguments 365 | mode (DiscoverMode): see #WebApi.DiscoverMode 366 | use_cache (bool): use cache for discovery 367 | do_scan (bool): do scan 368 | bulk_size (int): number of hosts to handle at once 369 | ignore_single_check_errors (bool): Ignore errors in single check plugins 370 | """ 371 | data = NoNoneValueDict({ 372 | 'hostnames': hostnames, 373 | 'mode': mode.value, 374 | "use_cache": use_cache, 375 | "do_scan": do_scan, 376 | "bulk_size": bulk_size, 377 | "ignore_single_check_errors": ignore_single_check_errors 378 | }) 379 | self.make_request('bulk_discovery_start', data=data, query_params=None) 380 | 381 | def bulk_discovery_status(self): 382 | """ 383 | Get status of bulk discovery 384 | """ 385 | return self.make_request('bulk_discovery_status', data=None, query_params=None) 386 | 387 | def get_user(self, user_id): 388 | """ 389 | Gets a single user 390 | 391 | # Arguments 392 | user_id (str): ID of user to get 393 | """ 394 | return self.get_all_users()[user_id] 395 | 396 | def get_all_users(self): 397 | """ 398 | Gets all users and their attributes 399 | """ 400 | return self.make_request('get_all_users') 401 | 402 | def add_user(self, user_id, username, password, **custom_attrs): 403 | """ 404 | Adds a new user 405 | 406 | # Arguments 407 | user_id (str): user ID that will be used to log in 408 | username (str): name of the user to create 409 | password (str): password that will be used to log in 410 | custom_attrs (dict): attributes that can be set for a user, look at output from #WebApi.get_all_users 411 | """ 412 | data = NoNoneValueDict({ 413 | 'users': { 414 | user_id: { 415 | 'alias': username, 416 | 'password': password 417 | } 418 | } 419 | }) 420 | 421 | data['users'][user_id].update(custom_attrs) 422 | 423 | return self.make_request('add_users', data=data) 424 | 425 | def add_automation_user(self,user_id, username, secret, **custom_attrs): 426 | """ 427 | Adds a new automation user 428 | 429 | # Arguments 430 | user_id (str): user ID that will be used to log in 431 | username (str): name of the user to create 432 | secret (str): secret that will be used to log in 433 | custom_attrs (dict): attributes that can be set for a user, look at output from #WebApi.get_all_users 434 | """ 435 | data = NoNoneValueDict({ 436 | 'users': { 437 | user_id: { 438 | 'alias': username, 439 | 'automation_secret': secret 440 | } 441 | } 442 | }) 443 | 444 | data['users'][user_id].update(custom_attrs) 445 | 446 | return self.make_request('add_users', data=data) 447 | 448 | def edit_user(self, user_id, attributes, unset_attributes=None): 449 | """ 450 | Edits an existing user 451 | 452 | # Arguments 453 | user_id (str): ID of user to edit 454 | attributes (dict): attributes to set for given host 455 | unset_attributes (list): list of attribute keys to unset 456 | """ 457 | data = NoNoneValueDict({ 458 | 'users': { 459 | user_id: { 460 | 'set_attributes': attributes, 461 | 'unset_attributes': unset_attributes if unset_attributes else [] 462 | } 463 | } 464 | }) 465 | 466 | return self.make_request('edit_users', data=data) 467 | 468 | def delete_user(self, user_id): 469 | """ 470 | Deletes a user 471 | 472 | # Arguments 473 | user_id (str): ID of user to delete 474 | """ 475 | data = NoNoneValueDict({ 476 | 'users': [user_id] 477 | }) 478 | 479 | return self.make_request('delete_users', data=data) 480 | 481 | def get_folder(self, folder, effective_attributes=False): 482 | """ 483 | Gets one folder 484 | 485 | # Arguments 486 | folder (str): name of folder to get 487 | effective_attributes (bool): If True attributes with default values will be returned 488 | """ 489 | data = NoNoneValueDict({ 490 | 'folder': folder 491 | }) 492 | 493 | query_params = { 494 | 'effective_attributes': 1 if effective_attributes else 0 495 | } 496 | 497 | return self.make_request('get_folder', data=data, query_params=query_params) 498 | 499 | def get_all_folders(self): 500 | """ 501 | Gets all folders 502 | """ 503 | return self.make_request('get_all_folders') 504 | 505 | def add_folder(self, folder, **attributes): 506 | """ 507 | Adds a new folder 508 | 509 | # Arguments 510 | folder (str): name of folder to add 511 | attributes (dict): attributes to set for the folder, look at output from #WebApi.get_folder 512 | """ 513 | data = NoNoneValueDict({ 514 | 'folder': folder, 515 | 'attributes': attributes if attributes else {} 516 | }) 517 | 518 | return self.make_request('add_folder', data=data) 519 | 520 | def edit_folder(self, folder, **attributes): 521 | """ 522 | Edits an existing folder 523 | 524 | # Arguments 525 | folder (str): name of folder to edit 526 | attributes (dict): attributes to set for the folder, look at output from #WebApi.get_folder 527 | """ 528 | data = NoNoneValueDict({ 529 | 'folder': folder, 530 | 'attributes': attributes if attributes else {} 531 | }) 532 | 533 | return self.make_request('edit_folder', data=data) 534 | 535 | def delete_folder(self, folder): 536 | """ 537 | Deletes an existing folder 538 | 539 | # Arguments 540 | folder (str): name of folder to delete 541 | """ 542 | data = NoNoneValueDict({ 543 | 'folder': folder 544 | }) 545 | 546 | return self.make_request('delete_folder', data=data) 547 | 548 | def get_contactgroup(self, group): 549 | """ 550 | Gets one contact group 551 | 552 | # Arguments 553 | group (str): name of contact group to get 554 | """ 555 | return self.get_all_contactgroups()[group] 556 | 557 | def get_all_contactgroups(self): 558 | """ 559 | Gets all contact groups 560 | """ 561 | return self.make_request('get_all_contactgroups') 562 | 563 | def add_contactgroup(self, group, alias): 564 | """ 565 | Adds a contact group 566 | 567 | # Arguments 568 | group (str): name of group to add 569 | alias (str): alias for group 570 | """ 571 | data = NoNoneValueDict({ 572 | 'groupname': group, 573 | 'alias': alias, 574 | }) 575 | 576 | return self.make_request('add_contactgroup', data=data) 577 | 578 | def edit_contactgroup(self, group, alias): 579 | """ 580 | Edits a contact group 581 | 582 | # Arguments 583 | group (str): name of group to edit 584 | alias (str): new alias for group 585 | """ 586 | data = NoNoneValueDict({ 587 | 'groupname': group, 588 | 'alias': alias, 589 | }) 590 | 591 | return self.make_request('edit_contactgroup', data=data) 592 | 593 | def delete_contactgroup(self, group): 594 | """ 595 | Deletes a contact group 596 | 597 | # Arguments 598 | group (str): name of group to delete 599 | """ 600 | data = NoNoneValueDict({ 601 | 'groupname': group, 602 | }) 603 | 604 | return self.make_request('delete_contactgroup', data=data) 605 | 606 | def delete_all_contactgroups(self): 607 | """ 608 | Deletes all contact groups 609 | """ 610 | for group in self.get_all_contactgroups(): 611 | self.delete_contactgroup(group) 612 | 613 | def get_hostgroup(self, group): 614 | """ 615 | Gets one host group 616 | 617 | # Arguments 618 | group (str): name of host group to get 619 | """ 620 | return self.get_all_hostgroups()[group] 621 | 622 | def get_all_hostgroups(self): 623 | """ 624 | Gets all host groups 625 | """ 626 | return self.make_request('get_all_hostgroups') 627 | 628 | def add_hostgroup(self, group, alias): 629 | """ 630 | Adds a host group 631 | 632 | # Arguments 633 | group (str): name of group to add 634 | alias (str): alias for group 635 | """ 636 | data = NoNoneValueDict({ 637 | 'groupname': group, 638 | 'alias': alias, 639 | }) 640 | 641 | return self.make_request('add_hostgroup', data=data) 642 | 643 | def edit_hostgroup(self, group, alias): 644 | """ 645 | Edits a host group 646 | 647 | # Arguments 648 | group (str): name of group to edit 649 | alias (str): new alias for group 650 | """ 651 | data = NoNoneValueDict({ 652 | 'groupname': group, 653 | 'alias': alias, 654 | }) 655 | 656 | return self.make_request('edit_hostgroup', data=data) 657 | 658 | def delete_hostgroup(self, group): 659 | """ 660 | Deletes a host group 661 | 662 | # Arguments 663 | group (str): name of group to delete 664 | """ 665 | data = NoNoneValueDict({ 666 | 'groupname': group, 667 | }) 668 | 669 | return self.make_request('delete_hostgroup', data=data) 670 | 671 | def delete_all_hostgroups(self): 672 | """ 673 | Deletes all host groups 674 | """ 675 | for group in self.get_all_hostgroups(): 676 | self.delete_hostgroup(group) 677 | 678 | def get_servicegroup(self, group): 679 | return self.get_all_servicegroups()[group] 680 | 681 | def get_all_servicegroups(self): 682 | """ 683 | Gets all service groups 684 | """ 685 | return self.make_request('get_all_servicegroups') 686 | 687 | def add_servicegroup(self, group, alias): 688 | """ 689 | Adds a service group 690 | 691 | # Arguments 692 | group (str): name of group to add 693 | alias (str): alias for group 694 | """ 695 | data = NoNoneValueDict({ 696 | 'groupname': group, 697 | 'alias': alias, 698 | }) 699 | 700 | return self.make_request('add_servicegroup', data=data) 701 | 702 | def edit_servicegroup(self, group, alias): 703 | """ 704 | Edits a service group 705 | 706 | # Arguments 707 | group (str): name of group to edit 708 | alias (str): new alias for group 709 | """ 710 | data = NoNoneValueDict({ 711 | 'groupname': group, 712 | 'alias': alias, 713 | }) 714 | 715 | return self.make_request('edit_servicegroup', data=data) 716 | 717 | def delete_servicegroup(self, group): 718 | """ 719 | Deletes a service group 720 | 721 | # Arguments 722 | group (str): name of group to delete 723 | """ 724 | data = NoNoneValueDict({ 725 | 'groupname': group, 726 | }) 727 | 728 | return self.make_request('delete_servicegroup', data=data) 729 | 730 | def delete_all_servicegroups(self): 731 | """ 732 | Deletes all service groups 733 | """ 734 | for group in self.get_all_servicegroups(): 735 | self.delete_servicegroup(group) 736 | 737 | def get_ruleset(self, ruleset): 738 | """ 739 | Gets one rule set 740 | 741 | # Arguments 742 | ruleset (str): name of rule set to get 743 | """ 744 | data = NoNoneValueDict({ 745 | 'ruleset_name': ruleset, 746 | }) 747 | 748 | return self.make_request('get_ruleset', data=data, query_params={'output_format': 'python'}) 749 | 750 | def get_rulesets(self): 751 | """ 752 | Gets all rule sets 753 | """ 754 | return self.make_request('get_rulesets_info', query_params={'output_format': 'python'}) 755 | 756 | def set_ruleset(self, ruleset, ruleset_config): 757 | """ 758 | Edits one rule set 759 | 760 | # Arguments 761 | ruleset (str): name of rule set to edit 762 | ruleset_config (dict): config that will be set, have a look at return value of #WebApi.get_ruleset 763 | """ 764 | data = NoNoneValueDict({ 765 | 'ruleset_name': ruleset, 766 | 'ruleset': ruleset_config if ruleset_config else {} 767 | }) 768 | 769 | return self.make_request('set_ruleset', data=data, query_params={'request_format': 'python'}) 770 | 771 | def get_hosttags(self): 772 | """ 773 | Gets all host tags 774 | """ 775 | return self.make_request('get_hosttags') 776 | 777 | def set_hosttags(self, hosttags): 778 | """ 779 | Sets host tags 780 | 781 | As implemented by Check_MK, it is only possible to write the whole Host Tag Settings within an API-Call 782 | You can use the #WebApi.get_hosttags to get the current Tags, modify them and write the dict back via set_hosttags 783 | To ensure that no Tags are modified in the meantime you can use the configuration_hash key. 784 | 785 | e.g. 'configuration_hash': u'f31ea758a59473d15f378b692110996c' 786 | 787 | # Arguments 788 | hosttags (dict) with 2 mandatory keys: { 'aux_tags' : [], 'tag_groups' : [] } 789 | """ 790 | data = NoNoneValueDict(hosttags) 791 | 792 | return self.make_request('set_hosttags', data=data) 793 | 794 | def add_aux_tag(self, identifier, title, topic=None): 795 | """ 796 | Adds an auxiliary host tag 797 | 798 | # Arguments 799 | identifier (str): unique identifier of the tag to add 800 | title (str): title of the tag to add 801 | topic (str): topic of the tag to add 802 | """ 803 | hosttags = self.get_hosttags() 804 | 805 | hosttags.update( 806 | aux_tags=hosttags.get('aux_tags', []) + [{ 807 | 'id': identifier, 808 | 'title': title, 809 | 'topic': topic 810 | }] 811 | ) 812 | 813 | self.set_hosttags(hosttags) 814 | 815 | def add_tag_group(self, identifier, title, tags, topic=None): 816 | """ 817 | Adds a tag group 818 | 819 | # Arguments 820 | identifier (str): unique identifier of the tag to add 821 | title (str): title of the tag to add 822 | tags (list): tag choices to add, e.g. [{"id": "high", "title": "High Availability", "aux_tags": []}, ...] 823 | topic (str): topic of the tag to add 824 | """ 825 | hosttags = self.get_hosttags() 826 | 827 | hosttags.update( 828 | tag_groups=hosttags.get('tag_groups', []) + [{ 829 | 'id': identifier, 830 | 'title': title, 831 | 'topic': topic, 832 | 'tags': tags or [] 833 | }] 834 | ) 835 | 836 | self.set_hosttags(hosttags) 837 | 838 | def get_site(self, site_id): 839 | """ 840 | Gets a site 841 | 842 | # Arguments 843 | site_id (str): ID of site to get 844 | """ 845 | data = NoNoneValueDict({ 846 | 'site_id': site_id 847 | }) 848 | 849 | return self.make_request('get_site', data=data, query_params={'output_format': 'python'}) 850 | 851 | def set_site(self, site_id, site_config): 852 | """ 853 | Edits the connection to a site 854 | 855 | # Arguments 856 | site_id (str): ID of site to edit 857 | site_config: config that will be set, have a look at return value of #WebApi.get_site 858 | """ 859 | data = NoNoneValueDict({ 860 | 'site_id': site_id, 861 | 'site_config': site_config if site_config else {} 862 | }) 863 | 864 | return self.make_request('set_site', data=data, query_params={'request_format': 'python'}) 865 | 866 | def delete_site(self, site_id): 867 | """ 868 | Deletes a connection to a site 869 | 870 | # Arguments 871 | site_id (str): ID of site to delete the connection to 872 | """ 873 | data = NoNoneValueDict({ 874 | 'site_id': site_id 875 | }) 876 | 877 | return self.make_request('delete_site', data=data) 878 | 879 | def login_site(self, site_id, user, password): 880 | """ 881 | Logs in to site 882 | 883 | # Arguments 884 | site_id (str): ID of site to log in to 885 | """ 886 | data = NoNoneValueDict({ 887 | 'site_id': site_id, 888 | 'username': user, 889 | 'password': password 890 | }) 891 | 892 | return self.make_request('login_site', data=data) 893 | 894 | def logout_site(self, site_id): 895 | """ 896 | Logs out of site 897 | 898 | # Arguments 899 | site_id (str): ID of site to log out of 900 | """ 901 | data = NoNoneValueDict({ 902 | 'site_id': site_id 903 | }) 904 | 905 | return self.make_request('logout_site', data=data) 906 | 907 | def bake_agents(self): 908 | """ 909 | Bakes all agents 910 | 911 | Enterprise Edition only! 912 | """ 913 | return self.make_request('bake_agents') 914 | 915 | def activate_changes(self, mode=ActivateMode.DIRTY, 916 | sites=None, allow_foreign_changes=False): 917 | """ 918 | Activates all changes previously done 919 | 920 | # Arguments 921 | mode (ActivateMode): see #WebApi.ActivateMode 922 | sites (list): List of sites to activates changes on 923 | allow_foreign_changes (bool): If True changes of other users will be applied as well 924 | """ 925 | data = NoNoneValueDict({ 926 | 'sites': sites 927 | }) 928 | 929 | query_params = { 930 | 'mode': mode.value, 931 | 'allow_foreign_changes': 1 if allow_foreign_changes else 0 932 | } 933 | 934 | return self.make_request('activate_changes', query_params=query_params, data=data) 935 | --------------------------------------------------------------------------------