You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1430 lines
47 KiB
1430 lines
47 KiB
<?php
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 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 template.
|
|
*/
|
|
class CTemplate extends CHostGeneral {
|
|
|
|
protected $sortColumns = ['hostid', 'host', 'name'];
|
|
|
|
/**
|
|
* Get template data.
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get($options = []) {
|
|
$result = [];
|
|
|
|
$sqlParts = [
|
|
'select' => ['templates' => 'h.hostid'],
|
|
'from' => ['hosts' => 'hosts h'],
|
|
'where' => ['h.status='.HOST_STATUS_TEMPLATE],
|
|
'group' => [],
|
|
'order' => [],
|
|
'limit' => null
|
|
];
|
|
|
|
$defOptions = [
|
|
'groupids' => null,
|
|
'templateids' => null,
|
|
'parentTemplateids' => null,
|
|
'hostids' => null,
|
|
'graphids' => null,
|
|
'itemids' => null,
|
|
'triggerids' => null,
|
|
'with_items' => null,
|
|
'with_triggers' => null,
|
|
'with_graphs' => null,
|
|
'with_httptests' => null,
|
|
'editable' => false,
|
|
'nopermissions' => null,
|
|
// filter
|
|
'evaltype' => TAG_EVAL_TYPE_AND_OR,
|
|
'tags' => null,
|
|
'filter' => null,
|
|
'search' => '',
|
|
'searchByAny' => null,
|
|
'startSearch' => false,
|
|
'excludeSearch' => false,
|
|
'searchWildcardsEnabled' => null,
|
|
// output
|
|
'output' => API_OUTPUT_EXTEND,
|
|
'selectGroups' => null,
|
|
'selectTemplateGroups' => null,
|
|
'selectHosts' => null,
|
|
'selectTemplates' => null,
|
|
'selectParentTemplates' => null,
|
|
'selectItems' => null,
|
|
'selectDiscoveries' => null,
|
|
'selectTriggers' => null,
|
|
'selectGraphs' => null,
|
|
'selectMacros' => null,
|
|
'selectDashboards' => null,
|
|
'selectHttpTests' => null,
|
|
'selectTags' => null,
|
|
'selectValueMaps' => null,
|
|
'countOutput' => false,
|
|
'groupCount' => false,
|
|
'preservekeys' => false,
|
|
'sortfield' => '',
|
|
'sortorder' => '',
|
|
'limit' => null,
|
|
'limitSelects' => null
|
|
];
|
|
$options = zbx_array_merge($defOptions, $options);
|
|
$this->validateGet($options);
|
|
|
|
$this->checkDeprecatedParam($options, 'selectGroups');
|
|
|
|
// editable + PERMISSION CHECK
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) {
|
|
$permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ;
|
|
$userGroups = getUserGroupsByUserId(self::$userData['userid']);
|
|
|
|
$sqlParts['where'][] = 'EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM hosts_groups hgg'.
|
|
' JOIN rights r'.
|
|
' ON r.id=hgg.groupid'.
|
|
' AND '.dbConditionInt('r.groupid', $userGroups).
|
|
' WHERE h.hostid=hgg.hostid'.
|
|
' GROUP BY hgg.hostid'.
|
|
' HAVING MIN(r.permission)>'.PERM_DENY.
|
|
' AND MAX(r.permission)>='.zbx_dbstr($permission).
|
|
')';
|
|
}
|
|
|
|
// groupids
|
|
if (!is_null($options['groupids'])) {
|
|
zbx_value2array($options['groupids']);
|
|
|
|
$sqlParts['from']['hosts_groups'] = 'hosts_groups hg';
|
|
$sqlParts['where'][] = dbConditionInt('hg.groupid', $options['groupids']);
|
|
$sqlParts['where']['hgh'] = 'hg.hostid=h.hostid';
|
|
|
|
if ($options['groupCount']) {
|
|
$sqlParts['group']['hg'] = 'hg.groupid';
|
|
}
|
|
}
|
|
|
|
// templateids
|
|
if (!is_null($options['templateids'])) {
|
|
zbx_value2array($options['templateids']);
|
|
|
|
$sqlParts['where']['templateid'] = dbConditionInt('h.hostid', $options['templateids']);
|
|
}
|
|
|
|
// parentTemplateids
|
|
if (!is_null($options['parentTemplateids'])) {
|
|
zbx_value2array($options['parentTemplateids']);
|
|
|
|
$sqlParts['from']['hosts_templates'] = 'hosts_templates ht';
|
|
$sqlParts['where'][] = dbConditionInt('ht.templateid', $options['parentTemplateids']);
|
|
$sqlParts['where']['hht'] = 'h.hostid=ht.hostid';
|
|
|
|
if ($options['groupCount']) {
|
|
$sqlParts['group']['templateid'] = 'ht.templateid';
|
|
}
|
|
}
|
|
|
|
// hostids
|
|
if (!is_null($options['hostids'])) {
|
|
zbx_value2array($options['hostids']);
|
|
|
|
$sqlParts['from']['hosts_templates'] = 'hosts_templates ht';
|
|
$sqlParts['where'][] = dbConditionInt('ht.hostid', $options['hostids']);
|
|
$sqlParts['where']['hht'] = 'h.hostid=ht.templateid';
|
|
|
|
if ($options['groupCount']) {
|
|
$sqlParts['group']['ht'] = 'ht.hostid';
|
|
}
|
|
}
|
|
|
|
// itemids
|
|
if (!is_null($options['itemids'])) {
|
|
zbx_value2array($options['itemids']);
|
|
|
|
$sqlParts['from']['items'] = 'items i';
|
|
$sqlParts['where'][] = dbConditionInt('i.itemid', $options['itemids']);
|
|
$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
|
|
}
|
|
|
|
// triggerids
|
|
if (!is_null($options['triggerids'])) {
|
|
zbx_value2array($options['triggerids']);
|
|
|
|
$sqlParts['from']['functions'] = 'functions f';
|
|
$sqlParts['from']['items'] = 'items i';
|
|
$sqlParts['where'][] = dbConditionInt('f.triggerid', $options['triggerids']);
|
|
$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
|
|
$sqlParts['where']['fi'] = 'f.itemid=i.itemid';
|
|
}
|
|
|
|
// graphids
|
|
if (!is_null($options['graphids'])) {
|
|
zbx_value2array($options['graphids']);
|
|
|
|
$sqlParts['from']['graphs_items'] = 'graphs_items gi';
|
|
$sqlParts['from']['items'] = 'items i';
|
|
$sqlParts['where'][] = dbConditionInt('gi.graphid', $options['graphids']);
|
|
$sqlParts['where']['igi'] = 'i.itemid=gi.itemid';
|
|
$sqlParts['where']['hi'] = 'h.hostid=i.hostid';
|
|
}
|
|
|
|
// with_items
|
|
if (!is_null($options['with_items'])) {
|
|
$sqlParts['where'][] = 'EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM items i'.
|
|
' WHERE h.hostid=i.hostid'.
|
|
' AND i.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'.
|
|
')';
|
|
}
|
|
|
|
// with_triggers
|
|
if (!is_null($options['with_triggers'])) {
|
|
$sqlParts['where'][] = 'EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM items i,functions f,triggers t'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND i.itemid=f.itemid'.
|
|
' AND f.triggerid=t.triggerid'.
|
|
' AND t.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'.
|
|
')';
|
|
}
|
|
|
|
// with_graphs
|
|
if (!is_null($options['with_graphs'])) {
|
|
$sqlParts['where'][] = 'EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM items i,graphs_items gi,graphs g'.
|
|
' WHERE i.hostid=h.hostid'.
|
|
' AND i.itemid=gi.itemid'.
|
|
' AND gi.graphid=g.graphid'.
|
|
' AND g.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'.
|
|
')';
|
|
}
|
|
|
|
// with_httptests
|
|
if (!empty($options['with_httptests'])) {
|
|
$sqlParts['where'][] = 'EXISTS (SELECT ht.httptestid FROM httptest ht WHERE ht.hostid=h.hostid)';
|
|
}
|
|
|
|
// tags
|
|
if ($options['tags'] !== null && $options['tags']) {
|
|
$sqlParts['where'][] = CApiTagHelper::addWhereCondition($options['tags'], $options['evaltype'], 'h',
|
|
'host_tag', 'hostid'
|
|
);
|
|
}
|
|
|
|
// filter
|
|
if (is_array($options['filter'])) {
|
|
$this->dbFilter('hosts h', $options, $sqlParts);
|
|
}
|
|
|
|
// search
|
|
if (is_array($options['search'])) {
|
|
zbx_db_search('hosts h', $options, $sqlParts);
|
|
}
|
|
|
|
// limit
|
|
if (zbx_ctype_digit($options['limit']) && $options['limit']) {
|
|
$sqlParts['limit'] = $options['limit'];
|
|
}
|
|
|
|
$sqlParts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
|
|
|
|
$upcased_index = array_search($this->tableAlias().'.name_upper', $sqlParts['select']);
|
|
|
|
if ($upcased_index !== false) {
|
|
unset($sqlParts['select'][$upcased_index]);
|
|
}
|
|
|
|
$sqlParts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sqlParts);
|
|
$res = DBselect(self::createSelectQueryFromParts($sqlParts), $sqlParts['limit']);
|
|
while ($template = DBfetch($res)) {
|
|
if ($options['countOutput']) {
|
|
if ($options['groupCount']) {
|
|
$result[] = $template;
|
|
}
|
|
else {
|
|
$result = $template['rowscount'];
|
|
}
|
|
}
|
|
else {
|
|
$template['templateid'] = $template['hostid'];
|
|
// Templates share table with hosts and host prototypes. Therefore remove template unrelated fields.
|
|
unset($template['hostid'], $template['discover']);
|
|
|
|
$result[$template['templateid']] = $template;
|
|
}
|
|
|
|
}
|
|
|
|
if ($options['countOutput']) {
|
|
return $result;
|
|
}
|
|
|
|
if ($result) {
|
|
$result = $this->addRelatedObjects($options, $result);
|
|
$result = $this->unsetExtraFields($result, ['name_upper']);
|
|
}
|
|
|
|
// removing keys (hash -> array)
|
|
if (!$options['preservekeys']) {
|
|
$result = zbx_cleanHashes($result);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Validates the input parameters for the get() method.
|
|
*
|
|
* @param array $options
|
|
*
|
|
* @throws APIException if the input is invalid
|
|
*/
|
|
protected function validateGet(array $options) {
|
|
// Validate input parameters.
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
|
|
'selectTags' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => implode(',', ['tag', 'value'])],
|
|
'selectValueMaps' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => implode(',', ['valuemapid', 'name', 'mappings', 'uuid'])],
|
|
'selectParentTemplates' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL | API_ALLOW_COUNT, 'in' => implode(',', ['templateid', 'host', 'name', 'description', 'uuid'])]
|
|
]];
|
|
$options_filter = array_intersect_key($options, $api_input_rules['fields']);
|
|
if (!CApiInputValidator::validate($api_input_rules, $options_filter, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add template.
|
|
*
|
|
* @param array $templates
|
|
*
|
|
* @return array
|
|
*/
|
|
public function create(array $templates) {
|
|
$this->validateCreate($templates);
|
|
|
|
$ins_templates = [];
|
|
|
|
foreach ($templates as $template) {
|
|
unset($template['groups'], $template['templates'], $template['tags'], $template['macros']);
|
|
|
|
$ins_templates[] = $template + ['status' => HOST_STATUS_TEMPLATE];
|
|
}
|
|
|
|
$templateids = DB::insert('hosts', $ins_templates);
|
|
|
|
foreach ($templates as $index => &$template) {
|
|
$template['templateid'] = $templateids[$index];
|
|
}
|
|
unset($template);
|
|
|
|
$this->checkTemplatesLinks($templates);
|
|
|
|
$this->updateGroups($templates);
|
|
$this->updateTags($templates);
|
|
$this->updateMacros($templates);
|
|
$this->updateTemplates($templates);
|
|
|
|
self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_TEMPLATE, $templates);
|
|
|
|
return ['templateids' => $templateids];
|
|
}
|
|
|
|
/**
|
|
* @param array $templates
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
protected function validateCreate(array &$templates) {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['uuid'], ['host'], ['name']], 'fields' => [
|
|
'uuid' => ['type' => API_UUID],
|
|
'host' => ['type' => API_H_NAME, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('hosts', 'host')],
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('hosts', 'name'), 'default_source' => 'host'],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hosts', 'description')],
|
|
'vendor_name' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hosts', 'vendor_name')],
|
|
'vendor_version' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hosts', 'vendor_version')],
|
|
'groups' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['groupid']], 'fields' => [
|
|
'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'templates' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'tags' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['tag', 'value']], 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('host_tag', 'tag')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('host_tag', 'value'), 'default' => DB::getDefault('host_tag', 'value')]
|
|
]],
|
|
'macros' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['macro']], 'fields' => [
|
|
'macro' => ['type' => API_USER_MACRO, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('hostmacro', 'macro')],
|
|
'type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_MACRO_TYPE_TEXT, ZBX_MACRO_TYPE_SECRET, ZBX_MACRO_TYPE_VAULT]), 'default' => ZBX_MACRO_TYPE_TEXT],
|
|
'value' => ['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', [ZBX_MACRO_TYPE_VAULT])], 'type' => API_VAULT_SECRET, 'provider' => CSettingsHelper::get(CSettingsHelper::VAULT_PROVIDER), 'length' => DB::getFieldLength('hostmacro', 'value')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'value')]
|
|
]],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'description')]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $templates, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
self::checkVendorFields($templates);
|
|
|
|
self::addUuid($templates);
|
|
|
|
self::checkUuidDuplicates($templates);
|
|
$this->checkDuplicates($templates);
|
|
$this->checkGroups($templates);
|
|
$this->checkTemplates($templates);
|
|
}
|
|
|
|
/**
|
|
* Add the UUID to those of the given templates that don't have the 'uuid' parameter set.
|
|
*
|
|
* @param array $templates
|
|
*/
|
|
private static function addUuid(array &$templates): void {
|
|
foreach ($templates as &$template) {
|
|
if (!array_key_exists('uuid', $template)) {
|
|
$template['uuid'] = generateUuidV4();
|
|
}
|
|
}
|
|
unset($template);
|
|
}
|
|
|
|
/**
|
|
* Verify template UUIDs are not repeated.
|
|
*
|
|
* @param array $templates
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkUuidDuplicates(array $templates, array $db_templates = null): void {
|
|
$template_indexes = [];
|
|
|
|
foreach ($templates as $i => $template) {
|
|
if (!array_key_exists('uuid', $template)) {
|
|
continue;
|
|
}
|
|
|
|
if ($db_templates === null || $template['uuid'] !== $db_templates[$template['templateid']]['uuid']) {
|
|
$template_indexes[$template['uuid']] = $i;
|
|
}
|
|
}
|
|
|
|
if (!$template_indexes) {
|
|
return;
|
|
}
|
|
|
|
$duplicates = DB::select('hosts', [
|
|
'output' => ['uuid'],
|
|
'filter' => [
|
|
'status' => HOST_STATUS_TEMPLATE,
|
|
'uuid' => array_keys($template_indexes)
|
|
],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($duplicates) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Invalid parameter "%1$s": %2$s.', '/'.($template_indexes[$duplicates[0]['uuid']] + 1),
|
|
_('template with the same UUID already exists')
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $templates
|
|
*
|
|
* @return array
|
|
*/
|
|
public function update(array $templates): array {
|
|
$this->validateUpdate($templates, $db_templates);
|
|
|
|
$upd_templates =[];
|
|
|
|
foreach ($templates as $template) {
|
|
$upd_template = DB::getUpdatedValues('hosts', $template, $db_templates[$template['templateid']]);
|
|
|
|
if ($upd_template) {
|
|
$upd_templates[] = [
|
|
'values' => $upd_template,
|
|
'where' => ['hostid' => $template['templateid']]
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($upd_templates) {
|
|
DB::update('hosts', $upd_templates);
|
|
}
|
|
|
|
$this->updateGroups($templates, $db_templates);
|
|
$this->updateTags($templates, $db_templates);
|
|
$this->updateMacros($templates, $db_templates);
|
|
$this->updateTemplates($templates, $db_templates);
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_TEMPLATE, $templates, $db_templates);
|
|
|
|
return ['templateids' => array_column($templates, 'templateid')];
|
|
}
|
|
|
|
/**
|
|
* @param array $templates
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
protected function validateUpdate(array &$templates, array &$db_templates = null) {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['uuid'], ['templateid'], ['host'], ['name']], 'fields' => [
|
|
'uuid' => ['type' => API_UUID],
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'host' => ['type' => API_H_NAME, 'length' => DB::getFieldLength('hosts', 'host')],
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('hosts', 'name')],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hosts', 'description')],
|
|
'vendor_name' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hosts', 'vendor_name')],
|
|
'vendor_version' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hosts', 'vendor_version')],
|
|
'groups' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['groupid']], 'fields' => [
|
|
'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'templates' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'templates_clear' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'tags' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['tag', 'value']], 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('host_tag', 'tag')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('host_tag', 'value'), 'default' => DB::getDefault('host_tag', 'value')]
|
|
]],
|
|
'macros' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['hostmacroid']], 'fields' => [
|
|
'hostmacroid' => ['type' => API_ID],
|
|
'macro' => ['type' => API_USER_MACRO, 'length' => DB::getFieldLength('hostmacro', 'macro')],
|
|
'type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_MACRO_TYPE_TEXT, ZBX_MACRO_TYPE_SECRET, ZBX_MACRO_TYPE_VAULT])],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'value')],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'description')]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $templates, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_templates = $this->get([
|
|
'output' => ['uuid', 'templateid', 'host', 'name', 'description', 'vendor_name', 'vendor_version'],
|
|
'templateids' => array_column($templates, 'templateid'),
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_templates) != count($templates)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
$this->addAffectedObjects($templates, $db_templates);
|
|
|
|
self::checkVendorFields($templates, $db_templates);
|
|
self::checkUuidDuplicates($templates, $db_templates);
|
|
$this->checkDuplicates($templates, $db_templates);
|
|
$this->checkGroups($templates, $db_templates);
|
|
$this->checkTemplates($templates, $db_templates);
|
|
$this->checkTemplatesLinks($templates, $db_templates);
|
|
$templates = $this->validateHostMacros($templates, $db_templates);
|
|
}
|
|
|
|
/**
|
|
* Check vendor fields for update or create operation.
|
|
*
|
|
* @param array $templates
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
private static function checkVendorFields(array $templates, array $db_templates = null): void {
|
|
$vendor_fields = array_fill_keys(['vendor_name', 'vendor_version'], '');
|
|
|
|
foreach ($templates as $i => $template) {
|
|
if (!array_key_exists('vendor_name', $template) && !array_key_exists('vendor_version', $template)) {
|
|
continue;
|
|
}
|
|
|
|
$_template = array_intersect_key($template, $vendor_fields);
|
|
|
|
if ($db_templates === null) {
|
|
$_template += $vendor_fields;
|
|
}
|
|
else {
|
|
$_template += array_intersect_key($db_templates[$template['templateid']], $vendor_fields);
|
|
}
|
|
|
|
if (($_template['vendor_name'] === '') !== ($_template['vendor_version'] === '')) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
|
|
_('both vendor_name and vendor_version should be either present or empty')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete template.
|
|
*
|
|
* @param array $templateids
|
|
* @param array $templateids['templateids']
|
|
*
|
|
* @return array
|
|
*/
|
|
public function delete(array $templateids) {
|
|
$this->validateDelete($templateids, $db_templates);
|
|
|
|
self::unlinkTemplatesObjects($templateids, null, true);
|
|
|
|
// delete the discovery rules first
|
|
$db_lld_rules = DB::select('items', [
|
|
'output' => ['itemid', 'name'],
|
|
'filter' => [
|
|
'hostid' => $templateids,
|
|
'flags' => ZBX_FLAG_DISCOVERY_RULE
|
|
],
|
|
'preservekeys' => true
|
|
]);
|
|
if ($db_lld_rules) {
|
|
CDiscoveryRule::deleteForce($db_lld_rules);
|
|
}
|
|
|
|
// delete the items
|
|
$db_items = DB::select('items', [
|
|
'output' => ['itemid', 'name'],
|
|
'filter' => [
|
|
'hostid' => $templateids,
|
|
'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
|
|
'type' => CItem::SUPPORTED_ITEM_TYPES
|
|
],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if ($db_items) {
|
|
CItem::deleteForce($db_items);
|
|
}
|
|
|
|
// delete host from maps
|
|
if (!empty($templateids)) {
|
|
DB::delete('sysmaps_elements', ['elementtype' => SYSMAP_ELEMENT_TYPE_HOST, 'elementid' => $templateids]);
|
|
}
|
|
|
|
// disable actions
|
|
// actions from conditions
|
|
$actionids = [];
|
|
$sql = 'SELECT DISTINCT actionid'.
|
|
' FROM conditions'.
|
|
' WHERE conditiontype='.CONDITION_TYPE_TEMPLATE.
|
|
' AND '.dbConditionString('value', $templateids);
|
|
$dbActions = DBselect($sql);
|
|
while ($dbAction = DBfetch($dbActions)) {
|
|
$actionids[$dbAction['actionid']] = $dbAction['actionid'];
|
|
}
|
|
|
|
// actions from operations
|
|
$sql = 'SELECT DISTINCT o.actionid'.
|
|
' FROM operations o,optemplate ot'.
|
|
' WHERE o.operationid=ot.operationid'.
|
|
' AND '.dbConditionInt('ot.templateid', $templateids);
|
|
$dbActions = DBselect($sql);
|
|
while ($dbAction = DBfetch($dbActions)) {
|
|
$actionids[$dbAction['actionid']] = $dbAction['actionid'];
|
|
}
|
|
|
|
if (!empty($actionids)) {
|
|
DB::update('actions', [
|
|
'values' => ['status' => ACTION_STATUS_DISABLED],
|
|
'where' => ['actionid' => $actionids]
|
|
]);
|
|
}
|
|
|
|
// delete action conditions
|
|
DB::delete('conditions', [
|
|
'conditiontype' => CONDITION_TYPE_TEMPLATE,
|
|
'value' => $templateids
|
|
]);
|
|
|
|
// delete action operation commands
|
|
$operationids = [];
|
|
$sql = 'SELECT DISTINCT ot.operationid'.
|
|
' FROM optemplate ot'.
|
|
' WHERE '.dbConditionInt('ot.templateid', $templateids);
|
|
$dbOperations = DBselect($sql);
|
|
while ($dbOperation = DBfetch($dbOperations)) {
|
|
$operationids[$dbOperation['operationid']] = $dbOperation['operationid'];
|
|
}
|
|
|
|
DB::delete('optemplate', [
|
|
'templateid'=>$templateids
|
|
]);
|
|
|
|
// delete empty operations
|
|
$delOperationids = [];
|
|
$sql = 'SELECT DISTINCT o.operationid'.
|
|
' FROM operations o'.
|
|
' WHERE '.dbConditionInt('o.operationid', $operationids).
|
|
' AND NOT EXISTS(SELECT NULL FROM optemplate ot WHERE ot.operationid=o.operationid)';
|
|
$dbOperations = DBselect($sql);
|
|
while ($dbOperation = DBfetch($dbOperations)) {
|
|
$delOperationids[$dbOperation['operationid']] = $dbOperation['operationid'];
|
|
}
|
|
|
|
DB::delete('operations', [
|
|
'operationid'=>$delOperationids
|
|
]);
|
|
|
|
// delete web scenarios
|
|
$db_httptests = DB::select('httptest', [
|
|
'output' => ['httptestid', 'name'],
|
|
'filter' => ['hostid' => $templateids],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if ($db_httptests) {
|
|
CHttpTest::deleteForce($db_httptests);
|
|
}
|
|
|
|
// Get host prototype operations from LLD overrides where this template is linked.
|
|
$lld_override_operationids = [];
|
|
|
|
$db_lld_override_operationids = DBselect(
|
|
'SELECT loo.lld_override_operationid'.
|
|
' FROM lld_override_operation loo'.
|
|
' WHERE EXISTS('.
|
|
'SELECT NULL'.
|
|
' FROM lld_override_optemplate lot'.
|
|
' WHERE lot.lld_override_operationid=loo.lld_override_operationid'.
|
|
' AND '.dbConditionId('lot.templateid', $templateids).
|
|
')'
|
|
);
|
|
while ($db_lld_override_operationid = DBfetch($db_lld_override_operationids)) {
|
|
$lld_override_operationids[] = $db_lld_override_operationid['lld_override_operationid'];
|
|
}
|
|
|
|
if ($lld_override_operationids) {
|
|
DB::delete('lld_override_optemplate', ['templateid' => $templateids]);
|
|
|
|
// Make sure there no other operations left to safely delete the operation.
|
|
$delete_lld_override_operationids = [];
|
|
|
|
$db_delete_lld_override_operationids = DBselect(
|
|
'SELECT loo.lld_override_operationid'.
|
|
' FROM lld_override_operation loo'.
|
|
' WHERE NOT EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM lld_override_opstatus los'.
|
|
' WHERE los.lld_override_operationid=loo.lld_override_operationid'.
|
|
')'.
|
|
' AND NOT EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM lld_override_opdiscover lod'.
|
|
' WHERE lod.lld_override_operationid=loo.lld_override_operationid'.
|
|
')'.
|
|
' AND NOT EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM lld_override_opinventory loi'.
|
|
' WHERE loi.lld_override_operationid=loo.lld_override_operationid'.
|
|
')'.
|
|
' AND NOT EXISTS ('.
|
|
'SELECT NULL'.
|
|
' FROM lld_override_optemplate lot'.
|
|
' WHERE lot.lld_override_operationid=loo.lld_override_operationid'.
|
|
')'.
|
|
' AND '.dbConditionId('loo.lld_override_operationid', $lld_override_operationids)
|
|
);
|
|
|
|
while ($db_delete_lld_override_operationid = DBfetch($db_delete_lld_override_operationids)) {
|
|
$delete_lld_override_operationids[] = $db_delete_lld_override_operationid['lld_override_operationid'];
|
|
}
|
|
|
|
if ($delete_lld_override_operationids) {
|
|
DB::delete('lld_override_operation', ['lld_override_operationid' => $delete_lld_override_operationids]);
|
|
}
|
|
}
|
|
|
|
// Finally delete the template.
|
|
DB::delete('host_tag', ['hostid' => $templateids]);
|
|
DB::delete('hosts', ['hostid' => $templateids]);
|
|
|
|
$this->addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_TEMPLATE, $db_templates);
|
|
|
|
return ['templateids' => $templateids];
|
|
}
|
|
|
|
/**
|
|
* @param array $templateids
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private function validateDelete(array &$templateids, array &$db_templates = null): void {
|
|
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $templateids, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_templates = $this->get([
|
|
'output' => ['templateid', 'host', 'name'],
|
|
'templateids' => $templateids,
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_templates) != count($templateids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
$del_templates = [];
|
|
$result = DBselect(
|
|
'SELECT ht.hostid,ht.templateid AS del_templateid,htt.templateid'.
|
|
' FROM hosts_templates ht,hosts_templates htt'.
|
|
' WHERE ht.hostid=htt.hostid'.
|
|
' AND ht.templateid!=htt.templateid'.
|
|
' AND '.dbConditionId('ht.templateid', $templateids).
|
|
' AND '.dbConditionId('htt.templateid', $templateids, true)
|
|
);
|
|
|
|
while ($row = DBfetch($result)) {
|
|
$del_templates[$row['del_templateid']][$row['hostid']][] = $row['templateid'];
|
|
}
|
|
|
|
$del_links_clear = [];
|
|
$options = [
|
|
'output' => ['templateid', 'hostid'],
|
|
'filter' => [
|
|
'templateid' => $templateids
|
|
]
|
|
];
|
|
$result = DBselect(DB::makeSql('hosts_templates', $options));
|
|
|
|
while ($row = DBfetch($result)) {
|
|
if (!in_array($row['hostid'], $templateids)) {
|
|
$del_links_clear[$row['templateid']][$row['hostid']] = true;
|
|
}
|
|
}
|
|
|
|
if ($del_templates) {
|
|
$this->checkTriggerExpressionsOfDelTemplates($del_templates);
|
|
}
|
|
|
|
if ($del_links_clear) {
|
|
$this->checkTriggerDependenciesOfHostTriggers($del_links_clear);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add given template groups, macros and templates to given templates.
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function massAdd(array $data) {
|
|
$this->validateMassAdd($data, $db_templates);
|
|
|
|
$templates = $this->getObjectsByData($data, $db_templates);
|
|
|
|
$this->updateGroups($templates, $db_templates);
|
|
$this->updateMacros($templates, $db_templates);
|
|
$this->updateTemplates($templates, $db_templates);
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_TEMPLATE, $templates, $db_templates);
|
|
|
|
return ['templateids' => array_column($data['templates'], 'templateid')];
|
|
}
|
|
|
|
/**
|
|
* Replace template groups, macros and templates on the given templates.
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function massUpdate(array $data) {
|
|
$this->validateMassUpdate($data, $db_templates);
|
|
|
|
$templates = $this->getObjectsByData($data, $db_templates);
|
|
|
|
$this->updateGroups($templates, $db_templates);
|
|
$this->updateMacros($templates, $db_templates);
|
|
$this->updateTemplates($templates, $db_templates);
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_TEMPLATE, $templates, $db_templates);
|
|
|
|
return ['templateids' => array_column($data['templates'], 'templateid')];
|
|
}
|
|
|
|
/**
|
|
* Remove given template groups, macros and templates from given templates.
|
|
*
|
|
* @param array $data
|
|
*
|
|
* @return array
|
|
*/
|
|
public function massRemove(array $data) {
|
|
$this->validateMassRemove($data, $db_templates);
|
|
|
|
$templates = $this->getObjectsByData($data, $db_templates);
|
|
|
|
$this->updateGroups($templates, $db_templates);
|
|
$this->updateMacros($templates, $db_templates);
|
|
$this->updateTemplates($templates, $db_templates);
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_TEMPLATE, $templates, $db_templates);
|
|
|
|
return ['templateids' => $data['templateids']];
|
|
}
|
|
|
|
/**
|
|
* @param array $data
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private function validateMassAdd(array &$data, ?array &$db_templates): void {
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
|
|
'templates' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'groups' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['groupid']], 'fields' => [
|
|
'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'macros' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['macro']], 'fields' => [
|
|
'macro' => ['type' => API_USER_MACRO, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('hostmacro', 'macro')],
|
|
'type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_MACRO_TYPE_TEXT, ZBX_MACRO_TYPE_SECRET, ZBX_MACRO_TYPE_VAULT]), 'default' => ZBX_MACRO_TYPE_TEXT],
|
|
'value' => ['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', [ZBX_MACRO_TYPE_VAULT])], 'type' => API_VAULT_SECRET, 'provider' => CSettingsHelper::get(CSettingsHelper::VAULT_PROVIDER), 'length' => DB::getFieldLength('hostmacro', 'value')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'value')]
|
|
]],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'description')]
|
|
]],
|
|
'templates_link' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $data, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_templates = $this->get([
|
|
'output' => ['templateid', 'host'],
|
|
'templateids' => array_column($data['templates'], 'templateid'),
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_templates) != count($data['templates'])) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
if (array_key_exists('groups', $data) && $data['groups']) {
|
|
$groupids = array_column($data['groups'], 'groupid');
|
|
|
|
$count = API::TemplateGroup()->get([
|
|
'countOutput' => true,
|
|
'groupids' => $groupids,
|
|
'editable' => true
|
|
]);
|
|
|
|
if ($count != count($groupids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
|
|
$this->massAddAffectedObjects('groups', $groupids, $db_templates);
|
|
}
|
|
|
|
if (array_key_exists('macros', $data) && $data['macros']) {
|
|
$macros = [];
|
|
|
|
foreach ($data['macros'] as $macro) {
|
|
$macros[CApiInputValidator::trimMacro($macro['macro'])] = $macro['macro'];
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['hostid', 'macro'],
|
|
'filter' => ['hostid' => array_keys($db_templates)]
|
|
];
|
|
$db_macros = DBselect(DB::makeSql('hostmacro', $options));
|
|
|
|
while ($db_macro = DBfetch($db_macros)) {
|
|
$trimmed_db_macro = CApiInputValidator::trimMacro($db_macro['macro']);
|
|
|
|
if (array_key_exists($trimmed_db_macro, $macros)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Macro "%1$s" already exists on "%2$s".', $macros[$trimmed_db_macro],
|
|
$db_templates[$db_macro['hostid']]['host']
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
foreach ($db_templates as &$db_template) {
|
|
$db_template['macros'] = [];
|
|
}
|
|
unset($db_host);
|
|
}
|
|
|
|
if (array_key_exists('templates_link', $data) && $data['templates_link']) {
|
|
$templateids = array_column($data['templates_link'], 'templateid');
|
|
|
|
$count = API::Template()->get([
|
|
'countOutput' => true,
|
|
'templateids' => $templateids
|
|
]);
|
|
|
|
if ($count != count($templateids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
|
|
$this->massAddAffectedObjects('templates', $templateids, $db_templates);
|
|
|
|
$this->massCheckTemplatesLinks('massadd', $templateids, $db_templates);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $data
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private function validateMassUpdate(array &$data, ?array &$db_templates): void {
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
|
|
'templates' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'groups' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['groupid']], 'fields' => [
|
|
'groupid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'macros' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['macro']], 'fields' => [
|
|
'macro' => ['type' => API_USER_MACRO, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('hostmacro', 'macro')],
|
|
'type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_MACRO_TYPE_TEXT, ZBX_MACRO_TYPE_SECRET, ZBX_MACRO_TYPE_VAULT]), 'default' => ZBX_MACRO_TYPE_TEXT],
|
|
'value' => ['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
|
|
['if' => ['field' => 'type', 'in' => implode(',', [ZBX_MACRO_TYPE_VAULT])], 'type' => API_VAULT_SECRET, 'provider' => CSettingsHelper::get(CSettingsHelper::VAULT_PROVIDER), 'length' => DB::getFieldLength('hostmacro', 'value')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'value')]
|
|
]],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('hostmacro', 'description')]
|
|
]],
|
|
'templates_link' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'templates_clear' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['templateid']], 'fields' => [
|
|
'templateid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $data, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_templates = $this->get([
|
|
'output' => ['templateid', 'host'],
|
|
'templateids' => array_column($data['templates'], 'templateid'),
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_templates) != count($data['templates'])) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
if (array_key_exists('groups', $data)) {
|
|
$groupids = array_column($data['groups'], 'groupid');
|
|
|
|
$count = API::TemplateGroup()->get([
|
|
'countOutput' => true,
|
|
'groupids' => $groupids
|
|
]);
|
|
|
|
if ($count != count($groupids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
|
|
$this->massAddAffectedObjects('groups', [], $db_templates);
|
|
|
|
$groupids = array_flip($groupids);
|
|
$edit_groupids = [];
|
|
|
|
foreach ($db_templates as $db_template) {
|
|
$_groupids = $groupids;
|
|
|
|
foreach ($db_template['groups'] as $db_group) {
|
|
if (array_key_exists($db_group['groupid'], $_groupids)) {
|
|
unset($_groupids[$db_group['groupid']]);
|
|
} else {
|
|
$edit_groupids[$db_group['groupid']] = true;
|
|
}
|
|
}
|
|
|
|
$edit_groupids += $_groupids;
|
|
}
|
|
|
|
if ($edit_groupids) {
|
|
$count = API::TemplateGroup()->get([
|
|
'countOutput' => true,
|
|
'groupids' => array_keys($edit_groupids),
|
|
'editable' => true
|
|
]);
|
|
|
|
if ($count != count($edit_groupids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('macros', $data)) {
|
|
$this->massAddAffectedObjects('macros', [], $db_templates);
|
|
}
|
|
|
|
if (array_key_exists('templates_link', $data)
|
|
|| (array_key_exists('templates_clear', $data) && $data['templates_clear'])) {
|
|
if (array_key_exists('templates_link', $data) && array_key_exists('templates_clear', $data)) {
|
|
$path_clear = '/templates_clear';
|
|
$path = '/templates_link';
|
|
|
|
foreach ($data['templates_clear'] as $i1_clear => $template_clear) {
|
|
foreach ($data['templates_link'] as $i1 => $template) {
|
|
if (bccomp($template['templateid'], $template_clear['templateid']) == 0) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
|
|
$path_clear.'/'.($i1_clear + 1).'/templateid',
|
|
_s('cannot be specified the value of parameter "%1$s"',
|
|
$path.'/'.($i1 + 1).'/templateid'
|
|
)
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->massAddAffectedObjects('templates', [], $db_templates);
|
|
|
|
$templateids_link = array_key_exists('templates_link', $data)
|
|
? array_column($data['templates_link'], 'templateid')
|
|
: [];
|
|
$templateids_clear = array_key_exists('templates_clear', $data)
|
|
? array_column($data['templates_clear'], 'templateid')
|
|
: [];
|
|
|
|
$edit_templateids = array_flip($templateids_clear);
|
|
|
|
if ($templateids_link) {
|
|
foreach ($db_templates as $db_template) {
|
|
$edit_templateids += array_flip(array_diff(array_column($db_template['templates'], 'templateid'),
|
|
$templateids_link
|
|
));
|
|
}
|
|
}
|
|
|
|
if ($edit_templateids) {
|
|
$count = $this->get([
|
|
'countOutput' => true,
|
|
'templateids' => array_keys($edit_templateids)
|
|
]);
|
|
|
|
if ($count != count($edit_templateids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
|
|
if (array_key_exists('templates_link', $data)) {
|
|
$this->massCheckTemplatesLinks('massupdate', $templateids_link, $db_templates, $templateids_clear);
|
|
}
|
|
else {
|
|
$this->massCheckTemplatesLinks('massremove', $templateids_clear, $db_templates, $templateids_clear);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $data
|
|
* @param array|null $db_templates
|
|
*
|
|
* @throws APIException if the input is invalid.
|
|
*/
|
|
private function validateMassRemove(array &$data, ?array &$db_templates): void {
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
|
|
'templateids' => ['type' => API_IDS, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_NORMALIZE, 'uniq' => true],
|
|
'groupids' => ['type' => API_IDS, 'flags' => API_NORMALIZE, 'uniq' => true],
|
|
'macros' => ['type' => API_USER_MACROS, 'flags' => API_NORMALIZE, 'uniq' => true, 'length' => DB::getFieldLength('hostmacro', 'macro')],
|
|
'templateids_link' => ['type' => API_IDS, 'flags' => API_NORMALIZE, 'uniq' => true],
|
|
'templateids_clear' => ['type' => API_IDS, 'flags' => API_NORMALIZE, 'uniq' => true]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $data, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_templates = $this->get([
|
|
'output' => ['templateid', 'host'],
|
|
'templateids' => $data['templateids'],
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_templates) != count($data['templateids'])) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
if (array_key_exists('groupids', $data) && $data['groupids']) {
|
|
$count = API::TemplateGroup()->get([
|
|
'countOutput' => true,
|
|
'groupids' => $data['groupids'],
|
|
'editable' => true
|
|
]);
|
|
|
|
if ($count != count($data['groupids'])) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
|
|
CTemplateGroup::checkTemplatesWithoutGroups($db_templates, $data['groupids']);
|
|
|
|
$this->massAddAffectedObjects('groups', $data['groupids'], $db_templates);
|
|
}
|
|
|
|
if (array_key_exists('macros', $data) && $data['macros']) {
|
|
$this->massAddAffectedObjects('macros', $data['macros'], $db_templates);
|
|
}
|
|
|
|
if ((array_key_exists('templateids_link', $data) && $data['templateids_link'])
|
|
|| (array_key_exists('templateids_clear', $data) && $data['templateids_clear'])) {
|
|
if (array_key_exists('templateids_link', $data) && $data['templateids_link']
|
|
&& array_key_exists('templateids_clear', $data) && $data['templateids_clear']) {
|
|
$templateids = array_unique(array_merge($data['templateids_link'], $data['templateids_clear']));
|
|
}
|
|
elseif (array_key_exists('templateids_link', $data) && $data['templateids_link']) {
|
|
$templateids = $data['templateids_link'];
|
|
}
|
|
else {
|
|
$templateids = $data['templateids_clear'];
|
|
}
|
|
|
|
$count = $this->get([
|
|
'countOutput' => true,
|
|
'templateids' => $templateids
|
|
]);
|
|
|
|
if ($count != count($templateids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_('No permissions to referred object or it does not exist!')
|
|
);
|
|
}
|
|
|
|
$this->massAddAffectedObjects('templates', $templateids, $db_templates);
|
|
|
|
$this->massCheckTemplatesLinks('massremove', $templateids, $db_templates,
|
|
array_key_exists('templateids_clear', $data) ? $data['templateids_clear'] : []
|
|
);
|
|
}
|
|
}
|
|
|
|
protected function addRelatedObjects(array $options, array $result) {
|
|
$result = parent::addRelatedObjects($options, $result);
|
|
|
|
// adding template groups
|
|
$this->addRelatedGroups($options, $result, 'selectGroups');
|
|
$this->addRelatedGroups($options, $result, 'selectTemplateGroups');
|
|
|
|
$templateids = array_keys($result);
|
|
|
|
if ($options['selectTemplates'] !== null) {
|
|
if ($options['selectTemplates'] != API_OUTPUT_COUNT) {
|
|
$templates = [];
|
|
$relationMap = $this->createRelationMap($result, 'templateid', 'hostid', 'hosts_templates');
|
|
$related_ids = $relationMap->getRelatedIds();
|
|
|
|
if ($related_ids) {
|
|
$templates = API::Template()->get([
|
|
'output' => $options['selectTemplates'],
|
|
'templateids' => $related_ids,
|
|
'preservekeys' => true
|
|
]);
|
|
if (!is_null($options['limitSelects'])) {
|
|
order_result($templates, 'host');
|
|
}
|
|
}
|
|
|
|
$result = $relationMap->mapMany($result, $templates, 'templates', $options['limitSelects']);
|
|
}
|
|
else {
|
|
$templates = API::Template()->get([
|
|
'parentTemplateids' => $templateids,
|
|
'countOutput' => true,
|
|
'groupCount' => true
|
|
]);
|
|
$templates = zbx_toHash($templates, 'templateid');
|
|
foreach ($result as $templateid => $template) {
|
|
$result[$templateid]['templates'] = array_key_exists($templateid, $templates)
|
|
? $templates[$templateid]['rowscount']
|
|
: '0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($options['selectHosts'] !== null) {
|
|
if ($options['selectHosts'] != API_OUTPUT_COUNT) {
|
|
$hosts = [];
|
|
$relationMap = $this->createRelationMap($result, 'templateid', 'hostid', 'hosts_templates');
|
|
$related_ids = $relationMap->getRelatedIds();
|
|
|
|
if ($related_ids) {
|
|
$hosts = API::Host()->get([
|
|
'output' => $options['selectHosts'],
|
|
'hostids' => $related_ids,
|
|
'preservekeys' => true
|
|
]);
|
|
if (!is_null($options['limitSelects'])) {
|
|
order_result($hosts, 'host');
|
|
}
|
|
}
|
|
|
|
$result = $relationMap->mapMany($result, $hosts, 'hosts', $options['limitSelects']);
|
|
}
|
|
else {
|
|
$hosts = API::Host()->get([
|
|
'templateids' => $templateids,
|
|
'countOutput' => true,
|
|
'groupCount' => true
|
|
]);
|
|
$hosts = zbx_toHash($hosts, 'templateid');
|
|
foreach ($result as $templateid => $template) {
|
|
$result[$templateid]['hosts'] = array_key_exists($templateid, $hosts)
|
|
? $hosts[$templateid]['rowscount']
|
|
: '0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($options['selectDashboards'] !== null) {
|
|
if ($options['selectDashboards'] != API_OUTPUT_COUNT) {
|
|
$dashboards = API::TemplateDashboard()->get([
|
|
'output' => $this->outputExtend($options['selectDashboards'], ['templateid']),
|
|
'templateids' => $templateids
|
|
]);
|
|
if (!is_null($options['limitSelects'])) {
|
|
order_result($dashboards, 'name');
|
|
}
|
|
|
|
// Build relation map.
|
|
$relationMap = new CRelationMap();
|
|
foreach ($dashboards as $key => $dashboard) {
|
|
$relationMap->addRelation($dashboard['templateid'], $key);
|
|
}
|
|
|
|
$dashboards = $this->unsetExtraFields($dashboards, ['templateid'], $options['selectDashboards']);
|
|
$result = $relationMap->mapMany($result, $dashboards, 'dashboards', $options['limitSelects']);
|
|
}
|
|
else {
|
|
$dashboards = API::TemplateDashboard()->get([
|
|
'templateids' => $templateids,
|
|
'countOutput' => true,
|
|
'groupCount' => true
|
|
]);
|
|
$dashboards = zbx_toHash($dashboards, 'templateid');
|
|
foreach ($result as $templateid => $template) {
|
|
$result[$templateid]['dashboards'] = array_key_exists($templateid, $dashboards)
|
|
? $dashboards[$templateid]['rowscount']
|
|
: '0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($options['selectTags'] !== null) {
|
|
foreach ($result as &$row) {
|
|
$row['tags'] = [];
|
|
}
|
|
unset($row);
|
|
|
|
if ($options['selectTags'] === API_OUTPUT_EXTEND) {
|
|
$output = ['hosttagid', 'hostid', 'tag', 'value'];
|
|
}
|
|
else {
|
|
$output = array_unique(array_merge(['hosttagid', 'hostid'], $options['selectTags']));
|
|
}
|
|
|
|
$sql_options = [
|
|
'output' => $output,
|
|
'filter' => ['hostid' => $templateids]
|
|
];
|
|
$db_tags = DBselect(DB::makeSql('host_tag', $sql_options));
|
|
|
|
while ($db_tag = DBfetch($db_tags)) {
|
|
$hostid = $db_tag['hostid'];
|
|
|
|
unset($db_tag['hosttagid'], $db_tag['hostid']);
|
|
|
|
$result[$hostid]['tags'][] = $db_tag;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Adds related template groups requested by "select*" options to the resulting object set.
|
|
*
|
|
* @param array $options [IN] Original input options.
|
|
* @param array $result [IN/OUT] Result output.
|
|
* @param string $option [IN] Possible values:
|
|
* - "selectGroups" (deprecated);
|
|
* - "selectHostGroups" (or any other value).
|
|
*/
|
|
private function addRelatedGroups(array $options, array &$result, string $option): void {
|
|
if ($options[$option] === null || $options[$option] === API_OUTPUT_COUNT) {
|
|
return;
|
|
}
|
|
|
|
$relationMap = $this->createRelationMap($result, 'hostid', 'groupid', 'hosts_groups');
|
|
$groups = API::TemplateGroup()->get([
|
|
'output' => $options[$option],
|
|
'groupids' => $relationMap->getRelatedIds(),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$output_tag = $option === 'selectGroups' ? 'groups' : 'templategroups';
|
|
$result = $relationMap->mapMany($result, $groups, $output_tag);
|
|
}
|
|
}
|