├── .gitignore ├── AUTHORS ├── COPYING ├── MANIFEST.in ├── README.md ├── hyclops.conf ├── hyclops ├── __init__.py ├── connector │ ├── __init__.py │ ├── base.py │ ├── ec2.py │ ├── ipmi.py │ └── vsphere.py ├── libcloud_driver │ ├── __init__.py │ └── vsphere.py └── queue.py ├── misc ├── cron_scripts │ └── delete_not_exist_hosts.py ├── externalscripts │ ├── get_aws_charges.py │ └── push_message.py ├── globalscripts │ └── request_action.py ├── import_data │ ├── globalmacros.xml │ ├── globalscripts.xml │ └── templates.xml ├── init.d │ ├── redhat │ │ └── hyclops │ └── ubuntu │ │ └── hyclops.conf └── zabbix-custom │ ├── 2.0 │ ├── custom │ │ ├── additional_blocks.inc.php │ │ ├── additional_blocks_func.inc.php │ │ ├── additional_func.inc.php │ │ └── monitoring.dashboard.js.php │ ├── dashboard.php │ ├── dashboard_scripts_exec.php │ ├── gateone.php │ └── include │ │ └── menu.inc.php │ └── 2.2 │ ├── custom │ ├── additional_blocks.inc.php │ ├── additional_blocks_func.inc.php │ ├── additional_func.inc.php │ └── monitoring.dashboard.js.php │ ├── dashboard.php │ ├── dashboard_scripts_exec.php │ ├── gateone.php │ └── include │ └── menu.inc.php ├── replace_frontend.sh ├── run.py ├── setup.py └── test ├── __init__.py ├── mock_libcloud.py ├── mock_psphere.py ├── mock_zabbix.py ├── test.conf ├── test_base.py ├── test_ec2.py ├── test_ipmi.py ├── test_libcloud_vsphere.py ├── test_queue.py └── test_vsphere.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.sw[po] 3 | *.egg-info 4 | .coverage 5 | dist 6 | build 7 | MANIFEST 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Development Team 2 | ================ 3 | 4 | TIS Inc. 5 | HyClops for Zabbix team 6 | 7 | Core developers 8 | =============== 9 | 10 | * Daisuke Ikeda 11 | * Kazuya Takahashi 12 | 13 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include run.py 3 | include hyclops.conf 4 | include misc/init.d/*/* 5 | include misc/import_data/* 6 | include misc/externalscripts/* 7 | include misc/globalscripts/* 8 | include misc/cron_scripts/* 9 | include misc/zabbix-custom/* 10 | include misc/zabbix-custom/*/* 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About {#about} 2 | 3 | HyClops is Zabbix extention that provide some functions to management Amazon EC2 and VMware vSphere on Zabbix. 4 | It extends Zabbix capability by using externalscripts, zabbix sender and message queue. 5 | 6 | **Our concept:** 7 | - To realize integrated management for Hybrid Cloud environments. 8 | - To realize automation of many operations. 9 | - To realize the autonomous system. 10 | 11 | HyClops has some features of integration & automation. 12 | These features are the first step for our concept. 13 | 14 | HyClops provides following features: 15 | 16 | - Automatically register EC2 instances and vSphere virtual machines to zabbix. 17 | - Monitor basic instance informations without zabbix agent. (using EC2 API or vSphere API) 18 | - Start/stop instances from Zabbix global script. 19 | - Display EC2 and vSphere informations on Zabbix dashboard by replacing dashboard to customized one. 20 | - (Experiments) connect SSH console of each instances from customized dashboard by using [Gate One](https://github.com/liftoff/GateOne). 21 | 22 | # Release Notes {#releases} 23 | 24 | - 2014/03/17 ver.0.2.1 25 | - Bug fix (minor release) 26 | - 2013/12/12 ver.0.2.0 27 | - Support Zabbix2.2. 28 | - Change the logic of automatic allocation Templates for unknown hosts. 29 | - Add a interface column to vSphere information panel in Zabbix dashboard. 30 | - 2013/07/12 ver.0.1.0 31 | - Support Amazon EC2 & VMware vSphere ESXi management. 32 | - Register instances info automatically. 33 | - Execute start/stop/reboot command to multi instances. 34 | - Connect to ssh console of each instances at Web browser. 35 | 36 | # Architecture {#architecture} 37 | 38 | ![architecture](http://tech-sketch.github.io/hyclops/assets/images/HyClops_architecture.png) 39 | 40 | HyClops is constructed by three components. 41 | 42 | ## Zabbix Server component 43 | 44 | - Modifying original Zabbix dashboard. 45 | - Monitoring hybrid cloud environments constantly by using Zabbix external scripts feature. 46 | - Controlling instance at each environments by using Zabbix global scripts feature. 47 | 48 | ## Message queueing process component 49 | 50 | - Running message queueing process ( ZeroMQ ) (default 5555/TCP). 51 | - Pulling message from queue regularly, and access to API of each environments (vSphere API or AWS API) according to contents of message. 52 | - Sending data to Zabbix by using zabbix sender. 53 | 54 | ## GateOne Server component 55 | 56 | - Managing some SSH connections. 57 | - Managing some authentication keys. 58 | 59 | # Install {#install} 60 | 61 | ## Requirements 62 | 63 | The following requirements exist for running this product: 64 | 65 | - Zabbix (>= 2.0) with zabbix sender 66 | - Python (>= 2.6) (developed on python 2.7) 67 | - python-setuptools 68 | - ZeroMQ 69 | - ipmitool 70 | - GateOne (if you want to use ssh console from browser) 71 | 72 | ## Install Zabbix Server & Zabbix Agent 73 | 74 | See the Zabbix official manual. 75 | 76 | Zabbix Agent is used for HyClops & GateOne process monitoring. 77 | 78 | ## Install related package 79 | 80 | In case of RHEL/CentOS 81 | 82 | Add ZeroMQ package repository (This is the process for CentOS6.) 83 | $ sudo curl http://download.opensuse.org/repositories/home:/fengshuo:/zeromq/CentOS_CentOS-6/home:fengshuo:zeromq.repo > /etc/yum.repos.d/zeromq.repo 84 | Install related package 85 | $ sudo yum install gcc gcc-c++ zeromq zeromq-devel python-devel python-setuptools ipmitool 86 | 87 | In case of Ubuntu 88 | 89 | $ sudo apt-get install gcc g++ libzmq1 ipmitool python-dev python-setuptools 90 | 91 | ## Install python module 92 | 93 | $ sudo pip install apache-libcloud zabbix-api pyzmq psphere python-daemon==1.6 configobj 94 | If python version < 2.7 95 | $ sudo pip install ordereddict argparse 96 | If you want to use the script collecting AWS billing data 97 | $ sudo pip install boto 98 | 99 | ## Install GateOne (Optional) 100 | 101 | See the GateOne official manual. 102 | 103 | Setting GateOne (configure API authentication) 104 | 105 | $ sudo /opt/gateone/gateone.py --auth=api --origins="http://zabbix-server-frontend/" 106 | $ sudo /opt/gateone/gateone.py --new_api_key 107 | $ sudo service gateone start 108 | 109 | In case of RHEL/CentOS 110 | 111 | $ sudo chkconfig gateone on 112 | 113 | In case of Ubuntu 114 | 115 | $ sudo update-rc.d gateone defaults 116 | 117 | ## Install HyClops 118 | 119 | $ wget https://github.com/tech-sketch/hyclops/archive/[version no.].tar.gz 120 | $ tar zxvf hyclops-[version no.].tar.gz 121 | $ cd hyclops-[version no.] 122 | $ sudo python setup.py install 123 | 124 | ### Copy files 125 | 126 | $ sudo cp -a ./misc/init.d/redhat/hyclops /etc/init.d/ # in case of SysV init 127 | $ sudo cp -a ./misc/init.d/ubuntu/hyclops.conf /etc/init/ # in case of Upstart 128 | $ sudo cp -a ./externalscripts/* [externalscripts dir] (/etc/zabbix/externalscripts/ etc...) 129 | 130 | ### Setting permissions 131 | 132 | $ sudo chown zabbix:zabbix [externalscripts dir](/etc/zabbix/externalscripts/ etc...)/* 133 | $ sudo chmod u+x [externalscripts dir](/etc/zabbix/externalscripts/ etc...)/* 134 | 135 | ### Setting HyClops 136 | 137 | $ sudo vim /opt/hyclops/hyclops.conf 138 | [hyclops] 139 | # listen_address = * # listen on all network interface 140 | # listen_address = 127.0.0.1 # listen on specific network interface 141 | listen_address = 127.0.0.1 # hyclops listhen IP address (default 127.0.0.1) 142 | listen_port = 5555 # hyclops listen port (default 5555) 143 | [zabbix] 144 | zabbix_server = 127.0.0.1 # zabbix server IP address/hostname 145 | zabbix_port = 10051 # zabbix server listen port 146 | frontend_url = http://%(zabbix_server)s/zabbix # zabbix server frontend url 147 | zabbix_user = Admin # zabbix user (for using host.create API) 148 | zabbix_password = zabbix # zabbix user password 149 | zabbix_sender = /usr/bin/zabbix_sender # zabbix sender binary path 150 | 151 | [ipmi] 152 | ipmitool = /usr/bin/ipmitool # ipmitool command path 153 | 154 | [logging] 155 | log_level = WARNING # hyclops log level 156 | log_file = /tmp/hyclops.log # hyclops log output file path 157 | 158 | [environments] 159 | # http_proxy = http://proxy.example.com:8080/ # setting http_proxy 160 | # https_proxy = http://proxy.example.com:8080/ # setting https_proxy 161 | # no_proxy = "localhost,127.0.0.1" # setting proxy exclude host 162 | 163 | ### Setting SELinux(In case of Enforce SELinux) 164 | 165 | HyClops process outputs logs to /opt/hyclops/logs/hyclops_server.log (default setting). 166 | There are 2 process writing to this log. 167 | 168 | 1. HyClops daemon process 169 | 2. Apache daemon process (when zabbix globalscripts are executed) 170 | 171 | So, Both of HyClops process user and Apache process user must be able to write to this log. 172 | 173 | $ sudo chcon -R -t public_content_rw_t /opt/hyclops/logs/ 174 | $ sudo chcon -R -t public_content_rw_t /tmp/suds/ # for tmp file when executing vSphere operation 175 | $ sudo setsebool -P allow_httpd_anon_write 1 176 | 177 | ### Server init setting 178 | 179 | In case of SysV init 180 | 181 | $ sudo chkconfig hyclops on 182 | $ sudo service hyclops start 183 | 184 | In case of Upstart 185 | 186 | $ sudo initctl start hyclops 187 | 188 | ## Replace some Zabbix dashboard files 189 | 190 | $ sudo python setup.py replace -d (zabbix frontend document root path) --zabbix-version=(2.0 or 2.2) 191 | 192 | ## Import Zabbix templates,scripts and globalmacro data 193 | 194 | Import three data. 195 | 196 | - misc/import_data/templates.xml 197 | - misc/import_data/globalscripts.xml 198 | - misc/import_data/globalmacros.xml 199 | 200 | Import process is automated by script. 201 | 202 | $ sudo python setup.py import -f -u -p 203 | (e.g. python setup.py import -f http://localhost/zabbix -u Admin -p zabbix) 204 | 205 | ## Setting cron script 206 | 207 | Hyclops has a feature to move hosts which not exist to "Not exist hosts" Zabbix hostgroup. 208 | If you want to remove these not exist hosts automatically, you should set cron script. 209 | 210 | $ sudo crontab -u hyclops -e 211 | */5 * * * * python /opt/hyclops/cron_scripts/delete_not_exist_hosts.py 212 | 213 | This script is set to remove after 30 days in default. 214 | If you want to change period, please set "-d" option (e.g. "-d 10" is setting to remove after 10days). 215 | 216 | # Configure {#configure} 217 | 218 | Configure in Zabbix frontend. 219 | 220 | ## Create value mappings 221 | 222 | Open "Administration > General > Value mapping" 223 | 224 | **Script return code** 225 | 226 | |raw data|mapping string| 227 | |---------|----------------| 228 | |0|success| 229 | |1|failure| 230 | 231 | **Libcloud Node State** 232 | 233 | |raw data|mapping string| 234 | |---------|----------------| 235 | |0|running| 236 | |1|rebooting| 237 | |2|terminated| 238 | |3|pending| 239 | |4|stopped| 240 | 241 | ## Setting global macros 242 | 243 | |macro|value| 244 | |:----|:---| 245 | |{$GATEONE_URL}|GateOne Server URL (e.g. https://gateone-server)| 246 | |{$GATEONE_KEY}|GateOne API key| 247 | |{$GATEONE_SECRET}|GateOne Secret key| 248 | |{$HYCLOPS_SERVER}|HyClops listen IPaddress/hostname (e.g. 127.0.0.1)| 249 | |{$HYCLOPS_PORT}|HyClops listen port (e.g. 5555)| 250 | 251 | (API key and secret key are described in /opt/gateone/server.conf) 252 | 253 | ## Create zabbix hosts 254 | 255 | **For AWS EC2** 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 |
setting itemssetting values
TemplatesAWSAccount
Macros(Required) {$KEY}AWS Access key
(Required) {$SECRET}AWS Secret key
(Optional) {$VM_TEMPLATES}Template names for All instances (separated by ",")
(Optional) {$VM_TEMPLATES_WINDOWS}Template names for Windows instances (separated by ",")
(Optional) {$VM_TEMPLATES_LINUX}Template names for Not Windows instances (separated by ",")
288 | 289 | **For vSphere ESXi** 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 |
setting itemssetting values
TemplatesvSphereESXi
SNMP interfacesvSphere ESXi management interface
Macros(Required) {$KEY}ESXi username
(Required) {$SECRET}ESXi password
(Optional) {$VM_TEMPLATES}Template names for All VMs(separated by ",")
(Optional) {$VM_TEMPLATES_WINDOWS}Template names for Windows VMs(separated by ",")
(Optional) {$VM_TEMPLATES_LINUX}Template names for Not Windows VMs(separated by ",")
326 | 327 | **For Physical Machine** 328 | 329 | |setting items|setting values| 330 | |---|---| 331 | |Templates|IPMI| 332 | |IPMI interfaces|IPMI interface| 333 | |IPMI settings|same of IPMI monitoring settings| 334 | 335 | **For HyClops Server & GateOne Server** 336 | 337 | If HyClops Server and GateOne Server is running on different from Zabbix Server, you should register each server hosts. 338 | Please assign "HyClops Server","GateOne Server" templates to these hosts. 339 | These templates include process monitoring item & log monitoring item. 340 | 341 | # Usage {#usage} 342 | 343 | ## Monitoring each environments 344 | 345 | Visit Zabbix dashboard. 346 | Added some areas in dashboard (vSphere status, Amazon Web Services status,Pysical Machine status). 347 | 348 | ![dashboard](http://tech-sketch.github.io/hyclops/assets/images/HyClops_dashboard.png) 349 | 350 | This area shows some information for each environments. 351 | For example, in case of AWS, instance number are displayed for each state(Powered on/Powered off). 352 | When you mouseover and click the number, you can see the list of instances. 353 | And more, when you mouseover each instance name, you can see the more detail information. 354 | 355 | ## Controlling instances (Start/Stop/Reboot) 356 | 357 | If you want to control instances, you can execute in Zabbix dashboard. 358 | 359 | 1. Check box on control target instance(you can select multi instances.) 360 | 2. Select control script 361 | 3. Execute 362 | 363 | ## Connecting ssh console 364 | 365 | At Zabbix dashboard, you can select vSphere VM or Amazon EC2 instance(Powered on status). 366 | 367 | ![ssh_connect](http://tech-sketch.github.io/hyclops/assets/images/ssh_connect.png) 368 | 369 | ![gateone](http://tech-sketch.github.io/hyclops/assets/images/gateone.png) 370 | 371 | # License {#license} 372 | 373 | HyClops for Zabbix is released under the GNU General Public License version2. 374 | The GPL official full text is published at this [link](http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt). 375 | 376 | Copyright 2013 TIS Inc. 377 | -------------------------------------------------------------------------------- /hyclops.conf: -------------------------------------------------------------------------------- 1 | # ============================ 2 | # User settings 3 | # ============================ 4 | 5 | [hyclops] 6 | # listen_address = * # listen on all network interface 7 | # listen_address = 127.0.0.1 # listen on specific network interface 8 | listen_address = 127.0.0.1 9 | listen_port = 5555 10 | 11 | [zabbix] 12 | zabbix_server = 127.0.0.1 13 | zabbix_port = 10051 14 | frontend_url = http://%(zabbix_server)s/zabbix 15 | zabbix_user = Admin 16 | zabbix_password = zabbix 17 | zabbix_sender = /usr/bin/zabbix_sender 18 | 19 | [ipmi] 20 | ipmitool = /usr/bin/ipmitool 21 | 22 | [logging] 23 | log_level = WARNING 24 | log_file = /opt/hyclops/logs/hyclops_server.log 25 | 26 | [environments] 27 | # http_proxy = http://proxy.example.com:8080/ 28 | # https_proxy = http://proxy.example.com:8080/ 29 | # no_proxy = "localhost,127.0.0.1" 30 | -------------------------------------------------------------------------------- /hyclops/__init__.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | -------------------------------------------------------------------------------- /hyclops/connector/__init__.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | -------------------------------------------------------------------------------- /hyclops/connector/base.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | import re 20 | import json 21 | import logging 22 | from zabbix_api import ZabbixAPI, ZabbixAPISubClass, ZabbixAPIException 23 | 24 | 25 | class BaseConnector(object): 26 | 27 | LIBCLOUD_BASE_METHODS = ["reboot", "destroy"] 28 | MACRO_KEY = "{$KEY}" 29 | MACRO_SECRET = "{$SECRET}" 30 | MACRO_GROUPS = "{$VM_GROUPS}" 31 | MACRO_REQUIRED_TEMPLATES = "{$REQUIRED_TEMPLATES}" 32 | MACRO_TEMPLATES = "{$VM_TEMPLATES}" 33 | MACRO_TEMPLATES_LINUX = "{$VM_TEMPLATES_LINUX}" 34 | MACRO_TEMPLATES_WINDOWS = "{$VM_TEMPLATES_WINDOWS}" 35 | NOT_EXIST_HOST_NAME_PREFIX = "_DELETED_" 36 | VISIBLE_NAME_MAX_LENGTH = 64 37 | 38 | def __init__(self, config): 39 | self.config = config 40 | self.logger = logging.getLogger("hyclops.connector.base") 41 | self.zabbix_server = config["zabbix"].get('zabbix_server', 'localhost') 42 | self.zabbix_port = config["zabbix"].get('zabbix_port', '10051') 43 | self.zabbix_sender_path = config["zabbix"].get('zabbix_sender', '/usr/bin/zabbix_sender') 44 | frontend_url = config["zabbix"].get('frontend_url', "http://%s/zabbix" % self.zabbix_server) 45 | zabbix_user = config["zabbix"].get('zabbix_user', '') 46 | zabbix_password = config["zabbix"].get('zabbix_password', '') 47 | self.zabbix_api = ZabbixAPI(frontend_url) 48 | self.zabbix_api.hostinterface = ZabbixAPISubClass(self.zabbix_api, dict({"prefix": "hostinterface"})) 49 | try: 50 | self.zabbix_api.login(zabbix_user, zabbix_password) 51 | except: 52 | self.logger.error("Cannot login zabbix server") 53 | raise 54 | 55 | def __call__(self, hostname, params): 56 | raise NotImplementedError("this connector is not implemented") 57 | 58 | # Zabbix Utils 59 | 60 | def zabbix_sender(self, zabbix_hostname, key, value): 61 | if value is None: 62 | value = "" 63 | elif not isinstance(value, str): 64 | value = json.dumps(value) 65 | args = (self.zabbix_sender_path, self.zabbix_server, self.zabbix_port, zabbix_hostname, key, value) 66 | cmd = "%s -z %s -p %s -s %s -k %s -o %s > /dev/null 2>&1" % self.escape_shell_args(args) 67 | return_code = os.system(cmd) 68 | if return_code != 0: 69 | raise Exception("Failed to run zabbix_sender") 70 | 71 | # TODO: use subprocess 72 | def escape_shell_args(self, args): 73 | if not isinstance(args, tuple) and not isinstance(args, list): 74 | args = [args] 75 | return tuple(["\\'".join("'" + p + "'" for p in str(arg).split("'")) for arg in args]) 76 | 77 | def addresses_to_interfaces(self, addresses, interface_types=[1, 2], main=True): 78 | ports = {1: 10050, 2: 161, 3: 623, 4: 12345} # 1: Zabbix Agent, 2: SNMP, 3: IPMI, 4: JMX 79 | interfaces = [] 80 | if not isinstance(addresses, list): 81 | addresses = [addresses] 82 | for addr in addresses: 83 | if (not isinstance(addr, basestring)) or addr == "": 84 | continue 85 | if re.match('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$', addr): 86 | for type in interface_types: 87 | interfaces.append({"type": type, "useip": 1, "ip": addr, "dns": "", "port": ports[type], "main": 0}) 88 | else: 89 | for type in interface_types: 90 | interfaces.append({"type": type, "useip": 0, "ip": "", "dns": addr, "port": ports[type], "main": 0}) 91 | if interfaces: 92 | if main: 93 | for type in interface_types: 94 | type_interfaces = [interface for interface in interfaces if interface["type"] == type] 95 | main_interfaces = [interface for interface in type_interfaces if interface["useip"] == 0] 96 | if main_interfaces: 97 | main_interfaces[0]["main"] = 1 98 | elif type_interfaces: 99 | type_interfaces[0]["main"] = 1 100 | return interfaces 101 | else: 102 | for type in interface_types: 103 | interfaces.append({"type": type, "useip": 0, "ip": "", "dns": "dummy-interface.invalid", "port": ports[type], "main": 1 if main else 0}) 104 | return interfaces 105 | 106 | def interfaces_to_addresses(self, interfaces): 107 | addresses = [] 108 | if not isinstance(interfaces, list): 109 | interfaces = [interfaces] 110 | for interface in interfaces: 111 | if (not isinstance(interface, dict)) or "useip" not in interface: 112 | continue 113 | addr = interface["ip"] if int(interface["useip"]) == 1 else interface["dns"] 114 | addresses.append(addr) 115 | return addresses 116 | 117 | def get_zabbix_host(self, hostname): 118 | hosts = self.zabbix_api.host.get({ 119 | "filter": {"host": hostname}, 120 | "output": "extend", 121 | "selectInterfaces": "extend", 122 | "selectMacros": "extend", 123 | "selectGroups": "extend", 124 | "selectInventory": "extend", 125 | }) 126 | if hosts: 127 | return hosts[0] 128 | else: 129 | return None 130 | 131 | def get_zabbix_interfaces(self, hostname, interface_type=None, main=False): 132 | hosts = self.zabbix_api.host.get({ 133 | "filter": {"host": hostname}, 134 | "selectInterfaces": "extend", 135 | }) 136 | if hosts: 137 | interfaces = hosts[0]["interfaces"] 138 | if isinstance(interfaces, dict): 139 | interfaces = interfaces.values() 140 | if interface_type: 141 | interfaces = [interface for interface in interfaces if int(interface["type"]) == interface_type] 142 | if main: 143 | interfaces = [interface for interface in interfaces if int(interface["main"]) == 1] 144 | return interfaces 145 | else: 146 | return [] 147 | 148 | def get_user_macro(self, hostname, key): 149 | if not key: 150 | return None 151 | if hostname: 152 | hosts = self.zabbix_api.host.get({"filter": {"host": hostname}, "selectMacros": "extend", "selectParentTemplates": ["templateid"]}) 153 | if hosts: 154 | # search host macro 155 | if "macros" in hosts[0]: 156 | # host macro format on Zabbix 2.0: 157 | # {'1': {'macro': '{$MACRO_NAME}', 'hostmacroid': '1', 'hostid': '10001', 'value': 'Macro value'}} 158 | # host macro format on Zabbix 2.2 159 | # [{'macro': '{$MACRO_NAME}', 'hostmacroid': '1', 'hostid': '10001', 'hosts': [{'hostid': '10001'}], 'value': 'Macro value'} 160 | macros = hosts[0]["macros"] 161 | if isinstance(macros, dict): 162 | macros = macros.values() 163 | macros = [macro for macro in macros if macro["macro"] == key] 164 | if macros: 165 | return macros[0]["value"] 166 | # search template macro 167 | template_ids = [template["templateid"] for template in hosts[0]["parentTemplates"]] 168 | templates = self.zabbix_api.template.get({"templateids": template_ids, "selectMacros": "extend"}) 169 | for template in templates: 170 | if "macros" in template: 171 | # template macro format on Zabbix 2.0: 172 | # [{'macro': '{$MACRO_NAME}', 'hostmacroid': '1', 'hostid': '10001', 'value': 'Macro value'}] 173 | macros = [macro for macro in template["macros"] if macro["macro"] == key] 174 | if macros: 175 | return macros[0]["value"] 176 | global_macros = self.zabbix_api.usermacro.get({"globalmacro": True, "filter": {"macro": key}}) 177 | if global_macros: 178 | return global_macros[0]["value"] 179 | else: 180 | return None 181 | 182 | def get_group_ids(self, owner_hostname, key=None): 183 | if not key: 184 | key = self.MACRO_GROUPS 185 | macro_value = self.get_user_macro(owner_hostname, key) 186 | group_names = [group_name.strip() for group_name in macro_value.split(',')] if macro_value else [] 187 | groups = [] 188 | for group_name in group_names: 189 | grp = self.zabbix_api.hostgroup.get({"filter": {"name": group_name}}) 190 | if grp: 191 | groups.append({"groupid": grp[0]["groupid"]}) 192 | else: 193 | self.logger.info("Group '%s' does not exist. Create group." % group_name) 194 | try: 195 | response = self.zabbix_api.hostgroup.create({"name": group_name}) 196 | groups.append({"groupid": response["groupids"][0]}) 197 | except ZabbixAPIException, e: 198 | self.logger.warning("Cannot create group: %s" % str(e)) 199 | return groups 200 | 201 | def get_template_ids(self, owner_hostname, key=None): 202 | if not key: 203 | key = self.MACRO_REQUIRED_TEMPLATES 204 | macro_value = self.get_user_macro(owner_hostname, key) 205 | template_names = [template_name.strip() for template_name in macro_value.split(',')] if macro_value else [] 206 | templates = [] 207 | for template_name in template_names: 208 | tmpl = self.zabbix_api.template.get({"filter": {"host": template_name}}) 209 | if tmpl: 210 | templates.append({"templateid": tmpl[0]["templateid"]}) 211 | else: 212 | self.logger.warning("Template '%s' does not exist" % template_name) 213 | return templates 214 | 215 | def get_user_template_ids(self, owner_hostname, node): 216 | templates = self.get_template_ids(owner_hostname, self.MACRO_TEMPLATES) 217 | if "platform" not in node.extra: 218 | self.logger.warning("Unknown platform: %s" % node.id) 219 | return templates 220 | else: 221 | if node.extra["platform"] is not None and node.extra["platform"].lower().find("windows") != -1: 222 | key = self.MACRO_TEMPLATES_WINDOWS 223 | else: 224 | key = self.MACRO_TEMPLATES_LINUX 225 | return templates + self.get_template_ids(owner_hostname, key) 226 | 227 | def get_assigned_template_ids(self, host): 228 | templates = [] 229 | for tmpl in host['parentTemplates']: 230 | templates.append({"templateid": tmpl["templateid"]}) 231 | return templates 232 | 233 | def adjust_string_length(self, base_string, suffix, max_length): 234 | if len(suffix) == 0: 235 | if len(base_string) > max_length: 236 | return base_string[0:max_length - len("..")] + ".." 237 | else: 238 | return base_string 239 | else: 240 | if len(base_string) + len(suffix) > max_length: 241 | if max_length < len(suffix): 242 | return ".." + suffix[len(suffix) - max_length + len(".."):] 243 | return base_string[0:max_length - len(".._" + suffix)] + ".._" + suffix 244 | else: 245 | return base_string + "_" + suffix 246 | -------------------------------------------------------------------------------- /hyclops/connector/ipmi.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | 19 | import logging 20 | import subprocess 21 | import traceback 22 | from libcloud.compute.types import NodeState 23 | from hyclops.connector.base import BaseConnector 24 | 25 | 26 | class IPMIConnector(BaseConnector): 27 | 28 | def __init__(self, config): 29 | super(IPMIConnector, self).__init__(config) 30 | self.type = "ipmi" 31 | self.logger = logging.getLogger('connector.ipmi') 32 | self.ipmitool = config["ipmi"].get('ipmitool', 'ipmitool') 33 | 34 | def __call__(self, hostname, params): 35 | try: 36 | result = self.run_command(hostname, params) 37 | if result: 38 | self.logger.debug("success. thread finished.") 39 | else: 40 | self.logger.warning("failed. thread finished.") 41 | except Exception: 42 | self.logger.error(traceback.format_exc()) 43 | 44 | def run_command(self, hostname, params): 45 | command = params.get('command', 'monitor') 46 | conn_params = self.get_connection_parameters(hostname) 47 | if conn_params is None: 48 | self.logger.warning("Failed to get credentials from zabbix") 49 | return False 50 | if command == "monitor": 51 | return self.monitor(hostname, conn_params) 52 | else: 53 | allow_commands = {"start": "on", "stop": "off", "reboot": "reset"} 54 | if not command in allow_commands.keys(): 55 | self.logger.warning("command %s is not supported" % command) 56 | return False 57 | else: 58 | output = self.run_ipmitool(allow_commands[command], **conn_params) 59 | return output is not None 60 | 61 | def get_connection_parameters(self, hostname): 62 | host = self.get_zabbix_host(hostname) 63 | if not host: 64 | self.logger.warning("zabbix host %s does not exist" % hostname) 65 | return 66 | interfaces = self.get_zabbix_interfaces(hostname, interface_type=3) 67 | ipmi_ip = self.interfaces_to_addresses(interfaces) 68 | if not ipmi_ip: 69 | self.logger.warning("ipmi interfaces does not exist on %s" % hostname) 70 | return 71 | else: 72 | ipmi_ip = ipmi_ip[0] 73 | key = host["ipmi_username"] 74 | secret = host["ipmi_password"] 75 | return {"ipmi_ip": ipmi_ip, "key": key, "secret": secret} 76 | 77 | def run_ipmitool(self, command, ipmi_ip, key, secret): 78 | cmd = [self.ipmitool, "-H", ipmi_ip, "-U", key, "-P", secret, "chassis", "power", command] 79 | try: 80 | output, error = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() 81 | output = output.strip() 82 | return output 83 | except subprocess.CalledProcessError: 84 | self.logger.error("Failed to %s target server" % command) 85 | return None 86 | 87 | def monitor(self, hostname, conn_params): 88 | output = self.run_ipmitool("status", **conn_params) 89 | if output == "Chassis Power is on": 90 | state = NodeState.RUNNING 91 | state_text = "running" 92 | result = True 93 | elif output == "Chassis Power is off": 94 | state = NodeState.TERMINATED 95 | state_text = "stopped" 96 | result = True 97 | else: 98 | self.logger.error("Failed to get IPMI status: %s" % output) 99 | state = NodeState.UNKNOWN 100 | state_text = "unknown" 101 | result = False 102 | # Update zabbix item 103 | self.zabbix_sender(hostname, "ipmi.state", state) 104 | self.zabbix_sender(hostname, "ipmi.state.text", state_text) 105 | return result 106 | -------------------------------------------------------------------------------- /hyclops/libcloud_driver/__init__.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | -------------------------------------------------------------------------------- /hyclops/libcloud_driver/vsphere.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from libcloud.compute.types import NodeState 19 | from libcloud.compute.base import Node, NodeDriver 20 | from psphere.client import Client 21 | from psphere.managedobjects import HostSystem 22 | 23 | 24 | class VSphereNodeDriver(NodeDriver): 25 | """ 26 | VMware vSphere node driver 27 | """ 28 | 29 | NODE_STATE_MAP = { 30 | 'poweredOn': NodeState.RUNNING, 31 | 'poweredOff': NodeState.UNKNOWN, 32 | 'suspended': NodeState.PENDING, 33 | } 34 | NODE_STATUS_MAP = { 35 | 'poweredOn': 'running', 36 | 'poweredOff': 'stopped', 37 | 'suspended': 'suspended', 38 | } 39 | 40 | def __init__(self, key, secret=None, secure=True, host=None): 41 | self.key = key 42 | self.secret = secret 43 | self.host = host 44 | self.connection = Client(server=host, username=key, password=secret) 45 | 46 | def _to_node(self, vm): 47 | public_ips = [] 48 | if vm.guest.toolsRunningStatus == "guestToolsRunning" and hasattr(vm.guest, "net"): 49 | for network in vm.guest.net: 50 | if hasattr(network, "ipConfig"): 51 | public_ips.extend([ip.ipAddress for ip in network.ipConfig.ipAddress if ip.state == "preferred"]) 52 | elif hasattr(network, "ipAddress"): 53 | public_ips.extend([ip for ip in network.ipAddress]) 54 | elif hasattr(vm.guest, "ipAddress"): 55 | public_ips.append(vm.guest.ipAddress) 56 | quickStats = vm.summary.quickStats 57 | cpu_usage = quickStats.overallCpuUsage if hasattr(quickStats, "overallCpuUsage") else 0 58 | memory_usage = quickStats.guestMemoryUsage * 1024 ** 2 if hasattr(quickStats, "guestMemoryUsage") else 0 59 | if hasattr(vm.runtime, "question"): 60 | stuck_state = 1 61 | question_id = vm.runtime.question.id 62 | choices = [{"key": info.key, "label": info.label, "summary": info.summary} for info in vm.runtime.question.choice.choiceInfo] 63 | if hasattr(vm.runtime.question, "message"): 64 | question = "Message: " 65 | for message in vm.runtime.question.message: 66 | if hasattr(message, "text"): 67 | question += message.text + "\n" 68 | else: 69 | question = vm.runtime.question.text 70 | else: 71 | stuck_state = 0 72 | question_id = None 73 | question = None 74 | choices = None 75 | node = Node( 76 | id=vm.config.uuid, 77 | name=vm.name, 78 | state=self.NODE_STATE_MAP[vm.runtime.powerState], 79 | public_ips=public_ips, 80 | private_ips=[], 81 | driver=self, 82 | extra={ 83 | 'managedObjectReference': vm, 84 | 'status': self.NODE_STATUS_MAP[vm.runtime.powerState], 85 | 'cpu': vm.summary.config.numCpu, 86 | 'memory': vm.summary.config.memorySizeMB * 1024 ** 2, 87 | 'toolsRunningStatus': str(vm.guest.toolsRunningStatus), 88 | 'toolsVersionStatus': str(vm.guest.toolsVersionStatus), 89 | 'cpu_usage': 100 * float(cpu_usage) / vm.summary.runtime.maxCpuUsage, 90 | 'memory_usage': memory_usage, 91 | 'vmpath': vm.summary.config.vmPathName, 92 | 'stuck_state': stuck_state, 93 | 'stuck_question_id': question_id, 94 | 'stuck_question': question, 95 | 'stuck_choices': choices, 96 | 'platform': vm.summary.config.guestFullName, 97 | } 98 | ) 99 | return node 100 | 101 | def _to_hardware_profile(self, host): 102 | datastores = [] 103 | for ds in host.datastore: 104 | datastores.append({ 105 | 'name': ds.name, 106 | 'freeSpace': ds.summary.freeSpace, 107 | 'capacity': ds.summary.capacity, 108 | 'type': ds.summary.type, 109 | }) 110 | hardware_profile = { 111 | 'id': host.summary.hardware.uuid, 112 | 'name': host.name, 113 | 'cpu': host.hardware.cpuInfo.numCpuThreads, 114 | 'cpu_usage': 100 * float(host.summary.quickStats.overallCpuUsage) / (host.summary.hardware.cpuMhz * host.summary.hardware.numCpuCores), 115 | 'cpu_assigned': sum([vm.summary.config.numCpu for vm in host.vm if vm.runtime.powerState == "poweredOn"]), 116 | 'memory': host.hardware.memorySize, 117 | 'memory_usage': host.summary.quickStats.overallMemoryUsage * 1024 ** 2, 118 | 'memory_assigned': sum([vm.summary.config.memorySizeMB * 1024 ** 2 for vm in host.vm if vm.runtime.powerState == "poweredOn"]), 119 | 'datastores': datastores, 120 | } 121 | return hardware_profile 122 | 123 | def list_nodes(self, ex_node_ids=None, ex_vmpath=None): 124 | nodes = [] 125 | hosts = HostSystem.all(self.connection) 126 | for host in hosts: 127 | for vm in host.vm: 128 | node = self._to_node(vm) 129 | if ex_node_ids is None or node.id in ex_node_ids: 130 | if ex_vmpath is None or vm.summary.config.vmPathName == ex_vmpath: 131 | nodes.append(node) 132 | uuids = [node.id for node in nodes] 133 | if ex_node_ids is not None and len(uuids) != len(set(uuids)): 134 | raise Exception("Cannot identify target node. Duplicate uuid exists") 135 | else: 136 | return nodes 137 | 138 | def reboot_node(self, node): 139 | vm = node.extra['managedObjectReference'] 140 | try: 141 | vm.RebootGuest() 142 | return True 143 | except: 144 | return False 145 | 146 | def destroy_node(self, node): 147 | vm = node.extra['managedObjectReference'] 148 | try: 149 | vm.UnregisterVM() 150 | return True 151 | except: 152 | return False 153 | 154 | def ex_start_node(self, node): 155 | vm = node.extra['managedObjectReference'] 156 | try: 157 | task = vm.PowerOnVM_Task() 158 | return task.info.state != "error" 159 | except: 160 | return False 161 | 162 | def ex_stop_node(self, node): 163 | vm = node.extra['managedObjectReference'] 164 | try: 165 | task = vm.PowerOffVM_Task() 166 | return task.info.state != "error" 167 | except: 168 | return False 169 | 170 | def ex_shutdown_node(self, node): 171 | vm = node.extra['managedObjectReference'] 172 | try: 173 | vm.ShutdownGuest() 174 | return True 175 | except: 176 | return False 177 | 178 | def ex_suspend_node(self, node): 179 | vm = node.extra['managedObjectReference'] 180 | try: 181 | task = vm.SuspendVM_Task() 182 | return task.info.state != "error" 183 | except: 184 | return False 185 | 186 | def ex_answer_node(self, node, choice): 187 | vm = node.extra['managedObjectReference'] 188 | try: 189 | if hasattr(vm.runtime, "question"): 190 | vm.AnswerVM(questionId=vm.runtime.question.id, answerChoice=choice) 191 | return True 192 | else: 193 | return False 194 | except: 195 | return False 196 | 197 | def ex_hardware_profiles(self): 198 | hardware_profiles = [] 199 | hosts = HostSystem.all(self.connection) 200 | for host in hosts: 201 | hardware_profiles.append(self._to_hardware_profile(host)) 202 | return hardware_profiles 203 | -------------------------------------------------------------------------------- /hyclops/queue.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import zmq 19 | import json 20 | import logging 21 | import threading 22 | from hyclops.connector.ec2 import EC2Connector 23 | from hyclops.connector.vsphere import VSphereConnector 24 | from hyclops.connector.ipmi import IPMIConnector 25 | 26 | 27 | class MessageQueue(): 28 | 29 | def __init__(self, config): 30 | self.connectors = { 31 | "ec2": EC2Connector(config), 32 | "vsphere": VSphereConnector(config), 33 | "ipmi": IPMIConnector(config), 34 | } 35 | self.socket = None 36 | self.threads = [] 37 | self.logger = logging.getLogger("hyclops.queue") 38 | 39 | def _recv_message(self): 40 | if self.socket is None: 41 | return None 42 | message = self.socket.recv_pyobj() 43 | self.logger.debug("recieved request: [%s]" % str(message)) 44 | if not isinstance(message, list) or len(message) < 2: 45 | self.logger.warning("bad message: [%s]" % str(message)) 46 | return None 47 | driver = message[0] 48 | zabbix_hostname = message[1] 49 | if len(message) > 2: 50 | try: 51 | params = json.loads(message[2]) 52 | except ValueError, e: 53 | self.logger.warning("%s: %s" % (str(e), message[2])) 54 | params = {} 55 | else: 56 | params = {} 57 | return {"driver": driver, "zabbix_hostname": zabbix_hostname, "params": params} 58 | 59 | def bind(self, listen_address="127.0.0.1", listen_port=5555, max_queue_size=100): 60 | context = zmq.Context() 61 | socket = context.socket(zmq.PULL) 62 | try: 63 | socket.setsockopt(zmq.HWM, max_queue_size) 64 | except AttributeError: 65 | socket.setsockopt(zmq.SNDHWM, max_queue_size) 66 | socket.setsockopt(zmq.RCVHWM, max_queue_size) 67 | try: 68 | socket.bind("tcp://%s:%s" % (listen_address, listen_port)) 69 | self.socket = socket 70 | self.logger.info("Start message queue") 71 | except Exception, e: 72 | self.logger.error("Failed to bind ZeroMQ socket: %s" % str(e)) 73 | socket.close() 74 | raise 75 | 76 | def poll(self): 77 | self.logger.debug("polling...") 78 | msg = self._recv_message() 79 | if msg is None: 80 | return 81 | for thread in self.threads: 82 | if not thread.isAlive(): 83 | self.threads.remove(thread) 84 | command = msg["params"].get("command", "monitor") 85 | thread_name = "%s-%s-%s" % (msg["driver"], msg["zabbix_hostname"], command) 86 | alive_thread_names = [thread.name for thread in self.threads] 87 | if thread_name in alive_thread_names: 88 | self.logger.debug("thread %s already running. skipped." % thread_name) 89 | elif msg["driver"] in self.connectors: 90 | self.logger.debug("run %s thread" % thread_name) 91 | th = threading.Thread(name=thread_name, target=self.connectors[msg["driver"]], 92 | kwargs={"hostname": msg["zabbix_hostname"], "params": msg["params"]}) 93 | th.start() 94 | self.threads.append(th) 95 | else: 96 | self.logger.warning("'%s' driver is not supported" % str(msg["driver"])) 97 | self.logger.debug("running threads: %s" % [{thread.name: thread.isAlive()} for thread in self.threads]) 98 | 99 | def close(self): 100 | self.logger.info("SystemExit") 101 | if self.socket is not None: 102 | self.socket.close() 103 | self.socket = None 104 | for thread in self.threads: 105 | # Bad practice. but no idea 106 | thread._Thread__stop() 107 | -------------------------------------------------------------------------------- /misc/cron_scripts/delete_not_exist_hosts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # HyClops for Zabbix 4 | # Copyright 2013 TIS Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | import os 21 | import logging 22 | import configobj 23 | import argparse 24 | from zabbix_api import ZabbixAPI 25 | from datetime import datetime 26 | import time 27 | 28 | # get options 29 | parser = argparse.ArgumentParser(description='Delete Zabbix hosts tool') 30 | parser.add_argument('-u', '--username', help='Zabbix API access username') 31 | parser.add_argument('-p', '--password', help='Zabbix API access password') 32 | parser.add_argument('-f', '--url', help='Zabbix frontend url') 33 | parser.add_argument('-g', '--group', help='Target Zabbix host group name') 34 | parser.add_argument('-d', '--days', help='Set expire period (days)') 35 | parser.add_argument('-s', '--seconds', help='Set expire period (seconds)') 36 | args = parser.parse_args() 37 | 38 | # load config 39 | config_file = "/opt/hyclops/hyclops.conf" 40 | config = configobj.ConfigObj(config_file) 41 | log_level = config["logging"]["log_level"] 42 | log_file = config["logging"]["log_file"] 43 | log_format = '[%(asctime)s] %(name)s (%(threadName)s) %(levelname)s: %(message)s' 44 | logging.basicConfig(filename=log_file, level=logging.WARNING, format=log_format) 45 | logger = logging.getLogger('delete_hosts') 46 | logger.setLevel(getattr(logging, log_level)) 47 | 48 | frontend_url = args.url if args.url else config["zabbix"]["frontend_url"] 49 | username = args.username if args.url else config["zabbix"]["zabbix_user"] 50 | password = args.password if args.password else config["zabbix"]["zabbix_password"] 51 | group_name = args.group if args.group else "Not exist hosts" 52 | 53 | if args.days: 54 | expire_period = int(args.days) * 24 * 60 * 60 55 | elif args.seconds: 56 | expire_period = int(args.seconds) 57 | else: 58 | expire_period = 2592000 # (2592000seconds = 30days) 59 | 60 | zabbix_api = ZabbixAPI(frontend_url) 61 | zabbix_api.login(username, password) 62 | 63 | group = zabbix_api.hostgroup.get({"filter": {"name": group_name}})[0] 64 | hosts = zabbix_api.host.get({"groupids": group["groupid"], "filter": {"status": 1}}) 65 | for host in hosts: 66 | item = zabbix_api.item.get({"hostids": host["hostid"], "search": {"key_": "instance.state"}}) 67 | if item: 68 | history = zabbix_api.history.get({"itemids": item[0]["itemid"], "limit": 1, "output": "extend"}) 69 | if history: 70 | now = int(time.mktime(datetime.now().timetuple())) 71 | if (now - int(history[0]["clock"])) < expire_period: 72 | continue 73 | try: 74 | zabbix_api.host.delete({"hostid": host["hostid"]}) 75 | print "Succeeded delete host :%s" % host["hostid"] 76 | logger.info("Succeeded delete host :%s" % host["hostid"]) 77 | except Exception, e: 78 | print "Failed delete host :%s" % host["hostid"] 79 | logger.error("Failed delete host :%s" % e) 80 | print "finish %s" % os.path.basename(__file__) 81 | logger.debug("finish %s" % os.path.basename(__file__)) 82 | -------------------------------------------------------------------------------- /misc/externalscripts/get_aws_charges.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # HyClops for Zabbix 4 | # Copyright 2013 TIS Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | import sys 21 | import datetime 22 | from boto.ec2 import cloudwatch 23 | 24 | if len(sys.argv) <= 2: 25 | print -1, 26 | sys.exit("Usage: %s {aws_access_key} {aws_secret_key}" % sys.argv[0]) 27 | else: 28 | key = sys.argv[1] 29 | secret = sys.argv[2] 30 | 31 | region = "us-east-1" # billing status belongs to us-east-1 32 | conn = cloudwatch.connect_to_region(region, aws_access_key_id=key, aws_secret_access_key=secret) 33 | statistics = conn.get_metric_statistics( 34 | period=300, 35 | start_time=datetime.datetime.now() - datetime.timedelta(days=1), 36 | end_time=datetime.datetime.now(), 37 | metric_name="EstimatedCharges", 38 | namespace="AWS/Billing", 39 | statistics=["Maximum"], 40 | dimensions={"Currency": "USD"}, 41 | unit=None 42 | ) 43 | 44 | statistics = sorted(statistics, key=lambda stat: stat["Timestamp"], reverse=True) 45 | # return latest data 46 | print statistics[0]["Maximum"], 47 | -------------------------------------------------------------------------------- /misc/externalscripts/push_message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # HyClops for Zabbix 3 | # Copyright 2013 TIS Inc. 4 | # 5 | # This program is free software; you can redistribute it and/or 6 | # modify it under the terms of the GNU General Public License 7 | # as published by the Free Software Foundation; either version 2 8 | # of the License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program; if not, write to the Free Software 17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | # 19 | # Usage: push_message.py [params] 20 | # 21 | 22 | import sys 23 | import zmq 24 | import pickle 25 | 26 | SUCCESS = 0 27 | FAILURE = 1 28 | DEFAULT_TIMEOUT = 3 29 | 30 | if len(sys.argv) <= 4: 31 | print FAILURE 32 | sys.exit(1) 33 | server_address = sys.argv[1] 34 | server_port = sys.argv[2] 35 | msg = pickle.dumps(sys.argv[3:]) 36 | timeout = DEFAULT_TIMEOUT 37 | 38 | context = zmq.Context() 39 | socket = context.socket(zmq.PUSH) 40 | socket.setsockopt(zmq.LINGER, 0) 41 | try: 42 | socket.connect("tcp://%s:%s" % (server_address, server_port)) 43 | tracker = socket.send(msg, copy=False, track=True) 44 | tracker.wait(timeout) 45 | print SUCCESS 46 | except: 47 | print FAILURE 48 | finally: 49 | socket.close() 50 | -------------------------------------------------------------------------------- /misc/globalscripts/request_action.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # HyClops for Zabbix 4 | # Copyright 2013 TIS Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or 7 | # modify it under the terms of the GNU General Public License 8 | # as published by the Free Software Foundation; either version 2 9 | # of the License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | import os 21 | import sys 22 | import json 23 | import logging 24 | import configobj 25 | from hyclops.connector.ec2 import EC2Connector 26 | from hyclops.connector.vsphere import VSphereConnector 27 | from hyclops.connector.ipmi import IPMIConnector 28 | 29 | if len(sys.argv) < 4: 30 | print "Usage for zabbix: %s[driver_name, {HOST.HOST}, {'command': command}]" % os.path.basename(sys.argv[0]) 31 | sys.exit() 32 | driver_name = sys.argv[1] 33 | zabbix_hostname = sys.argv[2] 34 | params = json.loads(sys.argv[3]) or {} 35 | config_file = "/opt/hyclops/hyclops.conf" 36 | 37 | # load config 38 | config = configobj.ConfigObj(config_file) 39 | log_level = config["logging"]["log_level"] 40 | log_file = config["logging"]["log_file"] 41 | log_format = '[%(asctime)s] %(name)s (%(threadName)s) %(levelname)s: %(message)s' 42 | logging.basicConfig(filename=log_file, level=logging.WARNING, format=log_format) 43 | logger = logging.getLogger('request_action') 44 | logger.setLevel(getattr(logging, log_level)) 45 | if "environments" in config: 46 | for key, value in config["environments"].items(): 47 | os.environ[key] = value 48 | 49 | # run command 50 | if driver_name == "ec2": 51 | connector = EC2Connector(config) 52 | elif driver_name == "vsphere": 53 | connector = VSphereConnector(config) 54 | elif driver_name == "ipmi": 55 | connector = IPMIConnector(config) 56 | else: 57 | print "%s driver does not supported." % driver_name 58 | sys.exit() 59 | try: 60 | result = connector.run_command(zabbix_hostname, params) 61 | print result 62 | except Exception, e: 63 | print e 64 | -------------------------------------------------------------------------------- /misc/import_data/globalmacros.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.0 4 | 2013-04-30T08:35:59Z 5 | 6 | 7 | {$GATEONE_URL} 8 | GateOne URL 9 | 10 | 11 | {$GATEONE_KEY} 12 | GateOne API Key 13 | 14 | 15 | {$GATEONE_SECRET} 16 | GateOne Secret Key 17 | 18 | 19 | {$HYCLOPS_SERVER} 20 | 127.0.0.1 21 | 22 | 23 | {$HYCLOPS_PORT} 24 | 5555 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /misc/import_data/globalscripts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.0 4 | 2013-04-30T08:35:59Z 5 | 6 | 22 | 38 | 54 | 70 | 86 | 102 | 118 | 134 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /misc/init.d/redhat/hyclops: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # chkconfig: - 90 10 4 | # description: Zabbix extention daemon 5 | # config: /opt/hyclops/hyclops.conf 6 | # pidfile: /tmp/hyclops.pid 7 | # 8 | 9 | # Source function library. 10 | . /etc/rc.d/init.d/functions 11 | 12 | prog=hyclops 13 | exec=/opt/$prog/run.py 14 | conf=/opt/$prog/$prog.conf 15 | lockfile=/var/lock/subsys/$prog 16 | pidfile=/var/run/$prog/$prog.pid 17 | user=hyclops 18 | 19 | start() 20 | { 21 | if status -p $pidfile ${0##*/} > /dev/null ; then 22 | echo "Service ${0##*/} is already running" 23 | return 0 24 | else 25 | echo -n $"Starting $prog: " 26 | daemon --user $user --pidfile $pidfile "$exec -c $conf --pid $pidfile > /dev/null 2>&1" 27 | RETVAL=$? 28 | echo 29 | [ $RETVAL -eq 0 ] && touch $lockfile 30 | return $RETVAL 31 | fi 32 | } 33 | 34 | stop() 35 | { 36 | echo -n $"Shutting down $prog: " 37 | killproc -p $pidfile 38 | RETVAL=$? 39 | echo 40 | [ $RETVAL -eq 0 ] && rm -f $lockfile $pidfile 41 | return $RETVAL 42 | } 43 | 44 | case "$1" in 45 | start) 46 | start 47 | ;; 48 | stop) 49 | stop 50 | ;; 51 | status) 52 | status -p $pidfile ${0##*/} 53 | ;; 54 | restart) 55 | stop 56 | start 57 | ;; 58 | force-reload) 59 | restart 60 | ;; 61 | try-restart|condrestart) 62 | if status -p $pidfile ${0##*/} >/dev/null ; then 63 | restart 64 | fi 65 | ;; 66 | reload) 67 | action $"Service ${0##*/} does not support the reload action: " /bin/false 68 | exit 3 69 | ;; 70 | *) 71 | echo $"Usage: $0 {start|stop|status|restart|try-restart|force-reload}" 72 | exit 2 73 | ;; 74 | esac 75 | exit $? 76 | -------------------------------------------------------------------------------- /misc/init.d/ubuntu/hyclops.conf: -------------------------------------------------------------------------------- 1 | # hyclops - Start message poller 2 | description "HyClops" 3 | author "TIS Inc." 4 | start on runlevel [2345] 5 | stop on runlevel [016] 6 | respawn 7 | 8 | exec /opt/hyclops/run.py -c /opt/hyclops/hyclops.conf --pid /var/run/hyclops/hyclops.pid 9 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.0/custom/additional_blocks.inc.php: -------------------------------------------------------------------------------- 1 | setName('ipmi_control'); 30 | $ipmicontrol_form->setAttribute('id','ipmi_control'); 31 | $ipmicontrol_form->addVar('driver', 'ipmi'); 32 | $script_itemkey = 'push_message.py[{$HYCLOPS_SERVER},{$HYCLOPS_PORT},ipmi,{HOST.HOST}]'; 33 | 34 | $table->setHeader(array( 35 | is_show_all_nodes() ? S_NODE : null, 36 | new CCheckBox('all_physical_hosts', null, "checkAllHosts('".$ipmicontrol_form->getName()."', 'all_physical_hosts', '');"), 37 | _('Host name'), 38 | _('IPMI interface'), 39 | _('Status'), 40 | _('Problems'), 41 | )); 42 | 43 | $hosts = get_ipmi_hosts(); 44 | if(empty($hosts)){ 45 | return null; 46 | } 47 | 48 | foreach( $hosts as $host ){ 49 | $triggers = get_triggers($host['hostid']); 50 | $trigger_count = count($triggers); 51 | 52 | // get State 53 | if(!$preloading){ 54 | if(is_script_success($host['hostid'], $script_itemkey)){ 55 | $state = strtolower(get_item_value($host['hostid'], "ipmi.state.text")); 56 | }else{ 57 | $state = _('script failed'); 58 | } 59 | }else{ 60 | $state = _('loading...'); 61 | } 62 | 63 | // Check box 64 | $checkbox_col = new CCheckBox("hostids[]",null,null,$host['hostid']); 65 | if(!is_operation_available("ipmi", $state)){ 66 | $checkbox_col->attributes['disabled'] = 'disabled'; 67 | } 68 | 69 | // Host name 70 | if($trigger_count == 0){ 71 | $hostname_col = new CCol(new CSpan($host['host'])); 72 | }else{ 73 | $hostname_col = new CLink(new CSpan($host['host']), 'tr_status.php?&hostid='.$host['hostid'].'&show_triggers='.TRIGGERS_OPTION_ONLYTRUE); 74 | } 75 | 76 | // IPMI Interfaces 77 | $ipmi_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_IPMI); 78 | $ipmi_ip_col = new CCol(reset($ipmi_addresses)); 79 | 80 | // Status 81 | $state_col = new CCol($state, get_item_level($state)); 82 | 83 | // Problem 84 | if($trigger_count == 0){ 85 | $problem_col = new CCol($trigger_count,'normal'); 86 | }else{ 87 | $problem_col = new CCol($trigger_count,'high'); 88 | $problem_col->setHint(make_trigger_table($triggers,$hostname_col)); 89 | } 90 | 91 | $r = new CRow(); 92 | $r->addItem($checkbox_col); 93 | $r->addItem($hostname_col); 94 | $r->addItem($ipmi_ip_col); 95 | $r->addItem($state_col); 96 | $r->addItem($problem_col); 97 | $table->addRow($r); 98 | } 99 | 100 | $table->setFooter(new CCol(make_operation_box_footer(zbx_objectValues($hosts, 'hostid'), $form_name))); 101 | $ipmicontrol_form->addItem($table); 102 | $script = new CJSScript(get_js("jQuery('#hat_ipmistat_footer').html('"._s('Updated: %s', zbx_date2str(_('H:i:s')))."')")); 103 | return new CDiv(array($ipmicontrol_form, $script)); 104 | } 105 | 106 | function make_vspherestat_summary($preloading = false){ 107 | $table = new CTableInfo(); 108 | $table->setHeader(array( 109 | is_show_all_nodes() ? S_NODE : null, 110 | _('Host name'), 111 | _('Interface'), 112 | _('CPU threads [used/total]'), 113 | _('Memory(GB) [used/total]'), 114 | _('Datastores'), 115 | _('PoweredOn'), 116 | _('Suspended'), 117 | _('PoweredOff'), 118 | _('Others'), 119 | )); 120 | $hidden = new CDiv(null, "hidden"); 121 | $hidden->setAttribute("type", "hidden"); 122 | $script_itemkey = 'push_message.py[{$HYCLOPS_SERVER},{$HYCLOPS_PORT},vsphere,{HOST.HOST}]'; 123 | $hosts = get_vsphere_hosts(); 124 | if(empty($hosts)){ 125 | return null; 126 | } 127 | foreach( $hosts as $host ){ 128 | if(!$preloading){ 129 | if(is_script_success($host['hostid'], $script_itemkey)){ 130 | $hardware_profiles = get_hardware_profiles($host['hostid']); 131 | $datastores = get_datastores($host['hostid']); 132 | $instances = get_instances($host['hostid']); 133 | $instances = filter_instances($instances); 134 | 135 | $r = new CRow(); 136 | 137 | // Host name 138 | $col = new CCol($host['host'], 'vsphere'); 139 | $col->setAttribute("hostid", $host['hostid']); 140 | $r->addItem($col); 141 | 142 | // IP 143 | $snmp_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_SNMP); 144 | $r->addItem(reset($snmp_addresses)); 145 | 146 | // CPU thread 147 | $r->addItem(level_check($hardware_profiles['cpu_assigned'], $hardware_profiles['cpu'])); 148 | 149 | // Memory 150 | $r->addItem(level_check($hardware_profiles['memory_assigned'], $hardware_profiles['memory'])); 151 | 152 | // Datastores 153 | $datastores_count = new CSpan(count($datastores), 'pointer'); 154 | if(!empty($datastores)) $datastores_count->setHint(make_datastore_table($host['hostid'], $datastores)); 155 | $r->addItem(new CCol($datastores_count)); 156 | 157 | // PowerOn 158 | $poweron_vms = $instances["running"]; 159 | $poweron_count = new CSpan(count($poweron_vms), 'pointer'); 160 | if(!empty($poweron_vms)) $poweron_count->setHint(make_vm_table('poweron', $poweron_vms)); 161 | $r->addItem(new CCol($poweron_count)); 162 | 163 | // Suspended 164 | $suspend_vms = $instances["pending"]; 165 | $suspend_count = new CSpan(count($suspend_vms), 'pointer'); 166 | if(!empty($suspend_vms)) $suspend_count->setHint(make_vm_table('suspend', $suspend_vms)); 167 | $r->addItem(new CCol($suspend_count)); 168 | 169 | // PowerOff 170 | $poweroff_vms = $instances["stopped"]; 171 | $poweroff_count = new CSpan(count($poweroff_vms), 'pointer'); 172 | if(!empty($poweroff_vms)) $poweroff_count->setHint(make_vm_table('poweroff', $poweroff_vms)); 173 | $r->addItem(new CCol($poweroff_count)); 174 | 175 | // Other 176 | $other_vms = $instances["other"]; 177 | $other_count = new CSpan(count($other_vms), 'pointer'); 178 | if(!empty($other_vms)) $other_count->setHint(make_vm_table('other', $other_vms)); 179 | $other_state_col = new CCol($other_count, count($other_vms) > 0 ? 'high' : null); 180 | $r->addItem($other_state_col); 181 | 182 | $table->addRow($r); 183 | zbx_add_post_js('chkbxRange.pageGoName = "vms";'); 184 | }else{ 185 | $snmp_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_SNMP); 186 | $r = new CRow(); 187 | $r->addItem($host['host']); 188 | $r->addItem(reset($snmp_addresses)); 189 | $r->addItem(new CCol(_('script failed'), "high")); 190 | $r->addItem(array("-", "-", "-", "-", "-", "-")); 191 | $table->addRow($r); 192 | } 193 | }else{ 194 | // display loading message 195 | $snmp_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_SNMP); 196 | $r = new CRow(); 197 | $r->addItem($host['host']); 198 | $r->addItem(reset($snmp_addresses)); 199 | $r->addItem(_('loading...')); 200 | $r->addItem(array("-", "-", "-", "-", "-", "-")); 201 | $table->addRow($r); 202 | } 203 | } 204 | 205 | #insert_js('init_vsphere_contextmenu()'); 206 | $script = new CJSScript(get_js("jQuery('#hat_vspherestat_footer').html('"._s('Updated: %s', zbx_date2str(_('H:i:s')))."')")); 207 | return new CDiv(array($table, $script)); 208 | } 209 | 210 | function make_awsstat_summary($preloading = false){ 211 | $table = new CTableInfo(); 212 | $table->setHeader(array( 213 | is_show_all_nodes() ? S_NODE : null, 214 | _('Account name'), 215 | _('PoweredOn'), 216 | _('PoweredOff'), 217 | _('Billing/Month'), 218 | )); 219 | $script_itemkey = 'push_message.py[{$HYCLOPS_SERVER},{$HYCLOPS_PORT},ec2,{HOST.HOST}]'; 220 | 221 | $aws_accounts = get_aws_accounts(); 222 | if(empty($aws_accounts)){ 223 | return null; 224 | } 225 | foreach( $aws_accounts as $host ){ 226 | if(!$preloading){ 227 | if(is_script_success($host['hostid'], $script_itemkey)){ 228 | $instances = get_instances($host['hostid']); 229 | $instances = filter_instances($instances); 230 | $r = new CRow(); 231 | 232 | // Account name 233 | $col = new CCol($host['host']); 234 | $r->addItem($col); 235 | 236 | // Poweron (running + pending) 237 | $poweron_vms = array_merge($instances["running"], $instances["pending"]); 238 | $poweron_count = new CSpan(count($poweron_vms), 'pointer'); 239 | if(!empty($poweron_vms)) $poweron_count->setHint(make_ec2_table('ec2_poweron', $poweron_vms)); 240 | $r->addItem(new CCol($poweron_count)); 241 | 242 | // Poweroff (stopped + terminated + stopping + shutting-down) 243 | $poweroff_vms = $instances["stopped"]; 244 | $poweroff_count = new CSpan(count($poweroff_vms), 'pointer'); 245 | if(!empty($poweroff_vms)) $poweroff_count->setHint(make_ec2_table('ec2_poweroff', $poweroff_vms)); 246 | $r->addItem(new CCol($poweroff_count)); 247 | 248 | // AWS Charges 249 | $item = get_item_by_key('get_aws_charges.py[{$KEY},{$SECRET}]', $host["host"]); 250 | if(array_key_exists('lastvalue', $item)){ 251 | $r->addItem(new CLink($item["lastvalue"], "history.php?action=showgraph&itemid={$item["itemid"]}")); 252 | }else{ 253 | $r->addItem(new CCol(_('No data'))); 254 | } 255 | $table->addRow($r); 256 | zbx_add_post_js('chkbxRange.pageGoName = "vms";'); 257 | }else{ 258 | $r = new CRow(); 259 | $r->addItem($host['host']); 260 | $r->addItem(new CCol(_('script failed'), "high")); 261 | $r->addItem(array("-")); 262 | $table->addRow($r); 263 | } 264 | }else{ 265 | $r = new CRow(); 266 | $r->addItem($host['host']); 267 | $r->addItem(new CCol(_('loading...'))); 268 | $r->addItem(array("-")); 269 | $table->addRow($r); 270 | } 271 | } 272 | $script = new CJSScript(get_js("jQuery('#hat_awsstat_footer').html('"._s('Updated: %s', zbx_date2str(_('H:i:s')))."')")); 273 | return new CDiv(array($table, $script)); 274 | } 275 | 276 | ?> 277 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.0/custom/additional_blocks_func.inc.php: -------------------------------------------------------------------------------- 1 | $host*2){ 24 | $item = new CCol($guest.'/'.$host,'high'); 25 | }else if($guest > $host){ 26 | $item = new CCol($guest.'/'.$host,'warning'); 27 | }else{ 28 | $item = new CCol($guest.'/'.$host,'normal'); 29 | } 30 | return $item; 31 | } 32 | 33 | function check_script($form_name, $scriptname){ 34 | if($form_name == "ipmi_control"){ 35 | if(preg_match("/IPMI power/",$scriptname)){ 36 | return true; 37 | } 38 | }else if($form_name == "poweron_vms"){ 39 | if(preg_match("/(Stop|Reboot|Suspend) vSphere instance/", $scriptname)){ 40 | return true; 41 | } 42 | }else if($form_name == "poweroff_vms"){ 43 | if(preg_match("/Start vSphere instance/", $scriptname)){ 44 | return true; 45 | } 46 | }else if($form_name == "suspend_vms"){ 47 | if(preg_match("/vSphere instance/", $scriptname)){ 48 | return true; 49 | } 50 | }else if($form_name == "other_vms"){ 51 | if(preg_match("/vSphere instance/", $scriptname)){ 52 | return true; 53 | } 54 | }else if($form_name == "ec2_poweron"){ 55 | if(preg_match("/(Stop|Reboot) EC2 instance/", $scriptname)){ 56 | return true; 57 | } 58 | }else if($form_name == "ec2_poweroff"){ 59 | if(preg_match("/Start EC2 instance/", $scriptname)){ 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | function make_operation_box_footer($hostids, $form_name){ 67 | $box = new CComboBox("script"); 68 | $hostScripts = API::Script()->getScriptsByHosts($hostids); 69 | $scripts = array(); 70 | foreach($hostScripts as $hostid => $hscripts){ 71 | foreach($hscripts as $script){ 72 | if(!in_array($script, $scripts) && check_script($form_name, $script['name'])){ 73 | array_push($scripts, $script); 74 | } 75 | } 76 | } 77 | foreach($scripts as $script){ 78 | $option = new CComboItem($script['scriptid'], $script['name']); 79 | $box->addItem($option); 80 | } 81 | $button = new CButton('execute', _('Execute'), "return executeScriptOnMultipleHosts('${form_name}', 'script', 'hostids[]', 'Execute');"); 82 | $button->setAttribute('id', 'executeButton'); 83 | return array($box, $button); 84 | } 85 | 86 | function get_item_level($state){ 87 | switch($state){ 88 | case 'running': 89 | return 'normal'; 90 | case 'rebooting': 91 | case 'pending': 92 | case 'terminated': 93 | return 'warning'; 94 | case 'stopped': 95 | return null; 96 | case 'stuck': 97 | return 'average'; 98 | case 'loading...': 99 | return null; 100 | default: 101 | return 'unknown'; 102 | } 103 | } 104 | 105 | // for EC2 106 | 107 | function make_ec2_table($form_name, $instances){ 108 | $form = new CForm(); 109 | $form->setName($form_name); 110 | $form->setAttribute('id', $form_name); 111 | $form->addVar('driver', 'ec2'); 112 | $form->setAction('#'); 113 | 114 | // Create Table Header 115 | $table = new CTableInfo(); 116 | if($form_name == "ec2_poweron"){ 117 | $table->setHeader(array( 118 | is_show_all_nodes() ? _('Node') : null, 119 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."', 'all_hosts', '');"), 120 | _('Instance name'), 121 | _('Status'), 122 | _('Interface'), 123 | _('Availability zone'), 124 | _('SSH Connect'), 125 | )); 126 | }else{ 127 | $table->setHeader(array( 128 | is_show_all_nodes() ? _('Node') : null, 129 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."', 'all_hosts', '');"), 130 | _('Instance name'), 131 | _('Status'), 132 | _('Interface'), 133 | _('Availability zone'), 134 | )); 135 | } 136 | // Create Row 137 | foreach($instances as $instance){ 138 | $json = new CJSON(); 139 | $r = new CRow(); 140 | $r->addItem(new CCheckBox("hostids[]", null, null, $instance['hostid'])); 141 | $instance_name = new CSpan($instance["name"], 'pointer'); 142 | $instance_name->setHint(make_ec2_detail_table($instance)); 143 | $r->addItem(new CCol($instance_name)); 144 | $r->addItem(new CCol($instance["state"], get_item_level($instance["state"]))); 145 | $r->addItem($instance["public_dns"]); 146 | $r->addItem($instance["realm_id"]); 147 | if($form_name == "ec2_poweron"){ 148 | $ssh_link = new CLink(_('connect'), "gateone.php?hostid=$instance[hostid]"); 149 | $ssh_link->setTarget("_blank"); 150 | $r->addItem($ssh_link); 151 | } 152 | $table->addRow($r); 153 | } 154 | 155 | $hostids = array_map(function($instance){return $instance['hostid'];}, $instances); 156 | $footer = get_table_header(make_operation_box_footer($hostids, $form_name)); 157 | $form->addItem(array('', $table, '', $footer)); 158 | return $form; 159 | } 160 | 161 | function make_ec2_detail_table($instance){ 162 | $table = new CTableInfo(); 163 | $table->setHeader(array( 164 | _('Key'), 165 | _('Value'), 166 | )); 167 | foreach($instance as $key => $value){ 168 | if(!is_null($data = filtering_detail_info($key,$value))){ 169 | $r = new CRow(); 170 | $r->addItem($data["key"]); 171 | $r->addItem($data["value"]); 172 | $table->addRow($r); 173 | } 174 | } 175 | return $table; 176 | } 177 | 178 | function filtering_detail_info($key,$value){ 179 | $translate_table = array( 180 | "hostid" => _('Host ID'), 181 | "owner_hostid" => _('AWS account'), 182 | "instance_name" => _('Instance name'), 183 | "type" => _('Instance type'), 184 | "realm_id" => _('Availability zone'), 185 | "ami_id" => _('AMI ID'), 186 | "ami_name" => _('AMI name'), 187 | "private_dns" => _('Private DNS'), 188 | "launch_time" => _('Launch time'), 189 | "ramdisk_id" => _('Ramdisk ID'), 190 | "kernel_id" => _('Kernel ID'), 191 | "firewalls" => _('Security groups'), 192 | "instance_id" => _('Instance ID'), 193 | "elastic_ips" => _('Elastic IP'), 194 | "private_ips" => _('Private IP'), 195 | "platform" => _('Platform') 196 | ); 197 | if(isset($translate_table[$key])){ 198 | if(in_array($key, array("hostid", "owner_hostid"))){ 199 | $value = new CLink($value, "latest.php?hostid={$value}"); 200 | } 201 | return array("key" => $translate_table[$key], "value" => $value); 202 | }else{ 203 | return null; 204 | } 205 | } 206 | 207 | // for vSphere 208 | 209 | function make_vm_table($type, $instances){ 210 | $form_name = "{$type}_vms"; 211 | 212 | // Create Form 213 | $form = new CForm(); 214 | $form->setName($form_name); 215 | $form->setAttribute('id', $form_name); 216 | $form->addVar('driver', 'vsphere'); 217 | $form->setAction('#'); 218 | 219 | // Create Table Header 220 | $table = new CTableInfo(); 221 | switch($type){ 222 | case 'poweron': 223 | $table->setHeader(array( 224 | is_show_all_nodes() ? _('Node') : null, 225 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."','all_hosts','');"), 226 | _('Instance name'), 227 | _('Status'), 228 | _('CPU threads'), 229 | _('Memory(GB)'), 230 | _('Interface'), 231 | _('SSH Connect'), 232 | )); 233 | break; 234 | case 'poweroff': 235 | case 'suspend': 236 | $table->setHeader(array( 237 | is_show_all_nodes() ? _('Node') : null, 238 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."','all_hosts','');"), 239 | _('Instance name'), 240 | _('Status'), 241 | _('CPU threads'), 242 | _('Memory(GB)'), 243 | _('Interface'), 244 | )); 245 | break; 246 | case 'other': 247 | $table->setHeader(array( 248 | is_show_all_nodes() ? _('Node') : null, 249 | _('Instance name'), 250 | _('Status'), 251 | _('CPU thread'), 252 | _('Memory(GB)'), 253 | _('Interface'), 254 | _('Question'), 255 | )); 256 | break; 257 | default: 258 | return null; 259 | } 260 | 261 | // Create Row 262 | foreach($instances as $instance){ 263 | $table->addRow(make_hint_row($type, $instance)); 264 | } 265 | 266 | $hostids = array_map(function($instance){return $instance['hostid'];}, $instances); 267 | $footer = get_table_header(make_operation_box_footer($hostids, $form_name)); 268 | $form->addItem(array('', $table, '', $footer)); 269 | return $form; 270 | } 271 | 272 | function make_hint_row($type, $instance){ 273 | $state = $instance['state']; 274 | if($type == "other" && $instance["stuck_state"] == 1) $state = "stuck"; 275 | $r = new CRow(); 276 | if(in_array($type, array("poweron", "poweroff", "suspend"))){ 277 | $r->addItem(new CCheckBox("hostids[]", null, null, $instance['hostid'])); 278 | } 279 | $hostScripts = API::Script()->getScriptsByHosts(zbx_objectValues(array($instance['host']), 'hostid')); 280 | $hostSpan = new CSpan(nbsp($instance['name']), 'link_menu menu-host'); 281 | $hostSpan->setAttribute('data-menu', hostMenuData($instance['host'], $hostScripts[$instance['hostid']])); 282 | $r->addItem($hostSpan); 283 | $r->addItem(new CCol($state, get_item_level($state))); 284 | $r->addItem($instance['cpu']); 285 | $r->addItem($instance['memory']); 286 | $r->addItem($instance['main_interface']); 287 | if($type == "other" && $instance["stuck_state"] == 1){ 288 | $question = $instance['stuck_question']; 289 | $json = new CJSON(); 290 | $choiceinfos = $json->decode($instance['stuck_choices'], true); 291 | $answer_form = new CForm(); 292 | $answer_form->setAction('#'); 293 | $answer_form->setAttribute('id', "answer"); 294 | $answer_form->addVar("driver", "vsphere"); 295 | $answer_form->addVar("hostids[]", $instance['hostid']); 296 | $instancename = $instance['host']['host']; 297 | $answer_button = new CButton('answer', _('Answer'), "return checkAnswer('answer', 'choice', '${instancename}', 'Execute');"); 298 | $choice_table = new CTableInfo(); 299 | foreach($choiceinfos as $choice){ 300 | $radio = new CRadioButton('choice', $choice['key']); 301 | $label = new CLabel($choice['label']); 302 | $choice_table->addRow(new CRow(array($radio, $label))); 303 | } 304 | $answer_form->addItem($choice_table); 305 | $answer_form->addItem($answer_button); 306 | $question_span = new CSpan($question); 307 | $question_span->setHint($answer_form); 308 | $question_col = new CCol($question_span, 'warning'); 309 | $r->addItem($question_col); 310 | }else if($type == "poweron"){ 311 | $ssh_link = new CLink(_('connect'), "gateone.php?hostid=$instance[hostid]"); 312 | $ssh_link->setTarget("_blank"); 313 | $r->addItem($ssh_link); 314 | } 315 | return $r; 316 | } 317 | 318 | function make_datastore_table($hostid, $datastores){ 319 | $table = new CTableInfo(); 320 | $table->setHeader(array( 321 | _('Datastore Name'), 322 | _('Free Space'), 323 | _('Capacity'), 324 | )); 325 | foreach($datastores as $datastore){ 326 | $name_col = new CCol($datastore["name"]); 327 | $freeSpace = convert_units($datastore["freeSpace"], "B"); 328 | $capacity = convert_units($datastore["capacity"], "B"); 329 | $free_space_col = new CCol($freeSpace); 330 | $capacity_col = new CCol($capacity); 331 | $r = new CRow(array($name_col, $free_space_col, $capacity_col)); 332 | $table->addRow($r); 333 | } 334 | return $table; 335 | } 336 | 337 | // for IPMI 338 | 339 | function get_triggers($hostid){ 340 | $options = array( 341 | 'nodeids' => get_current_nodeid(), 342 | 'hostids' => $hostid, 343 | 'monitored' => 1, 344 | 'expandData' => 1, 345 | 'expandDescription' => 1, 346 | 'expandExpression' => 1, 347 | 'filter' => array( 348 | 'value' => TRIGGER_VALUE_TRUE 349 | ), 350 | 'output' => API_OUTPUT_EXTEND, 351 | ); 352 | return API::Trigger()->get($options); 353 | } 354 | 355 | function make_trigger_table($triggers, $host_name){ 356 | $table = new CTableInfo(); 357 | $table->setHeader(array( 358 | _('Host'), 359 | _('Problem'), 360 | _('Age') 361 | )); 362 | foreach($triggers as $trigger){ 363 | $description = $trigger["description"]; 364 | $r = new CRow(); 365 | $r->addItem($host_name); 366 | $r->addItem(new CCol($description, getSeverityStyle($trigger['priority']))); 367 | $r->addItem(zbx_date2age($trigger['lastchange'])); 368 | $table->addRow($r); 369 | } 370 | return $table; 371 | } 372 | 373 | ?> 374 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.0/custom/monitoring.dashboard.js.php: -------------------------------------------------------------------------------- 1 | 23 | 103 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.0/dashboard_scripts_exec.php: -------------------------------------------------------------------------------- 1 | array(T_ZBX_STR, O_OPT, P_SYS, DB_ID, 'isset({execute})'), 35 | 'hostname' => array(T_ZBX_STR, O_OPT, P_SYS, DB_ID, 'isset({answer})'), 36 | 'scriptid' => array(T_ZBX_INT, O_OPT, P_SYS, DB_ID, 'isset({execute})'), 37 | 'choiceid' => array(T_ZBX_STR, O_OPT, P_SYS, DB_ID, 'isset({answer})'), 38 | 'execute' => array(T_ZBX_INT, O_OPT, P_ACT, IN('0,1'), null), 39 | 'answer' => array(T_ZBX_INT, O_OPT, P_ACT, IN('0,1'), null) 40 | ); 41 | check_fields($fields); 42 | 43 | if (isset($_REQUEST['execute'])) { 44 | $json = new CJSON(); 45 | $scriptid = get_request('scriptid'); 46 | $hostids = $json->decode(get_request('hostids'), true); 47 | $hostids = $json->decode($hostids, true); 48 | 49 | $data = array( 50 | 'message' => '', 51 | 'info' => DBfetch(DBselect('SELECT s.name FROM scripts s WHERE s.scriptid='.$scriptid)) 52 | ); 53 | 54 | $results = array(); 55 | foreach ($hostids as $hostid) { 56 | $results[] = API::Script()->execute(array('hostid' => $hostid, 'scriptid' => $scriptid)); 57 | } 58 | 59 | $isErrorExist = false; 60 | foreach ($results as $result) { 61 | if (empty($result)) { 62 | $isErrorExist = true; 63 | } 64 | elseif ($result['response'] == 'failed') { 65 | error($result['value']); 66 | $isErrorExist = true; 67 | } 68 | else { 69 | $data['message'] .= convert_json_message($result['value'])."\n"; 70 | } 71 | } 72 | 73 | if ($isErrorExist) { 74 | show_error_message(_('Cannot connect to the trapper port of zabbix server daemon, but it should be available to run the script.')); 75 | } 76 | 77 | // render view 78 | $scriptView = new CView('general.script.execute', $data); 79 | $scriptView->render(); 80 | $scriptView->show(); 81 | }elseif (isset($_REQUEST['answer'])) { 82 | $hostname = get_request('hostname'); 83 | $choiceid = get_request('choiceid'); 84 | $script = '/opt/hyclops/globalscripts/request_action.py vsphere '.$hostname.' \'{"command": "answer", "choiceid":"'.$choiceid.'"}\' 2>&1'; 85 | $result = array(); 86 | $ret = null; 87 | exec($script, $result, $ret); 88 | $message = ''; 89 | if($ret == 0){ 90 | $message = convert_json_message($result[0]); 91 | }else{ 92 | $message = $result; 93 | } 94 | $data = array( 95 | 'message' => $message, 96 | 'info' => array("name" => 'Answer the question') 97 | ); 98 | $scriptView = new CView('general.script.execute', $data); 99 | $scriptView->render(); 100 | $scriptView->show(); 101 | } 102 | 103 | function convert_json_message($original_msg){ 104 | $message = ''; 105 | $tmp_msg = str_replace('\'', '"', $original_msg); 106 | $tmp_msg = str_replace('u"', '"', $tmp_msg); 107 | $tmp_msg = json_decode($tmp_msg, true); 108 | if($tmp_msg['result']){ 109 | $message = "Result: "; 110 | $message .= $tmp_msg['result']; 111 | $message .= "\nMessage: "; 112 | $message .= $tmp_msg['message']; 113 | $message .= "\n\n* Please wait a few minutes until the result is reflected in Dashboard."; 114 | }else{ 115 | $message = $original_msg; 116 | } 117 | return $message; 118 | } 119 | 120 | require_once dirname(__FILE__).'/include/page_footer.php'; 121 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.0/gateone.php: -------------------------------------------------------------------------------- 1 | get(array( 27 | "hostids" => $hostid, 28 | "selectInterfaces" => "extend" 29 | )); 30 | $ssh_con_url = ""; 31 | if(isset($hosts[0]["interfaces"])){ 32 | $ssh_con_url =get_ssh_interface($hosts[0]); 33 | } 34 | $user = CWebUser::$data['alias']; 35 | $auth = get_authobj($user); 36 | 37 | function get_ssh_interface($host){ 38 | foreach( $host["interfaces"] as $interface ){ 39 | if($interface["main"] == 1){ 40 | $ssh_interface = $interface["useip"] == 0 ? $interface["dns"] : $interface["ip"]; 41 | if($ssh_interface == 'dummy-interface.invalid'){ 42 | continue; 43 | }else{ 44 | return $ssh_interface; 45 | } 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | function get_global_macro($name){ 52 | $macros = API::UserMacro()->get(array( 53 | 'output' => API_OUTPUT_EXTEND, 54 | 'globalmacro' => true 55 | )); 56 | $macros = zbx_toHash($macros,'macro'); 57 | return $macros[$name]['value']; 58 | } 59 | 60 | function get_authobj($upn){ 61 | $api_key = get_global_macro('{$GATEONE_KEY}'); 62 | $secret = get_global_macro('{$GATEONE_SECRET}'); 63 | $authobj = array( 64 | 'api_key' => $api_key, 65 | 'upn' => $upn, 66 | 'timestamp' => (string)ceil(microtime(true)*1000), 67 | 'signature_method' => 'HMAC-SHA1', 68 | 'api_version' => '1.0' 69 | ); 70 | $authobj['signature'] = hash_hmac('sha1', $api_key.$upn.$authobj['timestamp'], $secret); 71 | return json_encode($authobj); 72 | } 73 | 74 | print<< 76 | 77 | 78 | 111 | 112 | 113 | 114 |
115 |
116 |
117 | 118 | 119 | EOF; 120 | ?> 121 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.0/include/menu.inc.php: -------------------------------------------------------------------------------- 1 | array( 26 | 'label' => _('Monitoring'), 27 | 'user_type' => USER_TYPE_ZABBIX_USER, 28 | 'node_perm' => PERM_READ_LIST, 29 | 'default_page_id' => 0, 30 | 'pages' => array( 31 | array( 32 | 'url' => 'dashboard.php', 33 | 'label' => _('Dashboard'), 34 | 'sub_pages' => array('dashconf.php') 35 | ), 36 | array( 37 | 'url' => 'overview.php', 38 | 'label' => _('Overview') 39 | ), 40 | array( 41 | 'url' => 'httpmon.php', 42 | 'label' => _('Web'), 43 | 'sub_pages' => array('httpdetails.php') 44 | ), 45 | array( 46 | 'url' => 'latest.php', 47 | 'label' => _('Latest data'), 48 | 'sub_pages' => array('history.php', 'chart.php') 49 | ), 50 | array( 51 | 'url' => 'tr_status.php', 52 | 'label' => _('Triggers'), 53 | 'sub_pages' => array('acknow.php', 'tr_comments.php', 'chart4.php', 'scripts_exec.php', 'dashboard_scripts_exec.php') 54 | ), 55 | array( 56 | 'url' => 'events.php', 57 | 'label' => _('Events'), 58 | 'sub_pages' => array('tr_events.php') 59 | ), 60 | array( 61 | 'url' => 'charts.php', 62 | 'label' => _('Graphs'), 63 | 'sub_pages' => array('chart2.php', 'chart3.php', 'chart6.php', 'chart7.php') 64 | ), 65 | array( 66 | 'url' => 'screens.php', 67 | 'label' => _('Screens'), 68 | 'sub_pages' => array('slides.php') 69 | ), 70 | array( 71 | 'url' => 'maps.php', 72 | 'label' => _('Maps'), 73 | 'sub_pages' => array('map.php') 74 | ), 75 | array( 76 | 'url' => 'discovery.php', 77 | 'label' => _('Discovery'), 78 | 'user_type' => USER_TYPE_ZABBIX_ADMIN 79 | ), 80 | array( 81 | 'url' => 'srv_status.php', 82 | 'label' => _('IT services'), 83 | 'force_disable_all_nodes' => true, 84 | 'sub_pages' => array('report3.php', 'chart5.php') 85 | ), 86 | array( 87 | 'url' => 'chart3.php' 88 | ), 89 | array( 90 | 'url' => 'imgstore.php' 91 | ), 92 | array( 93 | 'url' => 'search.php' 94 | ), 95 | array( 96 | 'url' => 'jsrpc.php' 97 | ) 98 | ) 99 | ), 100 | 'cm' => array( 101 | 'label' => _('Inventory'), 102 | 'user_type' => USER_TYPE_ZABBIX_USER, 103 | 'node_perm' => PERM_READ_LIST, 104 | 'default_page_id' => 0, 105 | 'pages' => array( 106 | array( 107 | 'url' => 'hostinventoriesoverview.php', 108 | 'label' => _('Overview') 109 | ), 110 | array( 111 | 'url' => 'hostinventories.php', 112 | 'label' => _('Hosts') 113 | ) 114 | ) 115 | ), 116 | 'reports' => array( 117 | 'label' => _('Reports'), 118 | 'user_type' => USER_TYPE_ZABBIX_USER, 119 | 'node_perm' => PERM_READ_LIST, 120 | 'default_page_id' => 0, 121 | 'pages' => array( 122 | array( 123 | 'url' => 'report1.php', 124 | 'label' => _('Status of Zabbix'), 125 | 'user_type' => USER_TYPE_SUPER_ADMIN 126 | ), 127 | array( 128 | 'url' => 'report2.php', 129 | 'label' => _('Availability report') 130 | ), 131 | array( 132 | 'url' => 'report5.php', 133 | 'label' => _('Triggers top 100') 134 | ), 135 | array( 136 | 'url' => 'report6.php', 137 | 'label' => _('Bar reports'), 138 | 'sub_pages' => array('popup_period.php', 'popup_bitem.php', 'chart_bar.php') 139 | ), 140 | array( 141 | 'url' => 'popup.php' 142 | ), 143 | array( 144 | 'url' => 'popup_right.php' 145 | ) 146 | ), 147 | ), 148 | 'config' => array( 149 | 'label' => _('Configuration'), 150 | 'user_type' => USER_TYPE_ZABBIX_ADMIN, 151 | 'node_perm' => PERM_READ_LIST, 152 | 'default_page_id' => 0, 153 | 'force_disable_all_nodes' => true, 154 | 'pages' => array( 155 | array( 156 | 'url' => 'conf.import.php' 157 | ), 158 | array( 159 | 'url' => 'hostgroups.php', 160 | 'label' => _('Host groups') 161 | ), 162 | array( 163 | 'url' => 'templates.php', 164 | 'label' => _('Templates') 165 | ), 166 | array( 167 | 'url' => 'hosts.php', 168 | 'label' => _('Hosts'), 169 | 'sub_pages' => array( 170 | 'items.php', 171 | 'triggers.php', 172 | 'graphs.php', 173 | 'applications.php', 174 | 'tr_logform.php', 175 | 'tr_testexpr.php', 176 | 'popup_trexpr.php', 177 | 'host_discovery.php', 178 | 'disc_prototypes.php', 179 | 'trigger_prototypes.php' 180 | ) 181 | ), 182 | array( 183 | 'url' => 'maintenance.php', 184 | 'label' => _('Maintenance') 185 | ), 186 | array( 187 | 'url' => 'httpconf.php', 188 | 'label' => _('Web'), 189 | 'sub_pages' => array('popup_httpstep.php') 190 | ), 191 | array( 192 | 'url' => 'actionconf.php', 193 | 'label' => _('Actions') 194 | ), 195 | array( 196 | 'url' => 'screenconf.php', 197 | 'label' => _('Screens'), 198 | 'sub_pages' => array('screenedit.php') 199 | ), 200 | array( 201 | 'url' => 'slideconf.php', 202 | 'label' => _('Slide shows'), 203 | ), 204 | array( 205 | 'url' => 'sysmaps.php', 206 | 'label' => _('Maps'), 207 | 'sub_pages' => array('image.php', 'sysmap.php') 208 | ), 209 | array( 210 | 'url' => 'discoveryconf.php', 211 | 'label' => _('Discovery') 212 | ), 213 | array( 214 | 'url' => 'services.php', 215 | 'label' => _('IT services') 216 | ), 217 | ) 218 | ), 219 | 'admin' => array( 220 | 'label' => _('Administration'), 221 | 'user_type' => USER_TYPE_SUPER_ADMIN, 222 | 'node_perm' => PERM_READ_WRITE, 223 | 'default_page_id' => 1, 224 | 'force_disable_all_nodes'=> true, 225 | 'pages' => array( 226 | array( 227 | 'url' => 'adm.gui.php', 228 | 'label' => _('General'), 229 | 'sub_pages' => array( 230 | 'adm.housekeeper.php', 231 | 'adm.images.php', 232 | 'adm.iconmapping.php', 233 | 'adm.regexps.php', 234 | 'adm.macros.php', 235 | 'adm.valuemapping.php', 236 | 'adm.workingtime.php', 237 | 'adm.triggerseverities.php', 238 | 'adm.triggerdisplayoptions.php', 239 | 'adm.other.php' 240 | ) 241 | ), 242 | array( 243 | 'url' => 'proxies.php', 244 | 'label' => _('DM'), 245 | 'sub_pages' => array('nodes.php') 246 | ), 247 | array( 248 | 'url' => 'authentication.php', 249 | 'label' => _('Authentication') 250 | ), 251 | array( 252 | 'url' => 'usergrps.php', 253 | 'label' => _('Users'), 254 | 'sub_pages' => array('users.php', 'popup_usrgrp.php') 255 | ), 256 | array( 257 | 'url' => 'media_types.php', 258 | 'label' => _('Media types') 259 | ), 260 | array( 261 | 'url' => 'scripts.php', 262 | 'label' => _('Scripts') 263 | ), 264 | array( 265 | 'url' => 'auditlogs.php', 266 | 'label' => _('Audit'), 267 | 'sub_pages' => array('auditacts.php') 268 | ), 269 | array( 270 | 'url' => 'queue.php', 271 | 'label' => _('Queue') 272 | ), 273 | array( 274 | 'url' => 'report4.php', 275 | 'label' => _('Notifications') 276 | ), 277 | array( 278 | 'url' => 'setup.php', 279 | 'label' => _('Installation'), 280 | 'sub_pages' => array('warning.php') 281 | ) 282 | ) 283 | ), 284 | 'login' => array( 285 | 'label' => _('Login'), 286 | 'user_type' => 0, 287 | 'default_page_id' => 0, 288 | 'hide_node_selection' => 1, 289 | 'force_disable_all_nodes'=> true, 290 | 'pages' => array( 291 | array( 292 | 'url' => 'index.php', 293 | 'sub_pages' => array('profile.php', 'popup_media.php') 294 | ) 295 | ) 296 | ) 297 | ); 298 | 299 | /** 300 | * NOTE - menu array format: 301 | * first level: 302 | * 'label' = main menu title. 303 | * 'default_page_id = default page url from 'pages' then opened menu. 304 | * 'pages' = collection of pages which are displayed from this menu. 305 | * these pages are saved a last visited submenu of main menu. 306 | * 307 | * second level (pages): 308 | * 'url' = real url for this page 309 | * 'label' = submenu title, if missing, menu skipped, but remembered as last visited page. 310 | * 'sub_pages' = collection of pages for displaying but not remembered as last visited. 311 | */ 312 | function zbx_construct_menu(&$main_menu, &$sub_menus, &$page) { 313 | global $ZBX_MENU, $USER_DETAILS; 314 | 315 | $denied_page_requested = false; 316 | $page_exists = false; 317 | $deny = !defined('ZBX_PAGE_NO_AUTHORIZATION'); 318 | 319 | foreach ($ZBX_MENU as $label => $menu) { 320 | $show_menu = true; 321 | 322 | if (isset($menu['user_type'])) { 323 | $show_menu &= ($menu['user_type'] <= $USER_DETAILS['type']); 324 | } 325 | if ($label == 'login') { 326 | $show_menu = false; 327 | } 328 | 329 | $menu_class = 'horizontal_menu_n'; 330 | $sub_menus[$label] = array(); 331 | foreach ($menu['pages'] as $sub_page) { 332 | $show_sub_menu = true; 333 | 334 | // show check 335 | if (!isset($sub_page['label'])) { 336 | $show_sub_menu = false; 337 | } 338 | if (!isset($sub_page['user_type'])) { 339 | $sub_page['user_type'] = $menu['user_type']; 340 | } 341 | if ($USER_DETAILS['type'] < $sub_page['user_type']) { 342 | $show_sub_menu = false; 343 | } 344 | 345 | $row = array( 346 | 'menu_text' => isset($sub_page['label']) ? $sub_page['label'] : '', 347 | 'menu_url' => $sub_page['url'], 348 | 'class' => 'highlight', 349 | 'selected' => false 350 | ); 351 | $sub_menu_active = ($page['file'] == $sub_page['url']); 352 | $sub_menu_active |= (isset($sub_page['sub_pages']) && str_in_array($page['file'], $sub_page['sub_pages'])); 353 | 354 | if ($sub_menu_active) { 355 | // permition check 356 | $deny &= ($USER_DETAILS['type'] < $menu['user_type'] || $USER_DETAILS['type'] < $sub_page['user_type']); 357 | 358 | $menu_class = 'active'; 359 | $page_exists = true; 360 | $page['menu'] = $label; 361 | $row['selected'] = true; 362 | 363 | if (!defined('ZBX_PAGE_NO_MENU')) { 364 | CProfile::update('web.menu.'.$label.'.last', $sub_page['url'], PROFILE_TYPE_STR); 365 | } 366 | } 367 | 368 | if ($show_sub_menu) { 369 | $sub_menus[$label][] = $row; 370 | } 371 | } 372 | 373 | if ($page_exists && !defined('ZBX_NOT_ALLOW_ALL_NODES') && (isset($menu['force_disable_all_nodes']) || isset($sub_page['force_disable_all_nodes']))) { 374 | define('ZBX_NOT_ALLOW_ALL_NODES', 1); 375 | } 376 | 377 | if ($page_exists && $deny) { 378 | $denied_page_requested = true; 379 | } 380 | 381 | if (!$show_menu) { 382 | unset($sub_menus[$label]); 383 | continue; 384 | } 385 | 386 | $menu_url = $sub_menus[$label][$menu['default_page_id']]['menu_url']; 387 | $mmenu_entry = new CCol($menu['label'], $menu_class); 388 | $mmenu_entry->setAttribute('id', $label); 389 | $mmenu_entry->addAction('onclick', 'javascript: redirect(\''.$menu_url.'\');'); 390 | $mmenu_entry->addAction('onmouseover', 'javascript: MMenu.mouseOver(\''.$label.'\');'); 391 | $mmenu_entry->addAction('onmouseout', 'javascript: MMenu.mouseOut();'); 392 | array_push($main_menu, $mmenu_entry); 393 | } 394 | 395 | if (!$page_exists && $page['type'] != PAGE_TYPE_XML && $page['type'] != PAGE_TYPE_CSV && $page['type'] != PAGE_TYPE_TEXT_FILE) { 396 | $denied_page_requested = true; 397 | } 398 | 399 | return $denied_page_requested; 400 | } 401 | 402 | function zbx_define_menu_restrictions($page, $ZBX_MENU) { 403 | foreach ($ZBX_MENU as $section) { 404 | foreach ($section['pages'] as $pid => $menu_page) { 405 | if ($menu_page['url'] == $page['file'] || (isset($menu_page['sub_pages']) && str_in_array($page['file'], $menu_page['sub_pages']))) { 406 | if (isset($section['force_disable_all_nodes']) && !defined('ZBX_NOT_ALLOW_ALL_NODES')) { 407 | define('ZBX_NOT_ALLOW_ALL_NODES', 1); 408 | } 409 | if (isset($section['hide_node_selection']) && !defined('ZBX_HIDE_NODE_SELECTION')) { 410 | define('ZBX_HIDE_NODE_SELECTION', 1); 411 | } 412 | return null; 413 | } 414 | } 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.2/custom/additional_blocks.inc.php: -------------------------------------------------------------------------------- 1 | setName('ipmi_control'); 30 | $ipmicontrol_form->setAttribute('id','ipmi_control'); 31 | $ipmicontrol_form->addVar('driver', 'ipmi'); 32 | $script_itemkey = 'push_message.py[{$HYCLOPS_SERVER},{$HYCLOPS_PORT},ipmi,{HOST.HOST}]'; 33 | 34 | $table->setHeader(array( 35 | is_show_all_nodes() ? S_NODE : null, 36 | new CCheckBox('all_physical_hosts', null, "checkAllHosts('".$ipmicontrol_form->getName()."', 'all_physical_hosts', '');"), 37 | _('Host name'), 38 | _('IPMI interface'), 39 | _('Status'), 40 | _('Problems'), 41 | )); 42 | 43 | $hosts = get_ipmi_hosts(); 44 | if(empty($hosts)){ 45 | return null; 46 | } 47 | 48 | foreach( $hosts as $host ){ 49 | $triggers = get_triggers($host['hostid']); 50 | $trigger_count = count($triggers); 51 | 52 | // get State 53 | if(!$preloading){ 54 | if(is_script_success($host['hostid'], $script_itemkey)){ 55 | $state = strtolower(get_item_value($host['hostid'], "ipmi.state.text")); 56 | }else{ 57 | $state = _('script failed'); 58 | } 59 | }else{ 60 | $state = _('loading...'); 61 | } 62 | 63 | // Check box 64 | $checkbox_col = new CCheckBox("hostids[]",null,null,$host['hostid']); 65 | if(!is_operation_available("ipmi", $state)){ 66 | $checkbox_col->attributes['disabled'] = 'disabled'; 67 | } 68 | 69 | // Host name 70 | if($trigger_count == 0){ 71 | $hostname_col = new CCol(new CSpan($host['host'])); 72 | }else{ 73 | $hostname_col = new CLink(new CSpan($host['host']), 'tr_status.php?&hostid='.$host['hostid'].'&show_triggers='.TRIGGERS_OPTION_ONLYTRUE); 74 | } 75 | 76 | // IPMI Interfaces 77 | $ipmi_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_IPMI); 78 | $ipmi_ip_col = new CCol(reset($ipmi_addresses)); 79 | 80 | // Status 81 | $state_col = new CCol($state, get_item_level($state)); 82 | 83 | // Problem 84 | if($trigger_count == 0){ 85 | $problem_col = new CCol($trigger_count,'normal'); 86 | }else{ 87 | $problem_col = new CCol($trigger_count,'high'); 88 | $problem_col->setHint(make_trigger_table($triggers,$hostname_col)); 89 | } 90 | 91 | $r = new CRow(); 92 | $r->addItem($checkbox_col); 93 | $r->addItem($hostname_col); 94 | $r->addItem($ipmi_ip_col); 95 | $r->addItem($state_col); 96 | $r->addItem($problem_col); 97 | $table->addRow($r); 98 | } 99 | 100 | $table->setFooter(new CCol(make_operation_box_footer(zbx_objectValues($hosts, 'hostid'), $form_name))); 101 | $ipmicontrol_form->addItem($table); 102 | $script = new CJSScript(get_js("jQuery('#hat_ipmistat_footer').html('"._s('Updated: %s', zbx_date2str(_('H:i:s')))."')")); 103 | return new CDiv(array($ipmicontrol_form, $script)); 104 | } 105 | 106 | function make_vspherestat_summary($preloading = false){ 107 | $table = new CTableInfo(); 108 | $table->setHeader(array( 109 | is_show_all_nodes() ? S_NODE : null, 110 | _('Host name'), 111 | _('Interface'), 112 | _('CPU threads [used/total]'), 113 | _('Memory(GB) [used/total]'), 114 | _('Datastores'), 115 | _('PoweredOn'), 116 | _('Suspended'), 117 | _('PoweredOff'), 118 | _('Others'), 119 | )); 120 | $hidden = new CDiv(null, "hidden"); 121 | $hidden->setAttribute("type", "hidden"); 122 | $script_itemkey = 'push_message.py[{$HYCLOPS_SERVER},{$HYCLOPS_PORT},vsphere,{HOST.HOST}]'; 123 | $hosts = get_vsphere_hosts(); 124 | if(empty($hosts)){ 125 | return null; 126 | } 127 | foreach( $hosts as $host ){ 128 | if(!$preloading){ 129 | if(is_script_success($host['hostid'], $script_itemkey)){ 130 | $hardware_profiles = get_hardware_profiles($host['hostid']); 131 | $datastores = get_datastores($host['hostid']); 132 | $instances = get_instances($host['hostid']); 133 | $instances = filter_instances($instances); 134 | 135 | $r = new CRow(); 136 | 137 | // Host name 138 | $col = new CCol($host['host'], 'vsphere'); 139 | $col->setAttribute("hostid", $host['hostid']); 140 | $r->addItem($col); 141 | 142 | // IP 143 | $snmp_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_SNMP); 144 | $r->addItem(reset($snmp_addresses)); 145 | 146 | // CPU thread 147 | $r->addItem(level_check($hardware_profiles['cpu_assigned'], $hardware_profiles['cpu'])); 148 | 149 | // Memory 150 | $r->addItem(level_check($hardware_profiles['memory_assigned'], $hardware_profiles['memory'])); 151 | 152 | // Datastores 153 | $datastores_count = new CSpan(count($datastores), 'pointer'); 154 | if(!empty($datastores)) $datastores_count->setHint(make_datastore_table($host['hostid'], $datastores)); 155 | $r->addItem(new CCol($datastores_count)); 156 | 157 | // PowerOn 158 | $poweron_vms = $instances["running"]; 159 | $poweron_count = new CSpan(count($poweron_vms), 'pointer'); 160 | if(!empty($poweron_vms)) $poweron_count->setHint(make_vm_table('poweron', $poweron_vms)); 161 | $r->addItem(new CCol($poweron_count)); 162 | 163 | // Suspended 164 | $suspend_vms = $instances["pending"]; 165 | $suspend_count = new CSpan(count($suspend_vms), 'pointer'); 166 | if(!empty($suspend_vms)) $suspend_count->setHint(make_vm_table('suspend', $suspend_vms)); 167 | $r->addItem(new CCol($suspend_count)); 168 | 169 | // PowerOff 170 | $poweroff_vms = $instances["stopped"]; 171 | $poweroff_count = new CSpan(count($poweroff_vms), 'pointer'); 172 | if(!empty($poweroff_vms)) $poweroff_count->setHint(make_vm_table('poweroff', $poweroff_vms)); 173 | $r->addItem(new CCol($poweroff_count)); 174 | 175 | // Other 176 | $other_vms = $instances["other"]; 177 | $other_count = new CSpan(count($other_vms), 'pointer'); 178 | if(!empty($other_vms)) $other_count->setHint(make_vm_table('other', $other_vms)); 179 | $other_state_col = new CCol($other_count, count($other_vms) > 0 ? 'high' : null); 180 | $r->addItem($other_state_col); 181 | 182 | $table->addRow($r); 183 | zbx_add_post_js('chkbxRange.pageGoName = "vms";'); 184 | }else{ 185 | $snmp_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_SNMP); 186 | $r = new CRow(); 187 | $r->addItem($host['host']); 188 | $r->addItem(reset($snmp_addresses)); 189 | $r->addItem(new CCol(_('script failed'), "high")); 190 | $r->addItem(array("-", "-", "-", "-", "-", "-")); 191 | $table->addRow($r); 192 | } 193 | }else{ 194 | // display loading message 195 | $snmp_addresses = get_addresses($host['hostid'], INTERFACE_TYPE_SNMP); 196 | $r = new CRow(); 197 | $r->addItem($host['host']); 198 | $r->addItem(reset($snmp_addresses)); 199 | $r->addItem(_('loading...')); 200 | $r->addItem(array("-", "-", "-", "-", "-", "-")); 201 | $table->addRow($r); 202 | } 203 | } 204 | 205 | #insert_js('init_vsphere_contextmenu()'); 206 | $script = new CJSScript(get_js("jQuery('#hat_vspherestat_footer').html('"._s('Updated: %s', zbx_date2str(_('H:i:s')))."')")); 207 | return new CDiv(array($table, $script)); 208 | } 209 | 210 | function make_awsstat_summary($preloading = false){ 211 | $table = new CTableInfo(); 212 | $table->setHeader(array( 213 | is_show_all_nodes() ? S_NODE : null, 214 | _('Account name'), 215 | _('PoweredOn'), 216 | _('PoweredOff'), 217 | _('Billing/Month'), 218 | )); 219 | $script_itemkey = 'push_message.py[{$HYCLOPS_SERVER},{$HYCLOPS_PORT},ec2,{HOST.HOST}]'; 220 | 221 | $aws_accounts = get_aws_accounts(); 222 | if(empty($aws_accounts)){ 223 | return null; 224 | } 225 | foreach( $aws_accounts as $host ){ 226 | if(!$preloading){ 227 | if(is_script_success($host['hostid'], $script_itemkey)){ 228 | $instances = get_instances($host['hostid']); 229 | $instances = filter_instances($instances); 230 | $r = new CRow(); 231 | 232 | // Account name 233 | $col = new CCol($host['host']); 234 | $r->addItem($col); 235 | 236 | // Poweron (running + pending) 237 | $poweron_vms = array_merge($instances["running"], $instances["pending"]); 238 | $poweron_count = new CSpan(count($poweron_vms), 'pointer'); 239 | if(!empty($poweron_vms)) $poweron_count->setHint(make_ec2_table('ec2_poweron', $poweron_vms)); 240 | $r->addItem(new CCol($poweron_count)); 241 | 242 | // Poweroff (stopped + terminated + stopping + shutting-down) 243 | $poweroff_vms = $instances["stopped"]; 244 | $poweroff_count = new CSpan(count($poweroff_vms), 'pointer'); 245 | if(!empty($poweroff_vms)) $poweroff_count->setHint(make_ec2_table('ec2_poweroff', $poweroff_vms)); 246 | $r->addItem(new CCol($poweroff_count)); 247 | 248 | // AWS Charges 249 | $item = get_item_by_key('get_aws_charges.py[{$KEY},{$SECRET}]', $host["host"]); 250 | $history = Manager::History()->getLast(array($item)); 251 | if(array_key_exists($item["itemid"], $history)){ 252 | if(count($history[$item["itemid"]]) > 0){ 253 | $r->addItem(new CLink($history[$item["itemid"]][0]["value"], "history.php?action=showgraph&itemid={$item["itemid"]}")); 254 | }else{ 255 | $r->addItem(new CCol(_('No data'))); 256 | } 257 | }else{ 258 | $r->addItem(new CCol(_('No data'))); 259 | } 260 | $table->addRow($r); 261 | zbx_add_post_js('chkbxRange.pageGoName = "vms";'); 262 | }else{ 263 | $r = new CRow(); 264 | $r->addItem($host['host']); 265 | $r->addItem(new CCol(_('script failed'), "high")); 266 | $r->addItem(array("-")); 267 | $table->addRow($r); 268 | } 269 | }else{ 270 | $r = new CRow(); 271 | $r->addItem($host['host']); 272 | $r->addItem(new CCol(_('loading...'))); 273 | $r->addItem(array("-")); 274 | $table->addRow($r); 275 | } 276 | } 277 | $script = new CJSScript(get_js("jQuery('#hat_awsstat_footer').html('"._s('Updated: %s', zbx_date2str(_('H:i:s')))."')")); 278 | return new CDiv(array($table, $script)); 279 | } 280 | 281 | ?> 282 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.2/custom/additional_blocks_func.inc.php: -------------------------------------------------------------------------------- 1 | $host*2){ 24 | $item = new CCol($guest.'/'.$host,'high'); 25 | }else if($guest > $host){ 26 | $item = new CCol($guest.'/'.$host,'warning'); 27 | }else{ 28 | $item = new CCol($guest.'/'.$host,'normal'); 29 | } 30 | return $item; 31 | } 32 | 33 | function check_script($form_name, $scriptname){ 34 | if($form_name == "ipmi_control"){ 35 | if(preg_match("/IPMI power/",$scriptname)){ 36 | return true; 37 | } 38 | }else if($form_name == "poweron_vms"){ 39 | if(preg_match("/(Stop|Reboot|Suspend) vSphere instance/", $scriptname)){ 40 | return true; 41 | } 42 | }else if($form_name == "poweroff_vms"){ 43 | if(preg_match("/Start vSphere instance/", $scriptname)){ 44 | return true; 45 | } 46 | }else if($form_name == "suspend_vms"){ 47 | if(preg_match("/vSphere instance/", $scriptname)){ 48 | return true; 49 | } 50 | }else if($form_name == "other_vms"){ 51 | if(preg_match("/vSphere instance/", $scriptname)){ 52 | return true; 53 | } 54 | }else if($form_name == "ec2_poweron"){ 55 | if(preg_match("/(Stop|Reboot) EC2 instance/", $scriptname)){ 56 | return true; 57 | } 58 | }else if($form_name == "ec2_poweroff"){ 59 | if(preg_match("/Start EC2 instance/", $scriptname)){ 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | function make_operation_box_footer($hostids, $form_name){ 67 | $box = new CComboBox("script"); 68 | $hostScripts = API::Script()->getScriptsByHosts($hostids); 69 | $scripts = array(); 70 | foreach($hostScripts as $hostid => $hscripts){ 71 | foreach($hscripts as $script){ 72 | if(!in_array($script, $scripts) && check_script($form_name, $script['name'])){ 73 | array_push($scripts, $script); 74 | } 75 | } 76 | } 77 | foreach($scripts as $script){ 78 | $option = new CComboItem($script['scriptid'], $script['name']); 79 | $box->addItem($option); 80 | } 81 | $button = new CButton('execute', _('Execute'), "return executeScriptOnMultipleHosts('${form_name}', 'script', 'hostids[]', 'Execute');"); 82 | $button->setAttribute('id', 'executeButton'); 83 | return array($box, $button); 84 | } 85 | 86 | function get_item_level($state){ 87 | switch($state){ 88 | case 'running': 89 | return 'normal'; 90 | case 'rebooting': 91 | case 'pending': 92 | case 'terminated': 93 | return 'warning'; 94 | case 'stopped': 95 | return null; 96 | case 'stuck': 97 | return 'average'; 98 | case 'loading...': 99 | return null; 100 | default: 101 | return 'unknown'; 102 | } 103 | } 104 | 105 | // for EC2 106 | 107 | function make_ec2_table($form_name, $instances){ 108 | $form = new CForm(); 109 | $form->setName($form_name); 110 | $form->setAttribute('id', $form_name); 111 | $form->addVar('driver', 'ec2'); 112 | $form->setAction('#'); 113 | 114 | // Create Table Header 115 | $table = new CTableInfo(); 116 | if($form_name == "ec2_poweron"){ 117 | $table->setHeader(array( 118 | is_show_all_nodes() ? _('Node') : null, 119 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."', 'all_hosts', '');"), 120 | _('Instance name'), 121 | _('Status'), 122 | _('Interface'), 123 | _('Availability zone'), 124 | _('SSH Connect'), 125 | )); 126 | }else{ 127 | $table->setHeader(array( 128 | is_show_all_nodes() ? _('Node') : null, 129 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."', 'all_hosts', '');"), 130 | _('Instance name'), 131 | _('Status'), 132 | _('Interface'), 133 | _('Availability zone'), 134 | )); 135 | } 136 | // Create Row 137 | foreach($instances as $instance){ 138 | $json = new CJSON(); 139 | $r = new CRow(); 140 | $r->addItem(new CCheckBox("hostids[]", null, null, $instance['hostid'])); 141 | $instance_name = new CSpan($instance["name"], 'pointer'); 142 | $instance_name->setHint(make_ec2_detail_table($instance)); 143 | $r->addItem(new CCol($instance_name)); 144 | $r->addItem(new CCol($instance["state"], get_item_level($instance["state"]))); 145 | $r->addItem($instance["public_dns"]); 146 | $r->addItem($instance["realm_id"]); 147 | if($form_name == "ec2_poweron"){ 148 | $ssh_link = new CLink(_('connect'), "gateone.php?hostid=$instance[hostid]"); 149 | $ssh_link->setTarget("_blank"); 150 | $r->addItem($ssh_link); 151 | } 152 | $table->addRow($r); 153 | } 154 | 155 | $hostids = array_map(function($instance){return $instance['hostid'];}, $instances); 156 | $footer = get_table_header(make_operation_box_footer($hostids, $form_name)); 157 | $form->addItem(array('', $table, '', $footer)); 158 | return $form; 159 | } 160 | 161 | function make_ec2_detail_table($instance){ 162 | $table = new CTableInfo(); 163 | $table->setHeader(array( 164 | _('Key'), 165 | _('Value'), 166 | )); 167 | foreach($instance as $key => $value){ 168 | if(!is_null($data = filtering_detail_info($key,$value))){ 169 | $r = new CRow(); 170 | $r->addItem($data["key"]); 171 | $r->addItem($data["value"]); 172 | $table->addRow($r); 173 | } 174 | } 175 | return $table; 176 | } 177 | 178 | function filtering_detail_info($key,$value){ 179 | $translate_table = array( 180 | "hostid" => _('Host ID'), 181 | "owner_hostid" => _('AWS account'), 182 | "instance_name" => _('Instance name'), 183 | "type" => _('Instance type'), 184 | "realm_id" => _('Availability zone'), 185 | "ami_id" => _('AMI ID'), 186 | "ami_name" => _('AMI name'), 187 | "private_dns" => _('Private DNS'), 188 | "launch_time" => _('Launch time'), 189 | "ramdisk_id" => _('Ramdisk ID'), 190 | "kernel_id" => _('Kernel ID'), 191 | "firewalls" => _('Security groups'), 192 | "instance_id" => _('Instance ID'), 193 | "elastic_ips" => _('Elastic IP'), 194 | "private_ips" => _('Private IP'), 195 | "platform" => _('Platform') 196 | ); 197 | if(isset($translate_table[$key])){ 198 | if(in_array($key, array("hostid", "owner_hostid"))){ 199 | $value = new CLink($value, "latest.php?hostid={$value}"); 200 | } 201 | return array("key" => $translate_table[$key], "value" => $value); 202 | }else{ 203 | return null; 204 | } 205 | } 206 | 207 | // for vSphere 208 | 209 | function make_vm_table($type, $instances){ 210 | $form_name = "{$type}_vms"; 211 | 212 | // Create Form 213 | $form = new CForm(); 214 | $form->setName($form_name); 215 | $form->setAttribute('id', $form_name); 216 | $form->addVar('driver', 'vsphere'); 217 | $form->setAction('#'); 218 | 219 | // Create Table Header 220 | $table = new CTableInfo(); 221 | switch($type){ 222 | case 'poweron': 223 | $table->setHeader(array( 224 | is_show_all_nodes() ? _('Node') : null, 225 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."','all_hosts','');"), 226 | _('Instance name'), 227 | _('Status'), 228 | _('CPU threads'), 229 | _('Memory(GB)'), 230 | _('Interface'), 231 | _('SSH Connect'), 232 | )); 233 | break; 234 | case 'poweroff': 235 | case 'suspend': 236 | $table->setHeader(array( 237 | is_show_all_nodes() ? _('Node') : null, 238 | new CCheckBox('all_hosts', null, "checkAllHosts('".$form_name."','all_hosts','');"), 239 | _('Instance name'), 240 | _('Status'), 241 | _('CPU threads'), 242 | _('Memory(GB)'), 243 | _('Interface'), 244 | )); 245 | break; 246 | case 'other': 247 | $table->setHeader(array( 248 | is_show_all_nodes() ? _('Node') : null, 249 | _('Instance name'), 250 | _('Status'), 251 | _('CPU thread'), 252 | _('Memory(GB)'), 253 | _('Interface'), 254 | _('Question'), 255 | )); 256 | break; 257 | default: 258 | return null; 259 | } 260 | 261 | // Create Row 262 | foreach($instances as $instance){ 263 | $table->addRow(make_hint_row($type, $instance)); 264 | } 265 | 266 | $hostids = array_map(function($instance){return $instance['hostid'];}, $instances); 267 | $footer = get_table_header(make_operation_box_footer($hostids, $form_name)); 268 | $form->addItem(array('', $table, '', $footer)); 269 | return $form; 270 | } 271 | 272 | function make_hint_row($type, $instance){ 273 | $state = $instance['state']; 274 | if($type == "other" && $instance["stuck_state"] == 1) $state = "stuck"; 275 | $r = new CRow(); 276 | if(in_array($type, array("poweron", "poweroff", "suspend"))){ 277 | $r->addItem(new CCheckBox("hostids[]", null, null, $instance['hostid'])); 278 | } 279 | $hostScripts = API::Script()->getScriptsByHosts(zbx_objectValues(array($instance['host']), 'hostid')); 280 | $hostSpan = new CSpan(nbsp($instance['name']), 'link_menu menu-host'); 281 | $hostSpan->setMenuPopup(getMenuPopupHost($instance['host'], $hostScripts[$instance['hostid']])); 282 | $r->addItem($hostSpan); 283 | $r->addItem(new CCol($state, get_item_level($state))); 284 | $r->addItem($instance['cpu']); 285 | $r->addItem($instance['memory']); 286 | $r->addItem($instance['main_interface']); 287 | if($type == "other" && $instance["stuck_state"] == 1){ 288 | $question = $instance['stuck_question']; 289 | $json = new CJSON(); 290 | $choiceinfos = $json->decode($instance['stuck_choices'], true); 291 | $answer_form = new CForm(); 292 | $answer_form->setAction('#'); 293 | $answer_form->setAttribute('id', "answer"); 294 | $answer_form->addVar("driver", "vsphere"); 295 | $answer_form->addVar("hostids[]", $instance['hostid']); 296 | $instancename = $instance['host']['host']; 297 | $answer_button = new CButton('answer', _('Answer'), "return checkAnswer('answer', 'choice', '${instancename}', 'Execute');"); 298 | $choice_table = new CTableInfo(); 299 | foreach($choiceinfos as $choice){ 300 | $radio = new CRadioButton('choice', $choice['key']); 301 | $label = new CLabel($choice['label']); 302 | $choice_table->addRow(new CRow(array($radio, $label))); 303 | } 304 | $answer_form->addItem($choice_table); 305 | $answer_form->addItem($answer_button); 306 | $question_span = new CSpan($question); 307 | $question_span->setHint($answer_form); 308 | $question_col = new CCol($question_span, 'warning'); 309 | $r->addItem($question_col); 310 | }else if($type == "poweron"){ 311 | $ssh_link = new CLink(_('connect'), "gateone.php?hostid=$instance[hostid]"); 312 | $ssh_link->setTarget("_blank"); 313 | $r->addItem($ssh_link); 314 | } 315 | return $r; 316 | } 317 | 318 | function make_datastore_table($hostid, $datastores){ 319 | $table = new CTableInfo(); 320 | $table->setHeader(array( 321 | _('Datastore Name'), 322 | _('Free Space'), 323 | _('Capacity'), 324 | )); 325 | foreach($datastores as $datastore){ 326 | $name_col = new CCol($datastore["name"]); 327 | $freeSpace = convert_units(array('value' => $datastore["freeSpace"], 'units' => "B")); 328 | $capacity = convert_units(array('value' => $datastore["capacity"], 'units' => "B")); 329 | $free_space_col = new CCol($freeSpace); 330 | $capacity_col = new CCol($capacity); 331 | $r = new CRow(array($name_col, $free_space_col, $capacity_col)); 332 | $table->addRow($r); 333 | } 334 | return $table; 335 | } 336 | 337 | // for IPMI 338 | 339 | function get_triggers($hostid){ 340 | $options = array( 341 | 'nodeids' => get_current_nodeid(), 342 | 'hostids' => $hostid, 343 | 'monitored' => 1, 344 | 'expandData' => 1, 345 | 'expandDescription' => 1, 346 | 'expandExpression' => 1, 347 | 'filter' => array( 348 | 'value' => TRIGGER_VALUE_TRUE 349 | ), 350 | 'output' => API_OUTPUT_EXTEND, 351 | ); 352 | return API::Trigger()->get($options); 353 | } 354 | 355 | function make_trigger_table($triggers, $host_name){ 356 | $table = new CTableInfo(); 357 | $table->setHeader(array( 358 | _('Host'), 359 | _('Problem'), 360 | _('Age') 361 | )); 362 | foreach($triggers as $trigger){ 363 | $description = $trigger["description"]; 364 | $r = new CRow(); 365 | $r->addItem($host_name); 366 | $r->addItem(new CCol($description, getSeverityStyle($trigger['priority']))); 367 | $r->addItem(zbx_date2age($trigger['lastchange'])); 368 | $table->addRow($r); 369 | } 370 | return $table; 371 | } 372 | 373 | ?> 374 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.2/custom/monitoring.dashboard.js.php: -------------------------------------------------------------------------------- 1 | 22 | 174 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.2/dashboard_scripts_exec.php: -------------------------------------------------------------------------------- 1 | array(T_ZBX_STR, O_OPT, P_SYS, DB_ID, 'isset({execute})'), 35 | 'hostname' => array(T_ZBX_STR, O_OPT, P_SYS, DB_ID, 'isset({answer})'), 36 | 'scriptid' => array(T_ZBX_INT, O_OPT, P_SYS, DB_ID, 'isset({execute})'), 37 | 'choiceid' => array(T_ZBX_STR, O_OPT, P_SYS, DB_ID, 'isset({answer})'), 38 | 'execute' => array(T_ZBX_INT, O_OPT, P_ACT, IN('0,1'), null), 39 | 'answer' => array(T_ZBX_INT, O_OPT, P_ACT, IN('0,1'), null) 40 | ); 41 | check_fields($fields); 42 | 43 | if (isset($_REQUEST['execute'])) { 44 | $json = new CJSON(); 45 | $scriptid = get_request('scriptid'); 46 | $hostids = $json->decode(get_request('hostids'), true); 47 | $hostids = $json->decode($hostids, true); 48 | 49 | $data = array( 50 | 'message' => '', 51 | 'info' => DBfetch(DBselect('SELECT s.name FROM scripts s WHERE s.scriptid='.$scriptid)) 52 | ); 53 | 54 | $results = array(); 55 | foreach ($hostids as $hostid) { 56 | $results[] = API::Script()->execute(array('hostid' => $hostid, 'scriptid' => $scriptid)); 57 | } 58 | 59 | $isErrorExist = false; 60 | foreach ($results as $result) { 61 | if (empty($result)) { 62 | $isErrorExist = true; 63 | } 64 | elseif ($result['response'] == 'failed') { 65 | error($result['value']); 66 | $isErrorExist = true; 67 | } 68 | else { 69 | $data['message'] .= convert_json_message($result['value'])."\n"; 70 | } 71 | } 72 | 73 | if ($isErrorExist) { 74 | show_error_message(_('Cannot connect to the trapper port of zabbix server daemon, but it should be available to run the script.')); 75 | } 76 | 77 | // render view 78 | $scriptView = new CView('general.script.execute', $data); 79 | $scriptView->render(); 80 | $scriptView->show(); 81 | }elseif (isset($_REQUEST['answer'])) { 82 | $hostname = get_request('hostname'); 83 | $choiceid = get_request('choiceid'); 84 | $script = '/opt/hyclops/globalscripts/request_action.py vsphere '.$hostname.' \'{"command": "answer", "choiceid":"'.$choiceid.'"}\' 2>&1'; 85 | $result = array(); 86 | $ret = null; 87 | exec($script, $result, $ret); 88 | $message = ''; 89 | if($ret == 0){ 90 | $message = convert_json_message($result[0]); 91 | }else{ 92 | $message = $result; 93 | } 94 | $data = array( 95 | 'message' => $message, 96 | 'info' => array("name" => 'Answer the question') 97 | ); 98 | $scriptView = new CView('general.script.execute', $data); 99 | $scriptView->render(); 100 | $scriptView->show(); 101 | } 102 | 103 | function convert_json_message($original_msg){ 104 | $message = ''; 105 | $tmp_msg = str_replace('\'', '"', $original_msg); 106 | $tmp_msg = str_replace('u"', '"', $tmp_msg); 107 | $tmp_msg = json_decode($tmp_msg, true); 108 | if($tmp_msg['result']){ 109 | $message = "Result: "; 110 | $message .= $tmp_msg['result']; 111 | $message .= "\nMessage: "; 112 | $message .= $tmp_msg['message']; 113 | $message .= "\n\n* Please wait a few minutes until the result is reflected in Dashboard."; 114 | }else{ 115 | $message = $original_msg; 116 | } 117 | return $message; 118 | } 119 | 120 | require_once dirname(__FILE__).'/include/page_footer.php'; 121 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.2/gateone.php: -------------------------------------------------------------------------------- 1 | get(array( 27 | "hostids" => $hostid, 28 | "selectInterfaces" => "extend" 29 | )); 30 | $ssh_con_url = ""; 31 | if(isset($hosts[0]["interfaces"])){ 32 | $ssh_con_url =get_ssh_interface($hosts[0]); 33 | } 34 | $user = CWebUser::$data['alias']; 35 | $auth = get_authobj($user); 36 | 37 | function get_ssh_interface($host){ 38 | foreach( $host["interfaces"] as $interface ){ 39 | if($interface["main"] == 1){ 40 | $ssh_interface = $interface["useip"] == 0 ? $interface["dns"] : $interface["ip"]; 41 | if($ssh_interface == 'dummy-interface.invalid'){ 42 | continue; 43 | }else{ 44 | return $ssh_interface; 45 | } 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | function get_global_macro($name){ 52 | $macros = API::UserMacro()->get(array( 53 | 'output' => API_OUTPUT_EXTEND, 54 | 'globalmacro' => true 55 | )); 56 | $macros = zbx_toHash($macros,'macro'); 57 | return $macros[$name]['value']; 58 | } 59 | 60 | function get_authobj($upn){ 61 | $api_key = get_global_macro('{$GATEONE_KEY}'); 62 | $secret = get_global_macro('{$GATEONE_SECRET}'); 63 | $authobj = array( 64 | 'api_key' => $api_key, 65 | 'upn' => $upn, 66 | 'timestamp' => (string)ceil(microtime(true)*1000), 67 | 'signature_method' => 'HMAC-SHA1', 68 | 'api_version' => '1.0' 69 | ); 70 | $authobj['signature'] = hash_hmac('sha1', $api_key.$upn.$authobj['timestamp'], $secret); 71 | return json_encode($authobj); 72 | } 73 | 74 | print<< 76 | 77 | 78 | 111 | 112 | 113 | 114 |
115 |
116 |
117 | 118 | 119 | EOF; 120 | ?> 121 | -------------------------------------------------------------------------------- /misc/zabbix-custom/2.2/include/menu.inc.php: -------------------------------------------------------------------------------- 1 | array( 26 | 'label' => _('Monitoring'), 27 | 'user_type' => USER_TYPE_ZABBIX_USER, 28 | 'node_perm' => PERM_READ, 29 | 'default_page_id' => 0, 30 | 'pages' => array( 31 | array( 32 | 'url' => 'dashboard.php', 33 | 'label' => _('Dashboard'), 34 | 'sub_pages' => array('dashconf.php') 35 | ), 36 | array( 37 | 'url' => 'overview.php', 38 | 'label' => _('Overview') 39 | ), 40 | array( 41 | 'url' => 'httpmon.php', 42 | 'label' => _('Web'), 43 | 'sub_pages' => array('httpdetails.php') 44 | ), 45 | array( 46 | 'url' => 'latest.php', 47 | 'label' => _('Latest data'), 48 | 'sub_pages' => array('history.php', 'chart.php') 49 | ), 50 | array( 51 | 'url' => 'tr_status.php', 52 | 'label' => _('Triggers'), 53 | 'sub_pages' => array('acknow.php', 'tr_comments.php', 'chart4.php', 'scripts_exec.php', 'dashboard_scripts_exec.php') 54 | ), 55 | array( 56 | 'url' => 'events.php', 57 | 'label' => _('Events'), 58 | 'sub_pages' => array('tr_events.php') 59 | ), 60 | array( 61 | 'url' => 'charts.php', 62 | 'label' => _('Graphs'), 63 | 'sub_pages' => array('chart2.php', 'chart3.php', 'chart6.php', 'chart7.php') 64 | ), 65 | array( 66 | 'url' => 'screens.php', 67 | 'label' => _('Screens'), 68 | 'sub_pages' => array('slides.php') 69 | ), 70 | array( 71 | 'url' => 'maps.php', 72 | 'label' => _('Maps'), 73 | 'sub_pages' => array('map.php') 74 | ), 75 | array( 76 | 'url' => 'discovery.php', 77 | 'label' => _('Discovery'), 78 | 'user_type' => USER_TYPE_ZABBIX_ADMIN 79 | ), 80 | array( 81 | 'url' => 'srv_status.php', 82 | 'label' => _('IT services'), 83 | 'force_disable_all_nodes' => true, 84 | 'sub_pages' => array('report3.php', 'chart5.php') 85 | ), 86 | array( 87 | 'url' => 'chart3.php' 88 | ), 89 | array( 90 | 'url' => 'imgstore.php' 91 | ), 92 | array( 93 | 'url' => 'search.php' 94 | ), 95 | array( 96 | 'url' => 'jsrpc.php' 97 | ) 98 | ) 99 | ), 100 | 'cm' => array( 101 | 'label' => _('Inventory'), 102 | 'user_type' => USER_TYPE_ZABBIX_USER, 103 | 'node_perm' => PERM_READ, 104 | 'default_page_id' => 0, 105 | 'pages' => array( 106 | array( 107 | 'url' => 'hostinventoriesoverview.php', 108 | 'label' => _('Overview') 109 | ), 110 | array( 111 | 'url' => 'hostinventories.php', 112 | 'label' => _('Hosts') 113 | ) 114 | ) 115 | ), 116 | 'reports' => array( 117 | 'label' => _('Reports'), 118 | 'user_type' => USER_TYPE_ZABBIX_USER, 119 | 'node_perm' => PERM_READ, 120 | 'default_page_id' => 0, 121 | 'pages' => array( 122 | array( 123 | 'url' => 'report1.php', 124 | 'label' => _('Status of Zabbix'), 125 | 'user_type' => USER_TYPE_SUPER_ADMIN 126 | ), 127 | array( 128 | 'url' => 'report2.php', 129 | 'label' => _('Availability report') 130 | ), 131 | array( 132 | 'url' => 'report5.php', 133 | 'label' => _('Triggers top 100') 134 | ), 135 | array( 136 | 'url' => 'report6.php', 137 | 'label' => _('Bar reports'), 138 | 'sub_pages' => array('popup_period.php', 'popup_bitem.php', 'chart_bar.php') 139 | ), 140 | array( 141 | 'url' => 'popup.php' 142 | ), 143 | array( 144 | 'url' => 'popup_right.php' 145 | ) 146 | ), 147 | ), 148 | 'config' => array( 149 | 'label' => _('Configuration'), 150 | 'user_type' => USER_TYPE_ZABBIX_ADMIN, 151 | 'node_perm' => PERM_READ, 152 | 'default_page_id' => 0, 153 | 'force_disable_all_nodes' => true, 154 | 'pages' => array( 155 | array( 156 | 'url' => 'conf.import.php' 157 | ), 158 | array( 159 | 'url' => 'hostgroups.php', 160 | 'label' => _('Host groups') 161 | ), 162 | array( 163 | 'url' => 'templates.php', 164 | 'label' => _('Templates') 165 | ), 166 | array( 167 | 'url' => 'hosts.php', 168 | 'label' => _('Hosts'), 169 | 'sub_pages' => array( 170 | 'items.php', 171 | 'triggers.php', 172 | 'graphs.php', 173 | 'applications.php', 174 | 'tr_logform.php', 175 | 'tr_testexpr.php', 176 | 'popup_trexpr.php', 177 | 'host_discovery.php', 178 | 'disc_prototypes.php', 179 | 'trigger_prototypes.php', 180 | 'host_prototypes.php', 181 | 'httpconf.php', 182 | 'popup_httpstep.php' 183 | ) 184 | ), 185 | array( 186 | 'url' => 'maintenance.php', 187 | 'label' => _('Maintenance') 188 | ), 189 | array( 190 | 'url' => 'actionconf.php', 191 | 'label' => _('Actions') 192 | ), 193 | array( 194 | 'url' => 'screenconf.php', 195 | 'label' => _('Screens'), 196 | 'sub_pages' => array('screenedit.php') 197 | ), 198 | array( 199 | 'url' => 'slideconf.php', 200 | 'label' => _('Slide shows'), 201 | ), 202 | array( 203 | 'url' => 'sysmaps.php', 204 | 'label' => _('Maps'), 205 | 'sub_pages' => array('image.php', 'sysmap.php') 206 | ), 207 | array( 208 | 'url' => 'discoveryconf.php', 209 | 'label' => _('Discovery') 210 | ), 211 | array( 212 | 'url' => 'services.php', 213 | 'label' => _('IT services') 214 | ), 215 | ) 216 | ), 217 | 'admin' => array( 218 | 'label' => _('Administration'), 219 | 'user_type' => USER_TYPE_SUPER_ADMIN, 220 | 'node_perm' => PERM_READ_WRITE, 221 | 'default_page_id' => 1, 222 | 'force_disable_all_nodes'=> true, 223 | 'pages' => array( 224 | array( 225 | 'url' => 'adm.gui.php', 226 | 'label' => _('General'), 227 | 'sub_pages' => array( 228 | 'adm.housekeeper.php', 229 | 'adm.images.php', 230 | 'adm.iconmapping.php', 231 | 'adm.regexps.php', 232 | 'adm.macros.php', 233 | 'adm.valuemapping.php', 234 | 'adm.workingtime.php', 235 | 'adm.triggerseverities.php', 236 | 'adm.triggerdisplayoptions.php', 237 | 'adm.other.php' 238 | ) 239 | ), 240 | array( 241 | 'url' => 'proxies.php', 242 | 'label' => _('DM'), 243 | 'sub_pages' => array('nodes.php') 244 | ), 245 | array( 246 | 'url' => 'authentication.php', 247 | 'label' => _('Authentication') 248 | ), 249 | array( 250 | 'url' => 'usergrps.php', 251 | 'label' => _('Users'), 252 | 'sub_pages' => array('users.php', 'popup_usrgrp.php') 253 | ), 254 | array( 255 | 'url' => 'media_types.php', 256 | 'label' => _('Media types') 257 | ), 258 | array( 259 | 'url' => 'scripts.php', 260 | 'label' => _('Scripts') 261 | ), 262 | array( 263 | 'url' => 'auditlogs.php', 264 | 'label' => _('Audit'), 265 | 'sub_pages' => array('auditacts.php') 266 | ), 267 | array( 268 | 'url' => 'queue.php', 269 | 'label' => _('Queue') 270 | ), 271 | array( 272 | 'url' => 'report4.php', 273 | 'label' => _('Notifications') 274 | ), 275 | array( 276 | 'url' => 'setup.php', 277 | 'label' => _('Installation') 278 | ) 279 | ) 280 | ), 281 | 'login' => array( 282 | 'label' => _('Login'), 283 | 'user_type' => 0, 284 | 'default_page_id' => 0, 285 | 'hide_node_selection' => 1, 286 | 'force_disable_all_nodes'=> true, 287 | 'pages' => array( 288 | array( 289 | 'url' => 'index.php', 290 | 'sub_pages' => array('profile.php', 'popup_media.php') 291 | ) 292 | ) 293 | ) 294 | ); 295 | 296 | /** 297 | * NOTE - menu array format: 298 | * first level: 299 | * 'label' = main menu title. 300 | * 'default_page_id = default page url from 'pages' then opened menu. 301 | * 'pages' = collection of pages which are displayed from this menu. 302 | * these pages are saved a last visited submenu of main menu. 303 | * 304 | * second level (pages): 305 | * 'url' = real url for this page 306 | * 'label' = submenu title, if missing, menu skipped, but remembered as last visited page. 307 | * 'sub_pages' = collection of pages for displaying but not remembered as last visited. 308 | */ 309 | function zbx_construct_menu(&$main_menu, &$sub_menus, &$page) { 310 | global $ZBX_MENU; 311 | 312 | $denied_page_requested = false; 313 | $page_exists = false; 314 | $deny = !defined('ZBX_PAGE_NO_AUTHORIZATION'); 315 | 316 | foreach ($ZBX_MENU as $label => $menu) { 317 | $show_menu = true; 318 | 319 | if (isset($menu['user_type'])) { 320 | $show_menu &= ($menu['user_type'] <= CWebUser::$data['type']); 321 | } 322 | if ($label == 'login') { 323 | $show_menu = false; 324 | } 325 | 326 | $menu_class = 'horizontal_menu_n'; 327 | $sub_menus[$label] = array(); 328 | foreach ($menu['pages'] as $sub_page) { 329 | $show_sub_menu = true; 330 | 331 | // show check 332 | if (!isset($sub_page['label'])) { 333 | $show_sub_menu = false; 334 | } 335 | if (!isset($sub_page['user_type'])) { 336 | $sub_page['user_type'] = $menu['user_type']; 337 | } 338 | if (CWebUser::$data['type'] < $sub_page['user_type']) { 339 | $show_sub_menu = false; 340 | } 341 | 342 | $row = array( 343 | 'menu_text' => isset($sub_page['label']) ? $sub_page['label'] : '', 344 | 'menu_url' => $sub_page['url'], 345 | 'class' => 'highlight', 346 | 'selected' => false 347 | ); 348 | $sub_menu_active = ($page['file'] == $sub_page['url']); 349 | $sub_menu_active |= (isset($sub_page['sub_pages']) && str_in_array($page['file'], $sub_page['sub_pages'])); 350 | 351 | if ($sub_menu_active) { 352 | // permition check 353 | $deny &= (CWebUser::$data['type'] < $menu['user_type'] || CWebUser::$data['type'] < $sub_page['user_type']); 354 | 355 | $menu_class = 'active'; 356 | $page_exists = true; 357 | $page['menu'] = $label; 358 | $row['selected'] = true; 359 | 360 | if (!defined('ZBX_PAGE_NO_MENU')) { 361 | CProfile::update('web.menu.'.$label.'.last', $sub_page['url'], PROFILE_TYPE_STR); 362 | } 363 | } 364 | 365 | if ($show_sub_menu) { 366 | $sub_menus[$label][] = $row; 367 | } 368 | } 369 | 370 | if ($page_exists && !defined('ZBX_NOT_ALLOW_ALL_NODES') && (isset($menu['force_disable_all_nodes']) || isset($sub_page['force_disable_all_nodes']))) { 371 | define('ZBX_NOT_ALLOW_ALL_NODES', 1); 372 | } 373 | 374 | if ($page_exists && $deny) { 375 | $denied_page_requested = true; 376 | } 377 | 378 | if (!$show_menu) { 379 | unset($sub_menus[$label]); 380 | continue; 381 | } 382 | 383 | $menu_url = $sub_menus[$label][$menu['default_page_id']]['menu_url']; 384 | $mmenu_entry = new CCol($menu['label'], $menu_class); 385 | $mmenu_entry->setAttribute('id', $label); 386 | $mmenu_entry->addAction('onclick', 'javascript: redirect(\''.$menu_url.'\');'); 387 | $mmenu_entry->addAction('onmouseover', 'javascript: MMenu.mouseOver(\''.$label.'\');'); 388 | $mmenu_entry->addAction('onmouseout', 'javascript: MMenu.mouseOut();'); 389 | array_push($main_menu, $mmenu_entry); 390 | } 391 | 392 | if (!$page_exists && $page['type'] != PAGE_TYPE_XML && $page['type'] != PAGE_TYPE_CSV && $page['type'] != PAGE_TYPE_TEXT_FILE) { 393 | $denied_page_requested = true; 394 | } 395 | 396 | return $denied_page_requested; 397 | } 398 | 399 | function zbx_define_menu_restrictions($page, $ZBX_MENU) { 400 | foreach ($ZBX_MENU as $section) { 401 | foreach ($section['pages'] as $menu_page) { 402 | if ($menu_page['url'] == $page['file'] || (isset($menu_page['sub_pages']) && str_in_array($page['file'], $menu_page['sub_pages']))) { 403 | if (isset($section['force_disable_all_nodes']) && !defined('ZBX_NOT_ALLOW_ALL_NODES')) { 404 | define('ZBX_NOT_ALLOW_ALL_NODES', 1); 405 | } 406 | if (isset($section['hide_node_selection']) && !defined('ZBX_HIDE_NODE_SELECTION')) { 407 | define('ZBX_HIDE_NODE_SELECTION', 1); 408 | } 409 | return null; 410 | } 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /replace_frontend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | target_frontend_dir=$1 4 | zabbix_version=$2 5 | if [[ ! $zabbix_version =~ ^(2.0|2.2)$ ]]; 6 | then 7 | echo "Not supported version (Supported only 2.0 or 2.2)" 8 | exit 1 9 | fi 10 | 11 | source_dir=`dirname ${0}`"/misc/zabbix-custom/$zabbix_version" 12 | 13 | if [ -d $target_frontend_dir ]; 14 | then 15 | echo "Replace and copy some files..." 16 | if [ -f $target_frontend_dir/dashboard.php.orig ]; 17 | then 18 | cp -r -a $source_dir/* $target_frontend_dir 19 | exit 0 20 | fi 21 | cp -r -a --backup=nil -S ".orig" $source_dir/* $target_frontend_dir 22 | exit 0 23 | fi 24 | echo "No such directory $target_frontend_dir" 25 | exit 1 26 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import time 6 | import logging 7 | import optparse 8 | import configobj 9 | import traceback 10 | from daemon import DaemonContext 11 | from daemon.pidfile import TimeoutPIDLockFile 12 | from hyclops.queue import MessageQueue 13 | 14 | # parse command-line options 15 | parser = optparse.OptionParser() 16 | basedir = os.path.dirname(os.path.abspath(__file__)) 17 | parser.add_option('-c', dest="config_file", help="config file location", default=os.path.join(basedir, "hyclops.conf")) 18 | parser.add_option('--pid', dest="pid_file", help="pid file location", default="/tmp/hyclops.pid") 19 | (options, args) = parser.parse_args() 20 | 21 | # load config file 22 | try: 23 | config = configobj.ConfigObj(options.config_file, file_error=True) 24 | except IOError, e: 25 | sys.exit(e) 26 | 27 | if os.path.exists(options.pid_file): 28 | sys.exit("pid file (%s) already exists" % options.pid_file) 29 | 30 | # start daemon 31 | with DaemonContext(pidfile=TimeoutPIDLockFile(options.pid_file, 1), stderr=sys.stderr): 32 | # logging settings 33 | log_level = config["logging"]["log_level"] 34 | log_file = config["logging"]["log_file"] 35 | log_format = '[%(asctime)s] %(name)s (%(threadName)s) %(levelname)s: %(message)s' 36 | logging.basicConfig(filename=log_file, level=logging.WARNING, format=log_format) 37 | logger = logging.getLogger('hyclops') 38 | logger.setLevel(getattr(logging, log_level)) 39 | 40 | # add environments 41 | for key, value in config["environments"].items(): 42 | os.environ[key] = value 43 | 44 | # create queue 45 | listen_address = config["hyclops"]["listen_address"] 46 | listen_port = config["hyclops"]["listen_port"] 47 | queue = MessageQueue(config) 48 | try: 49 | queue.bind(listen_address, listen_port) 50 | logger.info("Message queue is opened") 51 | except Exception, e: 52 | err_msg = "Failed to bind ZeroMQ socket: %s" % str(e) 53 | logger.error(err_msg) 54 | sys.exit(err_msg) 55 | 56 | # polling loop 57 | while True: 58 | try: 59 | queue.poll() 60 | time.sleep(3) 61 | except (KeyboardInterrupt, SystemExit), e: 62 | queue.close() 63 | logger.info("Message queue is closed by %s" % e.__class__.__name__) 64 | break 65 | except Exception, e: 66 | logger.error(traceback.format_exc()) 67 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import shutil 5 | import commands 6 | from setuptools import setup, Command 7 | 8 | 9 | class ReplaceCommand(Command): 10 | description = "replace zabbix dashboard to custom version" 11 | user_options = [('frontend-dir=', 'd', 'zabbix frontend location'), 12 | ('zabbix-version=', 'z', 'zabbix server version')] 13 | official_files = [ 14 | "dashboard.php", 15 | "include/menu.inc.php", 16 | ] 17 | 18 | def initialize_options(self): 19 | self.frontend_dir = "" 20 | self.zabbix_version = "2.2" 21 | 22 | def finalize_options(self): 23 | pass 24 | 25 | def run(self): 26 | if not os.path.exists(self.frontend_dir): 27 | print "Target directory does not exist" 28 | return 29 | if not os.path.exists(os.path.join(self.frontend_dir, "dashboard.php")): 30 | print "Could not find dashboard.php" 31 | return 32 | if not os.path.exists(os.path.join(self.frontend_dir, "custom")): 33 | os.makedirs(os.path.join(self.frontend_dir, "custom")) 34 | if self.zabbix_version not in ['2.0', '2.2']: 35 | print "Not supported version (Supported only 2.0 or 2.2)" 36 | return 37 | from_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "misc/zabbix-custom/%s/" % self.zabbix_version) 38 | for searchpath, dirs, files in os.walk(from_dir): 39 | for file in files: 40 | basename = os.path.join(searchpath, file).replace(from_dir, '') 41 | src_path = os.path.join(searchpath, file) 42 | dst_path = os.path.join(self.frontend_dir, basename) 43 | if basename in self.official_files: 44 | backup_path = dst_path.replace('.php', '.org') 45 | if os.path.exists(backup_path): 46 | print "backup file already exists: %s" % backup_path 47 | else: 48 | print "backup original file %s to %s" % (dst_path, backup_path) 49 | shutil.move(dst_path, backup_path) 50 | if os.path.exists(dst_path): 51 | print "update %s" % dst_path 52 | else: 53 | print "create %s" % dst_path 54 | shutil.copy2(src_path, dst_path) 55 | 56 | 57 | class RollbackCommand(Command): 58 | description = "rollback zabbix dashboard to original version" 59 | user_options = [('frontend-dir=', 'd', 'zabbix frontend location'), 60 | ('zabbix-version=', 'z', 'zabbix server version')] 61 | official_files = [ 62 | "dashboard.php", 63 | "include/menu.inc.php", 64 | ] 65 | 66 | def initialize_options(self): 67 | self.frontend_dir = "" 68 | self.zabbix_version = "2.2" 69 | 70 | def finalize_options(self): 71 | pass 72 | 73 | def run(self): 74 | if not os.path.exists(self.frontend_dir): 75 | print "Target directory does not exist" 76 | return 77 | if self.zabbix_version not in ['2.0', '2.2']: 78 | print "Not supported version (Supported only 2.0 or 2.2)" 79 | return 80 | from_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "misc/zabbix-custom/%s/" % self.zabbix_version) 81 | for searchpath, dirs, files in os.walk(from_dir): 82 | for file in files: 83 | basename = os.path.join(searchpath, file).replace(from_dir, '') 84 | dst_path = os.path.join(self.frontend_dir, basename) 85 | if basename in self.official_files: 86 | backup_path = dst_path.replace('.php', '.org') 87 | if os.path.exists(backup_path): 88 | print "rollback %s to %s" % (backup_path, dst_path) 89 | shutil.move(backup_path, dst_path) 90 | else: 91 | print "%s does not exist" % backup_path 92 | else: 93 | if os.path.exists(dst_path): 94 | print "remove %s" % dst_path 95 | os.remove(dst_path) 96 | if os.path.exists(os.path.join(self.frontend_dir, "custom")): 97 | os.rmdir(os.path.join(self.frontend_dir, "custom")) 98 | 99 | 100 | class ImportCommand(Command): 101 | description = "import zabbix global scripts and global macros" 102 | user_options = [("frontend-url=", "f", "zabbix frontend url"), 103 | ("user=", "u", "zabbix user name"), 104 | ("password=", "p", "zabbix password")] 105 | 106 | def initialize_options(self): 107 | # set default value 108 | self.frontend_url = "http://localhost/zabbix" 109 | self.user = "Admin" 110 | self.password = "zabbix" 111 | 112 | def finalize_options(self): 113 | pass 114 | 115 | def run(self): 116 | from zabbix_api import ZabbixAPI, ZabbixAPIException 117 | from xml.etree import ElementTree 118 | # connect zabbix frontend 119 | try: 120 | self.zabbix_api = ZabbixAPI(self.frontend_url) 121 | self.zabbix_api.login(self.user, self.password) 122 | except ZabbixAPIException, e: 123 | print "Failed to connect zabbix frontend: %s" % str(e[0]).partition("while sending")[0] 124 | return 125 | # import templates 126 | print "Import templates" 127 | try: 128 | with open(os.path.join(pwd, "misc/import_data/templates.xml")) as f: 129 | template_xml = f.read() 130 | req = self.zabbix_api.json_obj("configuration.import", { 131 | "format": "xml", 132 | "source": template_xml, 133 | "rules": { 134 | "items": {"createMissing": True}, 135 | "applications": {"createMissing": True}, 136 | "graphs": {"createMissing": True}, 137 | "groups": {"createMissing": True}, 138 | "templateLinkage": {"createMissing": True}, 139 | "templates": {"createMissing": True}, 140 | "triggers": {"createMissing": True}, 141 | } 142 | }) 143 | self.zabbix_api.do_request(req) 144 | except IOError, e: 145 | print " " + str(e) 146 | except ZabbixAPIException, e: 147 | print " " + str(e[0]).partition("while sending")[0] 148 | # import global scripts 149 | print "Import global scripts" 150 | tree = ElementTree.parse(os.path.join(pwd, "misc/import_data/globalscripts.xml")) 151 | for script in tree.find("scripts"): 152 | try: 153 | params = {} 154 | params["name"] = script.findtext("name") 155 | params["command"] = script.findtext("command") 156 | params["host_access"] = script.findtext("host_access", 2) 157 | params["description"] = script.findtext("description", "") 158 | params["confirmation"] = script.findtext("confirmation", "") 159 | params["type"] = script.findtext("type", 0) 160 | params["execute_on"] = script.findtext("execute_on", 1) 161 | params["usrgrpid"] = self.get_user_group_id(script.find("usergroups")) 162 | params["groupid"] = self.get_group_id(script.find("groups")) 163 | self.zabbix_api.script.create(params) 164 | print " " + "Create '%s'" % params["name"] 165 | except ZabbixAPIException, e: 166 | print " " + str(e[0]).partition("while sending")[0] 167 | # import global macros 168 | print "Import global macros" 169 | tree = ElementTree.parse(os.path.join(pwd, "misc/import_data/globalmacros.xml")) 170 | for macro in tree.find("macros"): 171 | try: 172 | params = {} 173 | params["macro"] = macro.findtext("macro") 174 | params["value"] = macro.findtext("value") 175 | self.zabbix_api.usermacro.createglobal(params) 176 | print " " + "Create '%s'" % params["macro"] 177 | except ZabbixAPIException, e: 178 | print " " + str(e[0]).partition("while sending")[0] 179 | 180 | def get_user_group_id(self, usergroups, default=0): 181 | if len(usergroups) == 0: 182 | return default 183 | else: 184 | groupids = [] 185 | for group in usergroups: 186 | name = group.findtext("name") 187 | grps = self.zabbix_api.usergroup.get({ 188 | "filter": {"name": name}, 189 | }) 190 | if grps and "groupid" in grps[0]: 191 | groupids.append(grps[0]["groupid"]) 192 | return groupids[0] if groupids else default 193 | 194 | def get_group_id(self, groups, default=0): 195 | if len(groups) == 0: 196 | return default 197 | else: 198 | groupids = [] 199 | for group in groups: 200 | name = group.findtext("name") 201 | grps = self.zabbix_api.hostgroup.get({ 202 | "filter": {"name": name}, 203 | }) 204 | if grps and "groupid" in grps[0]: 205 | groupids.append(grps[0]["groupid"]) 206 | return groupids[0] if groupids else default 207 | 208 | 209 | # setup 210 | pwd = os.path.abspath(os.path.dirname(__file__)) 211 | setup(name='hyclops', 212 | version='0.2.1', 213 | description='Hybrid cloud operations plugin for Zabbix', 214 | author='TIS Inc.', 215 | url='http://tech-sketch.github.io/hyclops', 216 | package_dir={'hyclops': os.path.join(pwd, 'hyclops')}, 217 | packages=['hyclops', 'hyclops.connector', 'hyclops.libcloud_driver'], 218 | data_files=[ 219 | ('/opt/hyclops', [os.path.join(pwd, 'hyclops.conf'), os.path.join(pwd, 'run.py')]), 220 | ('/opt/hyclops/globalscripts', [os.path.join(pwd, 'misc/globalscripts/request_action.py')]), 221 | ('/opt/hyclops/logs', []), 222 | ('/var/run/hyclops', []), 223 | ('/opt/hyclops/cron_scripts', [os.path.join(pwd, 'misc/cron_scripts/delete_not_exist_hosts.py')]) 224 | ], 225 | install_requires=['apache_libcloud >=0.12.1', 'zabbix_api', 'pyzmq', 'psphere', 'configobj', 'python_daemon'], 226 | cmdclass={'replace': ReplaceCommand, 'rollback': RollbackCommand, 'import': ImportCommand}) 227 | # hyclops user add 228 | if os.system('/usr/bin/id hyclops') != 0: 229 | nologin_path = commands.getoutput('/usr/bin/which nologin') 230 | print nologin_path 231 | os.system('/usr/sbin/useradd hyclops -s ' + nologin_path) 232 | # change owner /opt/hyclops & /var/run/hyclops 233 | os.system('chown hyclops:hyclops -R /opt/hyclops/') 234 | os.system('chown hyclops:hyclops /var/run/hyclops/') 235 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | -------------------------------------------------------------------------------- /test/mock_libcloud.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | 19 | class MockNode(object): 20 | 21 | def __init__(self, id, name, state, public_ips, private_ips, 22 | driver, size=None, image=None, extra=None): 23 | self.id = str(id) if id else None 24 | self.name = name 25 | self.state = state 26 | self.public_ips = public_ips if public_ips else [] 27 | self.private_ips = private_ips if private_ips else [] 28 | self.driver = driver 29 | self.size = size 30 | self.image = image 31 | self.extra = extra or {} 32 | 33 | def __repr__(self): 34 | return "MockNode" % ( 35 | self.id, self.name, self.state, self.public_ips, self.private_ips, self.driver, self.size, self.image, self.extra) 36 | 37 | 38 | class MockNodeDriver(object): 39 | 40 | def __init__(self, key, secret=None, secure=True, host=None, port=None, 41 | api_version=None, **kwargs): 42 | pass 43 | 44 | def list_nodes(self, ex_node_ids=None): 45 | return [] 46 | 47 | def destroy_node(self, node): 48 | return True 49 | 50 | def reboot_node(self, node): 51 | return True 52 | 53 | 54 | class MockEC2NodeDriver(MockNodeDriver): 55 | 56 | nodes = [] 57 | images = [] 58 | 59 | @classmethod 60 | def clear_mock(cls): 61 | cls.nodes = [] 62 | cls.images = [] 63 | 64 | @classmethod 65 | def add_mock_node(cls, node): 66 | cls.nodes.append(node) 67 | 68 | @classmethod 69 | def add_mock_image(cls, image): 70 | cls.images.append(image) 71 | 72 | def list_nodes(self, ex_node_ids=None): 73 | return [node for node in self.nodes if ex_node_ids is None or node.id in ex_node_ids] 74 | 75 | def list_images(self, location=None, ex_image_ids=None): 76 | return [image for image in self.images if ex_image_ids is None or image.id in ex_image_ids] 77 | 78 | def ex_start_node(self, node): 79 | return True 80 | 81 | def ex_stop_node(self, node): 82 | return True 83 | 84 | 85 | class MockVSphereNodeDriver(MockNodeDriver): 86 | 87 | nodes = [] 88 | hardware_profiles = [] 89 | 90 | @classmethod 91 | def clear_mock(cls): 92 | cls.nodes = [] 93 | cls.hardware_profiles = [] 94 | 95 | @classmethod 96 | def add_mock_node(cls, node): 97 | cls.nodes.append(node) 98 | 99 | @classmethod 100 | def add_mock_hardware_profile(cls, hardware_profile): 101 | cls.hardware_profiles.append(hardware_profile) 102 | 103 | def list_nodes(self, ex_node_ids=None, ex_vmpath=None): 104 | return [node for node in self.nodes if ex_node_ids is None or node.id in ex_node_ids] 105 | 106 | def ex_start_node(self, node): 107 | return True 108 | 109 | def ex_stop_node(self, node): 110 | return True 111 | 112 | def ex_shutdown_node(self, node): 113 | return True 114 | 115 | def ex_hardware_profiles(self): 116 | return self.hardware_profiles 117 | -------------------------------------------------------------------------------- /test/mock_psphere.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from mock import MagicMock 19 | 20 | 21 | class MockHostSystem(object): 22 | 23 | hosts = [] 24 | 25 | @classmethod 26 | def clear_mock(cls): 27 | cls.hosts = [] 28 | 29 | @classmethod 30 | def add_mock_host(cls, host): 31 | cls.hosts.append(host) 32 | 33 | @classmethod 34 | def all(cls, client): 35 | return cls.hosts 36 | 37 | def __init__(self, params={}): 38 | for key, value in params.items(): 39 | setattr(self, key, value) 40 | 41 | 42 | class MockDatastore(object): 43 | 44 | def __init__(self, params={}): 45 | for key, value in params.items(): 46 | setattr(self, key, value) 47 | 48 | 49 | class MockVirtualMachine(object): 50 | 51 | vms = [] 52 | 53 | @classmethod 54 | def clear_mock(cls): 55 | cls.vms = [] 56 | 57 | @classmethod 58 | def add_mock_vm(cls, vm): 59 | cls.vms.append(vm) 60 | 61 | @classmethod 62 | def all(cls, client): 63 | return cls.vms 64 | 65 | @classmethod 66 | def get(cls, client=None, name=None): 67 | list = [vm for vm in cls.vms if vm.name == name] 68 | if list: 69 | return list[0] 70 | else: 71 | return None 72 | 73 | def __init__(self, params={}): 74 | for key, value in params.items(): 75 | setattr(self, key, value) 76 | 77 | def RebootGuest(self): 78 | pass 79 | 80 | def ShutdownGuest(self): 81 | pass 82 | 83 | def UnregisterVM(self): 84 | pass 85 | 86 | def AnswerVM(self, questionId, answerChoice): 87 | pass 88 | 89 | def PowerOnVM_Task(self): 90 | return MagicMock() 91 | 92 | def PowerOffVM_Task(self): 93 | return MagicMock() 94 | 95 | def SuspendVM_Task(self): 96 | return MagicMock() 97 | -------------------------------------------------------------------------------- /test/mock_zabbix.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | from mock import Mock 19 | from zabbix_api import ZabbixAPIException 20 | 21 | 22 | class MockZabbixAPI(object): 23 | 24 | def __init__(self, server='http://localhost/zabbix', user=None, passwd=None, 25 | log_level=None, timeout=10, r_query_len=10, **kwargs): 26 | self.hostid_seq = 10001 27 | self.interfaceid_seq = 1 28 | self.groupid_seq = 1 29 | self.macroid_seq = 1 30 | self.mock_hosts = [] 31 | self.mock_templates = [] 32 | self.mock_groups = [] 33 | self.mock_macros = [] 34 | 35 | self.host = Mock(**{ 36 | 'get.side_effect': self._host_get, 37 | 'create.side_effect': self._host_create, 38 | 'update.side_effect': self._host_update, 39 | 'delete.side_effect': self._host_delete, 40 | }) 41 | self.hostgroup = Mock(**{ 42 | 'get.side_effect': self._hostgroup_get, 43 | 'create.side_effect': self._hostgroup_create, 44 | }) 45 | self.hostinterface = Mock(**{ 46 | 'create.side_effect': self._hostinterface_create, 47 | 'update.side_effect': self._hostinterface_update, 48 | 'delete.side_effect': self._hostinterface_delete, 49 | }) 50 | self.template = Mock(**{ 51 | 'get.side_effect': self._template_get, 52 | 'create.side_effect': self._template_create, 53 | }) 54 | self.usermacro = Mock(**{ 55 | 'get.side_effect': self._usermacro_get, 56 | 'create.side_effect': self._usermacro_create, 57 | }) 58 | 59 | def login(self, user='', password='', save=True): 60 | if user == 'Admin' and password == 'zabbix': 61 | return True 62 | else: 63 | raise ZabbixAPIException() 64 | 65 | def _host_create(self, params): 66 | host = params 67 | host["hostid"] = self.hostid_seq 68 | self.hostid_seq += 1 69 | if host["interfaces"]: 70 | interfaces = host["interfaces"] 71 | host["interfaces"] = {} 72 | for interface in interfaces: 73 | interface["interfaceid"] = self.interfaceid_seq 74 | index = self.interfaceid_seq 75 | self.interfaceid_seq += 1 76 | interface["hostid"] = host["hostid"] 77 | host["interfaces"][index] = interface 78 | self.mock_hosts.append(host) 79 | return {"hostids": [host["hostid"]]} 80 | 81 | def _template_create(self, params): 82 | template = params 83 | template["templateid"] = self.hostid_seq # use hostid_seq 84 | self.hostid_seq += 1 85 | self.mock_templates.append(template) 86 | return {"templateids": [template["templateid"]]} 87 | 88 | def _hostgroup_create(self, params): 89 | group = params 90 | group["groupid"] = self.groupid_seq 91 | self.groupid_seq += 1 92 | self.mock_groups.append(group) 93 | return {"groupids": [group["groupid"]]} 94 | 95 | def _hostinterface_create(self, params): 96 | interface = params 97 | if interface["hostid"]: 98 | hosts = [host for host in self.mock_hosts if host["hostid"] == interface["hostid"]] 99 | if hosts: 100 | interface["interfaceid"] = self.interfaceid_seq 101 | index = self.interfaceid_seq 102 | self.interfaceid_seq += 1 103 | hosts[0]["interfaces"][index] = interface 104 | return {"interfaceids": [interface["interfaceid"]]} 105 | raise ZabbixAPIException() 106 | 107 | def _usermacro_create(self, params): 108 | macro = params 109 | macro["hostmacroids"] = self.macroid_seq 110 | self.macroid_seq += 1 111 | self.mock_macros.append(macro) 112 | return {"hostmacroids": [macro["hostmacroids"]]} 113 | 114 | def _host_update(self, params): 115 | if "hostid" not in params: 116 | raise ZabbixAPIException("hostid does not specified") 117 | elif params["hostid"] not in [host["hostid"] for host in self.mock_hosts]: 118 | raise ZabbixAPIException("host '%s' does not exist" % params["hostid"]) 119 | host = [host for host in self.mock_hosts if host["hostid"] == params["hostid"]][0] 120 | for key, value in params.items(): 121 | if key == "interfaces": 122 | continue 123 | host[key] = value 124 | return {"hostids": [host["hostid"]]} 125 | 126 | def _hostinterface_update(self, params): 127 | if "interfaceid" not in params: 128 | raise ZabbixAPIException("interfaceid does not specified") 129 | interfaces = [host["interfaces"] for host in self.mock_hosts] 130 | interfaces = reduce(lambda a, b: dict(a.items() + b.items()), interfaces) 131 | if params["interfaceid"] not in interfaces: 132 | raise ZabbixAPIException("interface '%s' does not exist" % params["interfaceid"]) 133 | interface = interfaces[params["interfaceid"]] 134 | for key, value in params.items(): 135 | interface[key] = value 136 | return {"interfaceids": [interface["interfaceid"]]} 137 | 138 | def _host_delete(self, params): 139 | if not isinstance(params, list): 140 | raise ZabbixAPIException("invalid format") 141 | deleted = [] 142 | for hostid in params: 143 | id = hostid["hostid"] if isinstance(hostid, dict) else hostid 144 | host = [host for host in self.mock_hosts if host["hostid"] == id][0] 145 | self.mock_hosts.remove(host) 146 | deleted.append(id) 147 | return {"hostids": deleted} 148 | 149 | def _hostinterface_delete(self, params): 150 | if not isinstance(params, list): 151 | raise ZabbixAPIException("invalid format") 152 | deleted = [] 153 | for host in self.mock_hosts: 154 | interfaces = host["interfaces"] 155 | for interfaceid in params: 156 | if interfaceid in interfaces: 157 | del interfaces[interfaceid] 158 | deleted.append(interfaceid) 159 | return {"interfaceids": deleted} 160 | 161 | def _host_get(self, params): 162 | hosts = self.mock_hosts[:] 163 | if "filter" in params: 164 | if "host" in params["filter"]: 165 | hosts = [host for host in hosts if host["host"] == params["filter"]["host"]] 166 | if "name" in params["filter"]: 167 | hosts = [host for host in hosts if host["name"] == params["filter"]["name"]] 168 | return hosts 169 | 170 | def _template_get(self, params): 171 | templates = self.mock_templates[:] 172 | if "filter" in params: 173 | if "host" in params["filter"]: 174 | templates = [template for template in templates if template["host"] == params["filter"]["host"]] 175 | if "templateids" in params: 176 | templates = [template for template in templates if int(template["templateid"]) in params["templateids"]] 177 | return templates 178 | 179 | def _usermacro_get(self, params): 180 | macros = self.mock_macros[:] 181 | if "globalmacro" in params: 182 | macros = [macro for macro in macros if macro["globalmacro"] == params["globalmacro"]] 183 | if "filter" in params: 184 | if "macro" in params["filter"]: 185 | macros = [macro for macro in macros if macro["macro"] == params["filter"]["macro"]] 186 | return macros 187 | 188 | def _hostgroup_get(self, params): 189 | groups = self.mock_groups[:] 190 | if "filter" in params: 191 | if "name" in params["filter"]: 192 | groups = [group for group in groups if group["name"] == params["filter"]["name"]] 193 | return groups 194 | -------------------------------------------------------------------------------- /test/test.conf: -------------------------------------------------------------------------------- 1 | # ============================ 2 | # User settings 3 | # ============================ 4 | 5 | [zabbix] 6 | zabbix_server = 127.0.0.1 7 | zabbix_port = 10051 8 | frontend_url = http://%(zabbix_server)s/zabbix 9 | zabbix_user = Admin 10 | zabbix_password = zabbix 11 | zabbix_sender = /usr/bin/zabbix_sender 12 | 13 | [ipmi] 14 | ipmitool = /usr/bin/ipmitool 15 | 16 | [logging] 17 | log_level = WARNING 18 | log_file = /tmp/hyclops.log 19 | 20 | [environments] 21 | #http_proxy = http://proxy.example.com:8080/ 22 | #https_proxy = http://proxy.example.com:8080/ 23 | #no_proxy = "localhost,127.0.0.1" 24 | 25 | -------------------------------------------------------------------------------- /test/test_ipmi.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | import os 19 | import unittest 20 | import logging 21 | import subprocess 22 | 23 | import configobj 24 | from mock import patch, Mock 25 | from hyclops.connector.ipmi import IPMIConnector 26 | from .mock_zabbix import MockZabbixAPI 27 | 28 | 29 | class TestIPMIConnector(unittest.TestCase): 30 | 31 | def setUp(self): 32 | logger = logging.getLogger('hyclops.connector.ipmi') 33 | logger.addHandler(logging.NullHandler()) 34 | self.patchers = [ 35 | patch('hyclops.connector.base.ZabbixAPI', new=MockZabbixAPI), 36 | patch('subprocess.check_output') 37 | ] 38 | for patcher in self.patchers: 39 | patcher.start() 40 | config_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "test.conf") 41 | self.config = configobj.ConfigObj(config_path, file_error=True) 42 | self.connector = IPMIConnector(config=self.config) 43 | self.connector.zabbix_api = MockZabbixAPI() 44 | self._set_zabbix_mock() 45 | 46 | def tearDown(self): 47 | for patcher in self.patchers: 48 | patcher.stop() 49 | 50 | def _set_zabbix_mock(self): 51 | zabbix_api = self.connector.zabbix_api 52 | # create IPMI Group 53 | response = zabbix_api.hostgroup.create({ 54 | "name": "IPMI" 55 | }) 56 | groupid_ipmi = response["groupids"][0] 57 | # create IPMI Template 58 | response = zabbix_api.template.create({ 59 | "host": "IPMI", 60 | "groups": [{"groupid": groupid_ipmi}], 61 | "macros": [], 62 | }) 63 | templateid_ipmi = response["templateids"][0] 64 | # create IPMI Host 65 | zabbix_api.host.create({ 66 | "host": "IPMIHost", 67 | "interfaces": [ 68 | {"type": 3, "main": 1, "useip": 1, "ip": "127.0.0.1", "dns": "", "port": 623}, 69 | ], 70 | "ipmi_available": 1, 71 | "ipmi_authtype": 6, 72 | "ipmi_username": "username", 73 | "ipmi_password": "password", 74 | "groups": [{"groupid": groupid_ipmi}], 75 | "parentTemplates": [{"templateid": templateid_ipmi}], 76 | "inventory": [], 77 | "macros": { 78 | "1": {"macro": "{$KEY}", "value": "key"}, 79 | "2": {"macro": "{$SECRET}", "value": "secret"}, 80 | }, 81 | }) 82 | # create NoIPMIInterface Host 83 | zabbix_api.host.create({ 84 | "host": "NoIPMIInterface", 85 | "interfaces": [ 86 | {"type": 1, "main": 1, "useip": 1, "ip": "127.0.0.1", "dns": "", "port": 10050}, 87 | ], 88 | "ipmi_available": 1, 89 | "ipmi_authtype": 6, 90 | "ipmi_username": "username", 91 | "ipmi_password": "password", 92 | "groups": [{"groupid": groupid_ipmi}], 93 | "parentTemplates": [{"templateid": templateid_ipmi}], 94 | "inventory": [], 95 | "macros": { 96 | "1": {"macro": "{$KEY}", "value": "key"}, 97 | "2": {"macro": "{$SECRET}", "value": "secret"}, 98 | }, 99 | }) 100 | 101 | def test_init(self): 102 | self.assertIsInstance(self.connector, IPMIConnector) 103 | self.assertIsInstance(self.connector.zabbix_api, MockZabbixAPI) 104 | self.assertEqual(self.connector.type, "ipmi") 105 | 106 | def test_call(self): 107 | self.connector.logger = Mock(debug=Mock(), warning=Mock(), error=Mock()) 108 | self.connector.run_command = Mock(return_value=True) 109 | self.connector(hostname="IPMIHost", params={}) 110 | self.connector.logger.debug.assert_called() 111 | self.connector.run_command = Mock(return_value=False) 112 | self.connector(hostname="IPMIHost", params={"command": "start"}) 113 | self.connector.logger.warning.assert_called() 114 | self.connector.run_command = Mock(side_effect=RuntimeError()) 115 | self.connector(hostname="not found", params={}) 116 | self.connector.logger.error.assert_called() 117 | 118 | def test_run_command(self): 119 | correct_messages = [ 120 | {"hostname": "IPMIHost", "params": {"command": "monitor"}}, 121 | {"hostname": "IPMIHost", "params": {"command": "reboot"}}, 122 | {"hostname": "IPMIHost", "params": {"command": "start"}}, 123 | {"hostname": "IPMIHost", "params": {"command": "stop"}}, 124 | ] 125 | invalid_messages = [ 126 | {"hostname": "IPMIHost", "params": {"command": "invalid"}}, 127 | {"hostname": "NotExist", "params": {"command": "stop"}}, 128 | ] 129 | self.connector.monitor = Mock(return_value=True) 130 | for message in correct_messages: 131 | self.assertTrue(self.connector.run_command(**message)) 132 | for message in invalid_messages: 133 | self.assertFalse(self.connector.run_command(**message)) 134 | 135 | def test_run_ipmi_tool(self): 136 | with patch('subprocess.Popen.communicate') as m: 137 | m.return_value = ("output", "error") 138 | result = self.connector.run_ipmitool(command="start", ipmi_ip="127.0.0.1", key="username", secret="password") 139 | self.assertEqual(result, "output") 140 | m.side_effect = subprocess.CalledProcessError(returncode=1, cmd="ipmitool") 141 | result = self.connector.run_ipmitool(command="start", ipmi_ip="127.0.0.1", key="username", secret="password") 142 | self.assertIsNone(result) 143 | 144 | def test_get_connection_parameters(self): 145 | result = self.connector.get_connection_parameters(hostname="IPMIHost") 146 | self.assertDictEqual(result, {"ipmi_ip": "127.0.0.1", "key": "username", "secret": "password"}) 147 | result = self.connector.get_connection_parameters(hostname="NoIPMIInterface") 148 | self.assertIsNone(result) 149 | result = self.connector.get_connection_parameters(hostname="not found") 150 | self.assertIsNone(result) 151 | 152 | def test_monitor(self): 153 | self.connector.zabbix_sender = Mock(return_value=True) 154 | self.connector.run_ipmitool = Mock(return_value="Chassis Power is on") 155 | result = self.connector.monitor(hostname="IPMIHost", conn_params={"ipmi_ip": "127.0.0.1", "key": "username", "secret": "password"}) 156 | self.assertTrue(result) 157 | self.connector.run_ipmitool = Mock(return_value="Chassis Power is off") 158 | result = self.connector.monitor(hostname="IPMIHost", conn_params={"ipmi_ip": "127.0.0.1", "key": "username", "secret": "password"}) 159 | self.assertTrue(result) 160 | self.connector.run_ipmitool = Mock(return_value="") 161 | result = self.connector.monitor(hostname="IPMIHost", conn_params={"ipmi_ip": "127.0.0.1", "key": "username", "secret": "password"}) 162 | self.assertFalse(result) 163 | 164 | if __name__ == '__main__': 165 | unittest.main() 166 | -------------------------------------------------------------------------------- /test/test_libcloud_vsphere.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | 19 | import sys 20 | import unittest 21 | from mock import patch, Mock, MagicMock 22 | from libcloud.compute.base import Node 23 | from hyclops.libcloud_driver.vsphere import VSphereNodeDriver 24 | from .mock_psphere import MockVirtualMachine, MockDatastore, MockHostSystem 25 | 26 | 27 | class VSphereTests(unittest.TestCase): 28 | 29 | def setUp(self): 30 | self.patchers = [] 31 | self.patchers.append(patch('hyclops.libcloud_driver.vsphere.Client')) 32 | self.patchers.append(patch('hyclops.libcloud_driver.vsphere.HostSystem', new=MockHostSystem)) 33 | for patcher in self.patchers: 34 | patcher.start() 35 | self.driver = VSphereNodeDriver(key="key", secret="secret", host="127.0.0.1") 36 | self._set_libcloud_mock() 37 | 38 | def tearDown(self): 39 | for patcher in self.patchers: 40 | patcher.stop() 41 | 42 | def _set_libcloud_mock(self): 43 | MockHostSystem.clear_mock() 44 | MockVirtualMachine.clear_mock() 45 | vm1 = MockVirtualMachine({ 46 | "name": "vm1", 47 | "summary": Mock(**{ 48 | "quickStats.overallCpuUsage": 100, 49 | "quickStats.guestMemoryUsage": 300, 50 | "config.numCpu": 1, 51 | "config.memorySizeMB": 2048, 52 | "config.vmPathName": "[datastore1] /foo/bar.vmx", 53 | "config.guestFullName": "CentOS 4/5/6 (64bit)", 54 | "runtime.maxCpuUsage": 2000}), 55 | "runtime": Mock(**{ 56 | "powerState": "poweredOn", 57 | "question": Mock(spec=['id', 'choice', 'text'], 58 | **{'id': 1, 59 | 'choice.choiceInfo': [Mock(**{'key': 1, 'label': 'choice1', 'summary': 'choice1'})], 60 | 'text': 'question message'})}), 61 | "config": Mock(**{ 62 | "uuid": "vsphere_uuid", 63 | "extraConfig": [MagicMock()]}), 64 | "guest": Mock(**{ 65 | "toolsRunningStatus": "guestToolsRunning", 66 | "toolsVersionStatus": "toolsVersionCurrent", 67 | "net": [MagicMock()]}) 68 | }) 69 | vm2 = MockVirtualMachine({ 70 | "name": "vm2", 71 | "summary": Mock(**{ 72 | "quickStats.overallCpuUsage": 100, 73 | "quickStats.guestMemoryUsage": 300, 74 | "config.numCpu": 1, 75 | "config.memorySizeMB": 2048, 76 | "config.vmPathName": "[datastore1] /foo/bar.vmx", 77 | "config.guestFullName": "CentOS 4/5/6 (64bit)", 78 | "runtime.maxCpuUsage": 2000}), 79 | "runtime": Mock(**{ 80 | "powerState": "poweredOn", 81 | "question": Mock(spec=['id', 'choice', 'message'], 82 | **{'id': 1, 83 | 'choice.choiceInfo': [Mock(**{'key': 1, 'label': 'choice1', 'summary': 'choice1'})], 84 | 'message': [Mock(text="question message"), Mock(text="with line feed")]})}), 85 | "config": Mock(**{ 86 | "uuid": "vsphere_uuid", 87 | "extraConfig": [MagicMock()]}), 88 | "guest": Mock(**{ 89 | "toolsRunningStatus": "guestToolsRunning", 90 | "toolsVersionStatus": "toolsVersionCurrent", 91 | "net": [Mock(spec=["ipAddress"], **{"ipAddress": ["127.0.0.1"]})]}), 92 | }) 93 | vm3 = MockVirtualMachine({ 94 | "name": "vm3", 95 | "summary": Mock(**{ 96 | "quickStats.overallCpuUsage": 100, 97 | "quickStats.guestMemoryUsage": 300, 98 | "config.numCpu": 1, 99 | "config.memorySizeMB": 2048, 100 | "config.vmPathName": "[datastore1] /foo/bar.vmx", 101 | "config.guestFullName": "CentOS 4/5/6 (64bit)", 102 | "runtime.maxCpuUsage": 2000}), 103 | "runtime": Mock(spec=["powerState"], **{"powerState": "poweredOn"}), 104 | "config": Mock(**{ 105 | "uuid": "vsphere_uuid", 106 | "extraConfig": [MagicMock()]}), 107 | "guest": Mock(spec=["toolsRunningStatus", "toolsVersionStatus", "ipAddress"], **{ 108 | "toolsRunningStatus": "guestToolsRunning", 109 | "toolsVersionStatus": "toolsVersionCurrent", 110 | "ipAddress": "127.0.0.1"}), 111 | }) 112 | vm4 = MockVirtualMachine({ 113 | "name": "vm4", 114 | "summary": Mock(**{ 115 | "quickStats.overallCpuUsage": 100, 116 | "quickStats.guestMemoryUsage": 300, 117 | "config.numCpu": 1, 118 | "config.memorySizeMB": 2048, 119 | "config.vmPathName": "[datastore1] /foo/bar.vmx", 120 | "config.guestFullName": "CentOS 4/5/6 (64bit)", 121 | "runtime.maxCpuUsage": 2000}), 122 | "runtime": Mock(**{ 123 | "powerState": "poweredOn", 124 | "question": Mock(spec=['id', 'choice', 'text'], 125 | **{'id': 1, 126 | 'choice.choiceInfo': [Mock(**{'key': 1, 'label': 'choice1', 'summary': 'choice1'})], 127 | 'text': 'question message'})}), 128 | "config": Mock(**{ 129 | "uuid": "vsphere_uuid", 130 | "extraConfig": [MagicMock()]}), 131 | "guest": Mock(**{ 132 | "toolsRunningStatus": "guestToolsRunning", 133 | "toolsVersionStatus": "toolsVersionCurrent", 134 | "net": [MagicMock()]}), 135 | }) 136 | MockVirtualMachine.add_mock_vm(vm1) 137 | MockVirtualMachine.add_mock_vm(vm2) 138 | MockVirtualMachine.add_mock_vm(vm3) 139 | MockVirtualMachine.add_mock_vm(vm4) 140 | self.vm = vm1 141 | self.host = MockHostSystem({ 142 | "name": "host name", 143 | "datastore": [ 144 | MockDatastore({ 145 | "name": "datastore name", 146 | "summary": Mock(**{"freeSpace": 400 * 1024 ** 3, 147 | "capacity": 800 * 1024 ** 3, 148 | "type": "nfs"}) 149 | }) 150 | ], 151 | "summary": Mock(**{ 152 | 'hardware.uuid': "hardware_uuid", 153 | 'hardware.cpuMhz': 2000, 154 | 'hardware.numCpuCores': 8, 155 | 'quickStats.overallCpuUsage': 300, 156 | 'quickStats.overallMemoryUsage': 1000}), 157 | "hardware": Mock(**{ 158 | 'cpuInfo.numCpuThreads': 16, 159 | 'memorySize': 16 * 1024 ** 3}), 160 | "vm": [vm1, vm2, vm3] # not include vm4 161 | }) 162 | MockHostSystem.add_mock_host(self.host) 163 | self.node = Node( 164 | id="vsphere_uuid", 165 | name="vm1", 166 | state=0, 167 | public_ips=[], 168 | private_ips=[], 169 | driver=self.driver, 170 | extra={ 171 | 'managedObjectReference': self.vm, 172 | 'status': 'running', 173 | 'cpu': 1, 174 | 'cpu_usage': 5.0, 175 | 'memory': 2048 * 1024 ** 2, 176 | 'memory_usage': 300 * 1024 ** 2, 177 | 'toolsRunningStatus': 'guestToolsRunning', 178 | 'toolsVersionStatus': 'toolsVersionCurrent', 179 | 'vmpath': '[datastore1] /foo/bar.vmx', 180 | 'stuck_state': 1, 181 | 'stuck_question_id': 1, 182 | 'stuck_question': "question message", 183 | 'stuck_choices': [{'label': 'choice1', 'key': 1, 'summary': 'choice1'}], 184 | 'platform': "CentOS 4/5/6 (64bit)", 185 | } 186 | ) 187 | 188 | def test_to_node(self): 189 | # pattern 1 190 | vm = MockVirtualMachine.get(name="vm1") 191 | expect = self.node 192 | node = self.driver._to_node(vm) 193 | self.assertEqual(expect.id, node.id) 194 | self.assertEqual(expect.name, node.name) 195 | self.assertEqual(expect.state, node.state) 196 | self.assertEqual(expect.public_ips, node.public_ips) 197 | self.assertEqual(expect.private_ips, node.private_ips) 198 | self.assertEqual(expect.driver, node.driver) 199 | self.assertDictEqual(expect.extra, node.extra) 200 | # pattern 2 201 | vm = MockVirtualMachine.get(name="vm2") 202 | node = self.driver._to_node(vm) 203 | self.assertEqual(node.extra["stuck_question"], "Message: question message\nwith line feed\n") 204 | self.assertListEqual(node.public_ips, ["127.0.0.1"]) 205 | # pattern 3 206 | vm = MockVirtualMachine.get(name="vm3") 207 | node = self.driver._to_node(vm) 208 | self.assertEqual(node.extra["stuck_state"], 0) 209 | self.assertListEqual(node.public_ips, ["127.0.0.1"]) 210 | 211 | def test_to_hardware_profile(self): 212 | expect = { 213 | 'id': 'hardware_uuid', 214 | 'name': 'host name', 215 | 'cpu': 16, 216 | 'cpu_assigned': 1 * len(self.host.vm), 217 | 'cpu_usage': 1.875, 218 | 'memory': 16 * 1024 ** 3, 219 | 'memory_assigned': 2 * 1024 ** 3 * len(self.host.vm), 220 | 'memory_usage': 1000 * 1024 ** 2, 221 | 'datastores': [{'name': 'datastore name', 'freeSpace': 400 * 1024 ** 3, 'capacity': 800 * 1024 ** 3, 'type': 'nfs'}] 222 | } 223 | hardware_profile = self.driver._to_hardware_profile(self.host) 224 | self.assertDictEqual(expect, hardware_profile) 225 | 226 | def test_list_nodes(self): 227 | nodes = self.driver.list_nodes() 228 | self.assertIsInstance(nodes, list) 229 | self.assertEqual(len(nodes), len(self.host.vm)) 230 | self.assertIsInstance(nodes[0], Node) 231 | self.assertEqual(nodes[0].driver, self.driver) 232 | # duplicate uuid pattern 233 | self.host.vm.append(MockVirtualMachine.get(name="vm4")) 234 | with self.assertRaises(Exception): 235 | self.driver.list_nodes(ex_node_ids="vsphere_uuid") 236 | 237 | def test_reboot_node(self): 238 | result = self.driver.reboot_node(self.node) 239 | self.assertTrue(result) 240 | self.vm.RebootGuest = Mock(side_effect=RuntimeError()) 241 | result = self.driver.reboot_node(self.node) 242 | self.assertFalse(result) 243 | 244 | def test_destroy_node(self): 245 | result = self.driver.destroy_node(self.node) 246 | self.assertTrue(result) 247 | self.vm.UnregisterVM = Mock(side_effect=RuntimeError()) 248 | result = self.driver.destroy_node(self.node) 249 | self.assertFalse(result) 250 | 251 | def test_ex_start_node(self): 252 | result = self.driver.ex_start_node(self.node) 253 | self.assertTrue(result) 254 | self.vm.PowerOnVM_Task = Mock(side_effect=RuntimeError()) 255 | result = self.driver.ex_start_node(self.node) 256 | self.assertFalse(result) 257 | 258 | def test_ex_stop_node(self): 259 | result = self.driver.ex_stop_node(self.node) 260 | self.assertTrue(result) 261 | self.vm.PowerOffVM_Task = Mock(side_effect=RuntimeError()) 262 | result = self.driver.ex_stop_node(self.node) 263 | self.assertFalse(result) 264 | 265 | def test_ex_shutdown_node(self): 266 | result = self.driver.ex_shutdown_node(self.node) 267 | self.assertTrue(result) 268 | self.vm.ShutdownGuest = Mock(side_effect=RuntimeError()) 269 | result = self.driver.ex_shutdown_node(self.node) 270 | self.assertFalse(result) 271 | 272 | def test_ex_suspend_node(self): 273 | result = self.driver.ex_suspend_node(self.node) 274 | self.assertTrue(result) 275 | self.vm.SuspendVM_Task = Mock(side_effect=RuntimeError()) 276 | result = self.driver.ex_suspend_node(self.node) 277 | self.assertFalse(result) 278 | 279 | def test_ex_answer_node(self): 280 | result = self.driver.ex_answer_node(self.node, 1) 281 | self.assertTrue(result) 282 | question = self.vm.runtime.question 283 | del self.vm.runtime.question 284 | result = self.driver.ex_answer_node(self.node, 1) 285 | self.assertFalse(result) 286 | self.vm.runtime.question = question 287 | self.vm.AnswerVM = Mock(side_effect=RuntimeError()) 288 | result = self.driver.ex_answer_node(self.node, 1) 289 | self.assertFalse(result) 290 | 291 | def test_ex_hardware_profiles(self): 292 | hardware_profiles = self.driver.ex_hardware_profiles() 293 | self.assertIsInstance(hardware_profiles, list) 294 | self.assertEqual(len(hardware_profiles), 1) 295 | self.assertIsInstance(hardware_profiles[0], dict) 296 | self.assertEqual(hardware_profiles[0]["id"], "hardware_uuid") 297 | 298 | 299 | if __name__ == '__main__': 300 | sys.exit(unittest.main()) 301 | -------------------------------------------------------------------------------- /test/test_queue.py: -------------------------------------------------------------------------------- 1 | # HyClops for Zabbix 2 | # Copyright 2013 TIS Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | 19 | import unittest 20 | import logging 21 | import threading 22 | import time 23 | import configobj 24 | from mock import Mock, patch 25 | from hyclops.queue import MessageQueue 26 | 27 | 28 | class TestMessageQueue(unittest.TestCase): 29 | 30 | def setUp(self): 31 | logger = logging.getLogger('queue') 32 | logger.addHandler(logging.NullHandler()) 33 | self.patchers = [ 34 | patch('hyclops.connector.ec2.EC2Connector.__new__'), 35 | patch('hyclops.connector.vsphere.VSphereConnector.__new__'), 36 | patch('hyclops.connector.ipmi.IPMIConnector.__new__'), 37 | ] 38 | for patcher in self.patchers: 39 | patcher.start() 40 | self.queue = MessageQueue(config=configobj.ConfigObj("test/conf/test.conf")) 41 | 42 | def tearDown(self): 43 | for patcher in self.patchers: 44 | patcher.stop() 45 | 46 | def test_init(self): 47 | self.assertIsInstance(self.queue.logger, logging.Logger) 48 | self.assertListEqual(self.queue.threads, []) 49 | self.assertIsNotNone(self.queue.connectors["ec2"]) 50 | self.assertIsNotNone(self.queue.connectors["vsphere"]) 51 | self.assertIsNotNone(self.queue.connectors["ipmi"]) 52 | self.assertIsNone(self.queue.socket) 53 | 54 | def test_recv_message(self): 55 | test_data = [ 56 | {"config": {"recv_pyobj.return_value": "bad_message"}, 57 | "expect": None}, 58 | {"config": {"recv_pyobj.return_value": ["ec2", "AWSAccount", "bad params"]}, 59 | "expect": {"driver": "ec2", "zabbix_hostname": "AWSAccount", "params": {}}}, 60 | {"config": {"recv_pyobj.return_value": ["ec2", "AWSAccount", '{"command":"monitor"}']}, 61 | "expect": {"driver": "ec2", "zabbix_hostname": "AWSAccount", "params": {"command": "monitor"}}}, 62 | {"config": {"recv_pyobj.return_value": ["ec2", "AWSAccount"]}, 63 | "expect": {"driver": "ec2", "zabbix_hostname": "AWSAccount", "params": {}}}, 64 | ] 65 | # queue is not opened 66 | self.assertIsNone(self.queue._recv_message()) 67 | for data in test_data: 68 | self.queue.socket = Mock(**data["config"]) 69 | if data["expect"] is None: 70 | self.assertIsNone(self.queue._recv_message()) 71 | else: 72 | self.assertDictEqual(self.queue._recv_message(), data["expect"]) 73 | 74 | def test_bind(self): 75 | with patch("zmq.Context") as m: 76 | # bind success 77 | mock_socket = Mock(**{"setsockopt.return_value": True, "bind.return_value": True}) 78 | m.return_value = Mock(**{"socket.return_value": mock_socket}) 79 | self.queue.bind() 80 | self.assertEqual(self.queue.socket, mock_socket) 81 | # bind failure 82 | mock_socket = Mock(**{"setsockopt.return_value": True, "bind.side_effect": Exception()}) 83 | m.return_value = Mock(**{"socket.return_value": mock_socket}) 84 | with self.assertRaises(Exception): 85 | self.queue.bind() 86 | 87 | def test_poll(self): 88 | with patch("hyclops.queue.MessageQueue._recv_message") as m: 89 | # message does not exist 90 | m.return_value = None 91 | self.assertIsNone(self.queue.poll()) 92 | self.assertListEqual(self.queue.threads, []) 93 | # driver does not exist 94 | m.return_value = {"driver": "not exist", "zabbix_hostname": "", "params": {}} 95 | self.assertIsNone(self.queue.poll()) 96 | self.assertListEqual(self.queue.threads, []) 97 | # receive correct message 98 | mock_connector = Mock(side_effect=(lambda hostname, params: time.sleep(1))) 99 | self.queue.connectors["ec2"] = mock_connector 100 | self.queue.connectors["vsphere"] = mock_connector 101 | self.queue.connectors["ipmi"] = mock_connector 102 | test_messages = [ 103 | {"driver": "ec2", "zabbix_hostname": "Hostname", "params": {}}, 104 | {"driver": "vsphere", "zabbix_hostname": "Hostname", "params": {}}, 105 | {"driver": "ipmi", "zabbix_hostname": "Hostname", "params": {}}, 106 | {"driver": "ipmi", "zabbix_hostname": "Hostname", "params": {}}, 107 | ] 108 | for test_message in test_messages: 109 | m.return_value = test_message 110 | self.queue.poll() 111 | thread_name = test_message["driver"] + "-" + test_message["zabbix_hostname"] + "-" + test_message["params"].get("command", "monitor") 112 | self.assertTrue(thread_name in [thread.name for thread in self.queue.threads]) 113 | self.assertEqual(len(self.queue.threads), 3) 114 | time.sleep(1) 115 | m.return_value = {"driver": "unknown", "zabbix_hostname": "unknown", "params": {}} 116 | self.queue.poll() 117 | self.assertTrue(len(self.queue.threads) < 3) 118 | 119 | def test_close(self): 120 | # queue is already closed 121 | self.queue.close() 122 | self.assertIsNone(self.queue.socket) 123 | self.assertEqual(len([thread for thread in self.queue.threads if thread.isAlive()]), 0) 124 | # close running queue 125 | self.queue.socket = Mock(**{"close.return_value": True}) 126 | mock_connector = Mock(side_effect=(lambda: time.sleep(10))) 127 | self.queue.threads.append(threading.Thread(target=mock_connector)) 128 | self.queue.close() 129 | self.assertIsNone(self.queue.socket) 130 | self.assertEqual(len([thread for thread in self.queue.threads if thread.isAlive()]), 0) 131 | 132 | 133 | if __name__ == '__main__': 134 | unittest.main() 135 | --------------------------------------------------------------------------------