diff options
Diffstat (limited to 'frontends/php/include/classes/api/services/CScript.php')
-rw-r--r-- | frontends/php/include/classes/api/services/CScript.php | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/frontends/php/include/classes/api/services/CScript.php b/frontends/php/include/classes/api/services/CScript.php new file mode 100644 index 00000000000..fa658a2fe3b --- /dev/null +++ b/frontends/php/include/classes/api/services/CScript.php @@ -0,0 +1,705 @@ +<?php +/* +** Zabbix +** Copyright (C) 2001-2014 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + + +/** + * Class containing methods for operations with scripts. + * + * @package API + */ +class CScript extends CApiService { + + protected $tableName = 'scripts'; + protected $tableAlias = 's'; + protected $sortColumns = array('scriptid', 'name'); + + /** + * Get scripts data. + * + * @param array $options + * @param array $options['itemids'] + * @param array $options['hostids'] deprecated (very slow) + * @param array $options['groupids'] + * @param array $options['triggerids'] + * @param array $options['scriptids'] + * @param bool $options['status'] + * @param bool $options['editable'] + * @param bool $options['count'] + * @param string $options['pattern'] + * @param int $options['limit'] + * @param string $options['order'] + * + * @return array + */ + public function get(array $options) { + $result = array(); + $userType = self::$userData['type']; + $userid = self::$userData['userid']; + + $sqlParts = array( + 'select' => array('scripts' => 's.scriptid'), + 'from' => array('scripts s'), + 'where' => array(), + 'order' => array(), + 'limit' => null + ); + + $defOptions = array( + 'groupids' => null, + 'hostids' => null, + 'scriptids' => null, + 'usrgrpids' => null, + 'editable' => null, + 'nopermissions' => null, + // filter + 'filter' => null, + 'search' => null, + 'searchByAny' => null, + 'startSearch' => null, + 'excludeSearch' => null, + 'searchWildcardsEnabled'=> null, + // output + 'output' => API_OUTPUT_EXTEND, + 'selectGroups' => null, + 'selectHosts' => null, + 'countOutput' => null, + 'preservekeys' => null, + 'sortfield' => '', + 'sortorder' => '', + 'limit' => null + ); + $options = zbx_array_merge($defOptions, $options); + + // editable + permission check + if ($userType != USER_TYPE_SUPER_ADMIN) { + if (!is_null($options['editable'])) { + return $result; + } + + $userGroups = getUserGroupsByUserId($userid); + + $sqlParts['where'][] = '(s.usrgrpid IS NULL OR '.dbConditionInt('s.usrgrpid', $userGroups).')'; + $sqlParts['where'][] = '(s.groupid IS NULL OR EXISTS ('. + 'SELECT NULL'. + ' FROM rights r'. + ' WHERE s.groupid=r.id'. + ' AND '.dbConditionInt('r.groupid', $userGroups). + ' GROUP BY r.id'. + ' HAVING MIN(r.permission)>'.PERM_DENY. + '))'; + } + + // groupids + if (!is_null($options['groupids'])) { + zbx_value2array($options['groupids']); + + $sqlParts['where'][] = '(s.groupid IS NULL OR '.dbConditionInt('s.groupid', $options['groupids']).')'; + } + + // hostids + if (!is_null($options['hostids'])) { + zbx_value2array($options['hostids']); + + // return scripts that are assigned to the hosts' groups or to no group + $hostGroups = API::HostGroup()->get(array( + 'output' => array('groupid'), + 'hostids' => $options['hostids'] + )); + $hostGroupIds = zbx_objectValues($hostGroups, 'groupid'); + + $sqlParts['where'][] = '('.dbConditionInt('s.groupid', $hostGroupIds).' OR s.groupid IS NULL)'; + } + + // usrgrpids + if (!is_null($options['usrgrpids'])) { + zbx_value2array($options['usrgrpids']); + + $sqlParts['where'][] = '(s.usrgrpid IS NULL OR '.dbConditionInt('s.usrgrpid', $options['usrgrpids']).')'; + } + + // scriptids + if (!is_null($options['scriptids'])) { + zbx_value2array($options['scriptids']); + + $sqlParts['where'][] = dbConditionInt('s.scriptid', $options['scriptids']); + } + + // search + if (is_array($options['search'])) { + zbx_db_search('scripts s', $options, $sqlParts); + } + + // filter + if (is_array($options['filter'])) { + $this->dbFilter('scripts s', $options, $sqlParts); + } + + // limit + if (zbx_ctype_digit($options['limit']) && $options['limit']) { + $sqlParts['limit'] = $options['limit']; + } + + $sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); + $sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts); + $res = DBselect($this->createSelectQueryFromParts($sqlParts), $sqlParts['limit']); + while ($script = DBfetch($res)) { + if ($options['countOutput']) { + $result = $script['rowscount']; + } + else { + $result[$script['scriptid']] = $script; + } + } + + if (!is_null($options['countOutput'])) { + return $result; + } + + if ($result) { + $result = $this->addRelatedObjects($options, $result); + $result = $this->unsetExtraFields($result, array('groupid', 'host_access'), $options['output']); + } + + // removing keys (hash -> array) + if (is_null($options['preservekeys'])) { + $result = zbx_cleanHashes($result); + } + + return $result; + } + + /** + * Add scripts. + * + * @param array $scripts + * @param array $scripts['name'] + * @param array $scripts['hostid'] + * + * @return array + */ + public function create(array $scripts) { + $scripts = zbx_toArray($scripts); + + $this->validateCreate($scripts); + + $scripts = $this->trimMenuPath($scripts); + + $this->validateMenuPath($scripts, __FUNCTION__); + + $scripts = $this->unsetExecutionType($scripts); + + $scriptIds = DB::insert('scripts', $scripts); + + return array('scriptids' => $scriptIds); + } + + /** + * Update scripts. + * + * @param array $scripts + * @param array $scripts['name'] + * @param array $scripts['hostid'] + * + * @return array + */ + public function update(array $scripts) { + $scripts = zbx_toArray($scripts); + + $this->validateUpdate($scripts); + + $scripts = $this->trimMenuPath($scripts); + + $this->validateMenuPath($scripts, __FUNCTION__); + + $scripts = $this->unsetExecutionType($scripts); + + $update = array(); + + foreach ($scripts as $script) { + $scriptId = $script['scriptid']; + unset($script['scriptid']); + + $update[] = array( + 'values' => $script, + 'where' => array('scriptid' => $scriptId) + ); + } + + DB::update('scripts', $update); + + return array('scriptids' => zbx_objectValues($scripts, 'scriptid')); + } + + /** + * Delete scripts. + * + * @param array $scriptIds + * + * @return array + */ + public function delete(array $scriptIds) { + $this->validateDelete($scriptIds); + + DB::delete('scripts', array('scriptid' => $scriptIds)); + + return array('scriptids' => $scriptIds); + } + + /** + * Execute script. + * + * @param string $data['scriptid'] + * @param string $data['hostid'] + * + * @return array + */ + public function execute(array $data) { + global $ZBX_SERVER, $ZBX_SERVER_PORT; + + $scriptId = $data['scriptid']; + $hostId = $data['hostid']; + + $scripts = $this->get(array( + 'hostids' => $hostId, + 'scriptids' => $scriptId, + 'output' => array('scriptid'), + 'preservekeys' => true + )); + + if (!isset($scripts[$scriptId])) { + self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); + } + + // execute script + $zabbixServer = new CZabbixServer($ZBX_SERVER, $ZBX_SERVER_PORT, ZBX_SCRIPT_TIMEOUT, ZBX_SOCKET_BYTES_LIMIT); + $result = $zabbixServer->executeScript($scriptId, $hostId); + if ($result !== false) { + // return the result in a backwards-compatible format + return array( + 'response' => 'success', + 'value' => $result + ); + } + else { + self::exception(ZBX_API_ERROR_INTERNAL, $zabbixServer->getError()); + } + } + + /** + * Returns all the scripts that are available on each given host. + * + * @param $hostIds + * + * @return array (an array of scripts in the form of array($hostId => array($script1, $script2, ...), ...) ) + */ + public function getScriptsByHosts($hostIds) { + zbx_value2array($hostIds); + + $scriptsByHost = array(); + + if (!$hostIds) { + return $scriptsByHost; + } + + foreach ($hostIds as $hostId) { + $scriptsByHost[$hostId] = array(); + } + + $scripts = $this->get(array( + 'output' => API_OUTPUT_EXTEND, + 'selectHosts' => array('hostid'), + 'hostids' => $hostIds, + 'sortfield' => 'name', + 'preservekeys' => true + )); + + if ($scripts) { + // resolve macros + $macrosData = array(); + foreach ($scripts as $scriptId => $script) { + if (!empty($script['confirmation'])) { + foreach ($script['hosts'] as $host) { + if (isset($scriptsByHost[$host['hostid']])) { + $macrosData[$host['hostid']][$scriptId] = $script['confirmation']; + } + } + } + } + if ($macrosData) { + $macrosData = CMacrosResolverHelper::resolve(array( + 'config' => 'scriptConfirmation', + 'data' => $macrosData + )); + } + + foreach ($scripts as $scriptId => $script) { + $hosts = $script['hosts']; + unset($script['hosts']); + // set script to host + foreach ($hosts as $host) { + $hostId = $host['hostid']; + + if (isset($scriptsByHost[$hostId])) { + $size = count($scriptsByHost[$hostId]); + $scriptsByHost[$hostId][$size] = $script; + + // set confirmation text with resolved macros + if (isset($macrosData[$hostId][$scriptId]) && $script['confirmation']) { + $scriptsByHost[$hostId][$size]['confirmation'] = $macrosData[$hostId][$scriptId]; + } + } + } + } + } + + return $scriptsByHost; + } + + /** + * Validates the input parameters for the create() method. + * + * @throws APIException if the input is invalid + * + * @param array $scripts + */ + protected function validateCreate(array $scripts) { + if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { + self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); + } + + $dbFields = array('command' => null, 'name' => null); + $names = array(); + + foreach ($scripts as $script) { + if (!check_db_fields($dbFields, $script)) { + self::exception(ZBX_API_ERROR_PARAMETERS, _('Wrong fields for script.')); + } + if (zbx_empty($script['name'])) { + self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty name for script.')); + } + if (isset($names[$script['name']])) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Duplicate script name "%1$s".', $script['name'])); + } + + $names[$script['name']] = $script['name']; + } + + $dbScripts = $this->get(array( + 'output' => array('name'), + 'filter' => array('name' => $names), + 'nopermissions' => true, + 'limit' => 1 + )); + if ($dbScript = reset($dbScripts)) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Script "%1$s" already exists.', $dbScript['name'])); + } + } + + /** + * Validates the input parameters for the update() method. + * + * @throws APIException if the input is invalid + * + * @param array $scripts + */ + protected function validateUpdate(array $scripts) { + if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { + self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); + } + + foreach ($scripts as $script) { + if (empty($script['scriptid'])) { + self::exception(ZBX_API_ERROR_PARAMETERS, _('Invalid method parameters.')); + } + } + + $scripts = zbx_toHash($scripts, 'scriptid'); + $scriptIds = array_keys($scripts); + + $names = array(); + + $dbScripts = $this->get(array( + 'scriptids' => $scriptIds, + 'output' => array('scriptid'), + 'preservekeys' => true + )); + + foreach ($scripts as $script) { + if (!isset($dbScripts[$script['scriptid']])) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Script with scriptid "%1$s" does not exist.', $script['scriptid'])); + } + + if (isset($script['name'])) { + if (zbx_empty($script['name'])) { + self::exception(ZBX_API_ERROR_PARAMETERS, _('Empty name for script.')); + } + if (isset($names[$script['name']])) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Duplicate script name "%1$s".', $script['name'])); + } + + $names[$script['name']] = $script['name']; + } + } + + if ($names) { + $dbScripts = $this->get(array( + 'output' => array('scriptid', 'name'), + 'filter' => array('name' => $names), + 'nopermissions' => true, + 'preservekeys' => true + )); + + foreach ($dbScripts as $dbScript) { + if (!isset($scripts[$dbScript['scriptid']]) + || bccomp($scripts[$dbScript['scriptid']]['scriptid'], $dbScript['scriptid']) != 0) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Script "%1$s" already exists.', $dbScript['name'])); + } + } + } + } + + /** + * Validates the input parameters for the delete() method. + * + * @throws APIException if the input is invalid + * + * @param array $scriptIds + */ + protected function validateDelete(array $scriptIds) { + if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { + self::exception(ZBX_API_ERROR_PERMISSIONS, _('You do not have permission to perform this operation.')); + } + + if (empty($scriptIds)) { + self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete scripts. Empty input parameter "scriptids".')); + } + + $dbScripts = $this->get(array( + 'scriptids' => $scriptIds, + 'editable' => true, + 'output' => array('name'), + 'preservekeys' => true + )); + + foreach ($scriptIds as $scriptId) { + if (isset($dbScripts[$scriptId])) { + continue; + } + + self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Cannot delete scripts. Script with scriptid "%1$s" does not exist.', $scriptId)); + } + + $actions = API::Action()->get(array( + 'scriptids' => $scriptIds, + 'nopermissions' => true, + 'preservekeys' => true, + 'output' => array('actionid', 'name'), + 'selectOperations' => array('opcommand') + )); + + foreach ($actions as $action) { + foreach ($action['operations'] as $operation) { + if (isset($operation['opcommand']['scriptid']) + && $operation['opcommand']['scriptid'] + && in_array($operation['opcommand']['scriptid'], $scriptIds)) { + self::exception(ZBX_API_ERROR_PARAMETERS, + _s('Cannot delete scripts. Script "%1$s" is used in action operation "%2$s".', + $dbScripts[$operation['opcommand']['scriptid']]['name'], $action['name'] + ) + ); + } + } + } + } + + /** + * Validates script name menu path. + * + * @throws APIException if the input is invalid + * + * @param array $scripts + * @param string $method + */ + protected function validateMenuPath(array $scripts, $method) { + $dbScripts = $this->get(array( + 'output' => array('scriptid', 'name'), + 'nopermissions' => true + )); + + foreach ($scripts as $script) { + if (!isset($script['name'])) { + continue; + } + + $folders = $path = splitPath($script['name']); + $name = array_pop($folders); + + // menu1/menu2/{empty} + if (zbx_empty($name)) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Empty name for script "%1$s".', $script['name'])); + } + + // menu1/{empty}/name + foreach ($folders as $folder) { + if (zbx_empty($folder)) { + self::exception(ZBX_API_ERROR_PARAMETERS, + _s('Incorrect menu path for script "%1$s".', $script['name'])); + } + } + + // validate path + foreach ($dbScripts as $dbScript) { + if ($method == 'update' && $script['scriptid'] === $dbScript['scriptid']) { + continue; + } + + $dbScriptFolders = $dbScriptPath = splitPath($dbScript['name']); + array_pop($dbScriptFolders); + + // script NAME cannot be a FOLDER for other scripts + $dbScriptFolderItems = array(); + foreach ($dbScriptFolders as $dbScriptFolder) { + $dbScriptFolderItems[] = $dbScriptFolder; + + if ($path === $dbScriptFolderItems) { + self::exception(ZBX_API_ERROR_PARAMETERS, + _s('Script name "%1$s" already used in menu path for script "%2$s".', + $script['name'], $dbScript['name']) + ); + } + } + + // script FOLDER cannot be a NAME for other scripts + $folderItems = array(); + foreach ($folders as $folder) { + $folderItems[] = $folder; + + if ($dbScriptPath === $folderItems) { + self::exception(ZBX_API_ERROR_PARAMETERS, + _s('Script menu path "%1$s" already used in script name "%2$s".', + $script['name'], $dbScript['name']) + ); + } + } + + // check duplicate script names in same menu path + if ($path == $dbScriptPath) { + self::exception(ZBX_API_ERROR_PARAMETERS, _s('Script "%1$s" already exists.', $script['name'])); + } + } + } + } + + /** + * Trim script name menu path. + * + * @param array $scripts + * + * @return array + */ + protected function trimMenuPath(array $scripts) { + foreach ($scripts as &$script) { + if (!isset($script['name'])) { + continue; + } + + $path = splitPath($script['name'], false); + + $script['name'] = ''; + + foreach ($path as $item) { + $script['name'] .= trim($item).'/'; + } + + $script['name'] = substr($script['name'], 0, strlen($script['name']) - 1); + } + unset($script); + + return $scripts; + } + + /** + * Unset script execution type if type is IPMI. + * + * @param array $scripts + * + * @return array + */ + protected function unsetExecutionType(array $scripts) { + foreach ($scripts as $key => $script) { + if (isset($script['type']) && $script['type'] == ZBX_SCRIPT_TYPE_IPMI) { + unset($scripts[$key]['execute_on']); + } + } + + return $scripts; + } + + protected function applyQueryOutputOptions($tableName, $tableAlias, array $options, array $sqlParts) { + $sqlParts = parent::applyQueryOutputOptions($tableName, $tableAlias, $options, $sqlParts); + + if ($options['output'] != API_OUTPUT_COUNT) { + if ($options['selectGroups'] !== null || $options['selectHosts'] !== null) { + $sqlParts = $this->addQuerySelect($this->fieldId('groupid'), $sqlParts); + $sqlParts = $this->addQuerySelect($this->fieldId('host_access'), $sqlParts); + } + } + + return $sqlParts; + } + + protected function addRelatedObjects(array $options, array $result) { + $result = parent::addRelatedObjects($options, $result); + + // adding groups + if ($options['selectGroups'] !== null && $options['selectGroups'] != API_OUTPUT_COUNT) { + foreach ($result as $scriptId => $script) { + $result[$scriptId]['groups'] = API::HostGroup()->get(array( + 'output' => $options['selectGroups'], + 'groupids' => $script['groupid'] ? $script['groupid'] : null, + 'editable' => ($script['host_access'] == PERM_READ_WRITE) ? true : null + )); + } + } + + // adding hosts + if ($options['selectHosts'] !== null && $options['selectHosts'] != API_OUTPUT_COUNT) { + $processedGroups = array(); + + foreach ($result as $scriptId => $script) { + if (isset($processedGroups[$script['groupid'].'_'.$script['host_access']])) { + $result[$scriptId]['hosts'] = $result[$processedGroups[$script['groupid'].'_'.$script['host_access']]]['hosts']; + } + else { + $result[$scriptId]['hosts'] = API::Host()->get(array( + 'output' => $options['selectHosts'], + 'groupids' => $script['groupid'] ? $script['groupid'] : null, + 'hostids' => $options['hostids'] ? $options['hostids'] : null, + 'editable' => ($script['host_access'] == PERM_READ_WRITE) ? true : null + )); + + $processedGroups[$script['groupid'].'_'.$script['host_access']] = $scriptId; + } + } + } + + return $result; + } +} |