diff options
Diffstat (limited to 'ui/app/controllers/CControllerPopupMassupdateItem.php')
-rw-r--r-- | ui/app/controllers/CControllerPopupMassupdateItem.php | 480 |
1 files changed, 226 insertions, 254 deletions
diff --git a/ui/app/controllers/CControllerPopupMassupdateItem.php b/ui/app/controllers/CControllerPopupMassupdateItem.php index 0f6aad4c194..e983b4ea7a9 100644 --- a/ui/app/controllers/CControllerPopupMassupdateItem.php +++ b/ui/app/controllers/CControllerPopupMassupdateItem.php @@ -23,86 +23,61 @@ require_once dirname(__FILE__).'/../../include/forms.inc.php'; class CControllerPopupMassupdateItem extends CController { - private $opt_interfaceid_expected = false; - protected function checkInput() { $fields = [ - 'allow_traps' => 'in '.implode(',', [HTTPCHECK_ALLOW_TRAPS_ON, HTTPCHECK_ALLOW_TRAPS_OFF]), - 'authtype' => 'string', 'context' => 'required|string|in host,template', - 'delay' => 'string', - 'delay_flex' => 'array', - 'description' => 'string', - 'discover' => 'in '.ZBX_PROTOTYPE_DISCOVER.','.ZBX_PROTOTYPE_NO_DISCOVER, - 'headers' => 'array', - 'history' => 'string', - 'history_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), 'ids' => 'required|array_id', - 'interfaceid' => 'id', - 'jmx_endpoint' => 'string', - 'logtimefmt' => 'string', - 'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]), - 'master_itemid' => 'id', - 'parent_discoveryid' => 'id', - 'password' => 'string', - 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]), - 'posts' => 'string', - 'preprocessing' => 'array', - 'privatekey' => 'string', 'prototype' => 'required|in 0,1', - 'publickey' => 'string', - 'status' => 'in '.implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]), - 'tags' => 'array', - 'trapper_hosts' => 'string', - 'trends' => 'string', + 'update' => 'in 1', + 'visible' => 'array', + 'parent_discoveryid' => 'id', + 'history_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), 'trends_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), - 'timeout' => 'string', + 'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]), + 'delay_flex' => 'array', + + // The fields used for all item types. 'type' => 'int32', - 'units' => 'string', - 'update' => 'in 1', - 'url' => 'string', - 'username' => 'string', 'value_type' => 'in '.implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT]), + 'units' => 'string', + 'history' => 'string', + 'trends' => 'string', 'valuemapid' => 'id', - 'visible' => 'array' - ]; + 'logtimefmt' => 'string', + 'description' => 'string', + 'status' => 'in '.implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]), + 'discover' => 'in '.ZBX_PROTOTYPE_DISCOVER.','.ZBX_PROTOTYPE_NO_DISCOVER, + 'tags' => 'array', + 'preprocessing' => 'array', - $this->opt_interfaceid_expected = (getRequest('interfaceid') == INTERFACE_TYPE_OPT); + // The fields used for multiple item types. + 'interfaceid' => 'id', + 'authtype' => 'string', + 'username' => 'string', + 'password' => 'string', + 'timeout' => 'string', + 'delay' => 'string', + 'trapper_hosts' => 'string', - if ($this->opt_interfaceid_expected) { - unset($fields['interfaceid']); - unset($_REQUEST['interfaceid']); - } + // Dependent item type specific fields. + 'master_itemid' => 'id', - $ret = $this->validateInput($fields); + // HTTP Agent item type specific fields. + 'url' => 'string', + 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]), + 'posts' => 'string', + 'headers' => 'array', + 'allow_traps' => 'in '.implode(',', [HTTPCHECK_ALLOW_TRAPS_ON, HTTPCHECK_ALLOW_TRAPS_OFF]), - if ($ret && $this->opt_interfaceid_expected) { - if ($this->hasInput('type')) { - $item_types = [$this->getInput('type')]; - } - else { - $options = [ - 'output' => ['type'], - 'itemids' => $this->getInput('ids') - ]; - $item_types = (bool) $this->getInput('prototype') - ? API::ItemPrototype()->get($options) - : API::Item()->get($options); - - $item_types = array_column($item_types, 'type', 'type'); - } + // JMX item type specific fields. + 'jmx_endpoint' => 'string', - foreach ($item_types as $item_type) { - if (itemTypeInterface($item_type) != INTERFACE_TYPE_OPT) { - error(_s('Incorrect value for field "%1$s": %2$s.', _('Host interface'), - interfaceType2str(INTERFACE_TYPE_OPT) - )); - $ret = false; + // SSH item type specific fields. + 'publickey' => 'string', + 'privatekey' => 'string' + ]; - break; - } - } - } + $ret = $this->validateInput($fields); if (!$ret) { $this->setResponse( @@ -118,52 +93,26 @@ class CControllerPopupMassupdateItem extends CController { } protected function checkPermissions() { - $entity = ($this->getInput('prototype') == 1) ? API::ItemPrototype() : API::Item(); - - return (bool) $entity->get([ - 'output' => [], - 'itemids' => $this->getInput('ids'), - 'editable' => true, - 'limit' => 1 - ]); - } - - protected function doAction() { - $this->setResponse($this->hasInput('update') ? $this->update() : $this->form()); - } - - /** - * Get array of updated items or item prototypes. - * - * @return array - */ - protected function getItemsOrPrototypes(): array { - $options = [ - 'output' => ['itemid', 'type'], - 'selectTags' => ['tag', 'value'], - 'itemids' => $this->getInput('ids'), - 'preservekeys' => true - ]; - - if ($this->getInput('prototype')) { - $result = API::ItemPrototype()->get($options); + if ($this->getInput('prototype') == 1) { + $count = API::ItemPrototype()->get([ + 'countOutput' => true, + 'itemids' => $this->getInput('ids'), + 'editable' => true + ]); } else { - $options['output'][] = 'flags'; - $result = API::Item()->get($options); + $count = API::Item()->get([ + 'countOutput' => true, + 'itemids' => $this->getInput('ids'), + 'editable' => true + ]); } - return $result; + return $count != 0; } - /** - * Update item or item prototype, return update action status. - * - * @param array $data Array of item or item prototypes data to update. - * @return bool - */ - protected function updateItemOrPrototype(array $data): bool { - return (bool) ($this->getInput('prototype') ? API::ItemPrototype()->update($data) : API::Item()->update($data)); + protected function doAction() { + $this->setResponse($this->hasInput('update') ? $this->update() : $this->form()); } /** @@ -172,189 +121,159 @@ class CControllerPopupMassupdateItem extends CController { * @return CControllerResponse */ protected function update(): CControllerResponse { - $result = true; - $ids = $this->getInput('ids'); - $prototype = (bool) $this->getInput('prototype'); - $input = [ - 'allow_traps' => HTTPCHECK_ALLOW_TRAPS_OFF, - 'authtype' => '', - 'delay' => DB::getDefault('items', 'delay'), - 'description' => '', - 'discover' => ZBX_PROTOTYPE_DISCOVER, - 'headers' => [], - 'history' => ITEM_NO_STORAGE_VALUE, - 'jmx_endpoint' => '', - 'logtimefmt' => '', - 'master_itemid' => 0, - 'password' => '', - 'post_type' => ZBX_POSTTYPE_RAW, - 'posts' => '', - 'preprocessing' => [], - 'privatekey' => '', - 'publickey' => '', - 'status' => ITEM_STATUS_ACTIVE, - 'tags' => [], - 'timeout' => '', - 'trapper_hosts' => '', - 'trends' => ITEM_NO_STORAGE_VALUE, - 'type' => 0, - 'units' => '', - 'url' => '', - 'username' => '', - 'value_type' => ITEM_VALUE_TYPE_UINT64, - 'valuemapid' => 0, - 'interfaceid' => $this->opt_interfaceid_expected ? 0 : '' - ]; - $this->getInputs($input, array_keys($input)); - - if ($this->getInput('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) { - $input['trends'] = ITEM_NO_STORAGE_VALUE; - } + $item_prototypes = (bool) $this->getInput('prototype', false); - if ($this->getInput('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) { - $input['history'] = ITEM_NO_STORAGE_VALUE; - } + try { + $input = [ + 'type' => DB::getDefault('items', 'type'), + 'value_type' => ITEM_VALUE_TYPE_UINT64, + 'units' => DB::getDefault('items', 'units'), + 'history' => ITEM_NO_STORAGE_VALUE, + 'trends' => ITEM_NO_STORAGE_VALUE, + 'valuemapid' => 0, + 'logtimefmt' => DB::getDefault('items', 'logtimefmt'), + 'description' => DB::getDefault('items', 'description'), + 'status' => DB::getDefault('items', 'status'), + 'discover' => DB::getDefault('items', 'discover'), + 'tags' => [], + 'preprocessing' => [], + + // The fields used for multiple item types. + 'interfaceid' => 0, + 'authtype' => DB::getDefault('items', 'authtype'), + 'username' => DB::getDefault('items', 'username'), + 'password' => DB::getDefault('items', 'password'), + 'timeout' => '', + 'delay' => DB::getDefault('items', 'delay'), + 'trapper_hosts' => DB::getDefault('items', 'trapper_hosts'), + + // Dependent item type specific fields. + 'master_itemid' => 0, + + // HTTP Agent item type specific fields. + 'url' => DB::getDefault('items', 'url'), + 'post_type' => DB::getDefault('items', 'post_type'), + 'posts' => DB::getDefault('items', 'posts'), + 'headers' => [], + 'allow_traps' => DB::getDefault('items', 'allow_traps'), + + // JMX item type specific fields. + 'jmx_endpoint' => DB::getDefault('items', 'jmx_endpoint'), + + // SSH item type specific fields. + 'publickey' => DB::getDefault('items', 'publickey'), + 'privatekey' => DB::getDefault('items', 'privatekey') + ]; - $input = array_intersect_key($input, $this->getInput('visible', [])); + $input = array_intersect_key($input, $this->getInput('visible', [])); + $this->getInputs($input, array_keys($input)); - if (array_key_exists('tags', $input)) { - $input['tags'] = array_filter($input['tags'], function ($tag) { - return ($tag['tag'] !== '' || $tag['value'] !== ''); - }); - } + $options = []; - try { - DBstart(); - $delay_flex = $this->getInput('delay_flex', []); - - if (array_key_exists('delay', $input) && $delay_flex) { - $simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]); - $time_period_parser = new CTimePeriodParser(['usermacros' => true]); - $scheduling_interval_parser = new CSchedulingIntervalParser(['usermacros' => true]); - - foreach ($delay_flex as $interval) { - if ($interval['type'] == ITEM_DELAY_FLEXIBLE) { - if ($interval['delay'] === '' && $interval['period'] === '') { - continue; - } - - if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) { - info(_s('Invalid interval "%1$s".', $interval['delay'])); - throw new Exception(); - } - elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) { - info(_s('Invalid interval "%1$s".', $interval['period'])); - throw new Exception(); - } - - $input['delay'] .= ';'.$interval['delay'].'/'.$interval['period']; - } - else { - if ($interval['schedule'] === '') { - continue; - } + if (array_key_exists('tags', $input)) { + $input['tags'] = prepareItemTags($input['tags']); - if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) { - info(_s('Invalid interval "%1$s".', $interval['schedule'])); - throw new Exception(); - } + $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [ + 'tag' => ['type' => API_STRING_UTF8], + 'value' => ['type' => API_STRING_UTF8] + ]]; - $input['delay'] .= ';'.$interval['schedule']; - } + if (!CApiInputValidator::validateUniqueness($api_input_rules, $input['tags'], '/tags', $error)) { + error($error); + throw new Exception(); } - } - if (array_key_exists('headers', $input) && $input['headers']) { - $input['headers']['value'] += array_fill_keys(array_keys($input['headers']['name']), ''); + $tag_values = []; - $headers = []; - foreach ($input['headers']['name'] as $i => $header_name) { - if ($header_name !== '' || $input['headers']['value'][$i] !== '') { - $headers[$header_name] = $input['headers']['value'][$i]; - } + foreach ($input['tags'] as $tag) { + $tag_values[$tag['tag']][] = $tag['value']; } - $input['headers'] = $headers; + + $options['selectTags'] = ['tag', 'value']; } - if (array_key_exists('preprocessing', $input) && $input['preprocessing']) { + if (array_key_exists('preprocessing', $input)) { $input['preprocessing'] = normalizeItemPreprocessingSteps($input['preprocessing']); } - $items_to_update = []; - $items = $this->getItemsOrPrototypes(); - - foreach ($ids as $id) { - $update_item = []; + if (array_key_exists('delay', $input)) { + $delay_flex = $this->getInput('delay_flex', []); - if (array_key_exists('tags', $input)) { - switch ($this->getInput('mass_update_tags', ZBX_ACTION_ADD)) { - case ZBX_ACTION_ADD: - $unique_tags = []; - foreach (array_merge($items[$id]['tags'], $input['tags']) as $tag) { - $unique_tags[$tag['tag']][$tag['value']] = $tag; - } - - foreach ($unique_tags as $tags_by_name) { - foreach ($tags_by_name as $tag) { - $update_item['tags'][] = $tag; - } - } - break; - - case ZBX_ACTION_REPLACE: - $update_item['tags'] = $input['tags']; - break; - - case ZBX_ACTION_REMOVE: - $diff_tags = []; - foreach ($items[$id]['tags'] as $a) { - foreach ($input['tags'] as $b) { - if ($a['tag'] === $b['tag'] && $a['value'] === $b['value']) { - continue 2; - } - } - - $diff_tags[] = $a; - } - $update_item['tags'] = $diff_tags; - break; - } + if (!isValidCustomIntervals($delay_flex)) { + throw new Exception(); } - if ($prototype || $items[$id]['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) { - $update_item += $input; + $input['delay'] = getDelayWithCustomIntervals($input['delay'], $delay_flex); + } - $type = array_key_exists('type', $input) ? $input['type'] : $items[$id]['type']; + if (array_key_exists('headers', $input)) { + $input['headers'] = prepareItemHeaders($input['headers']); + } - if ($type != ITEM_TYPE_JMX) { - unset($update_item['jmx_endpoint']); - } + $itemids = $this->getInput('ids'); - if ($type != ITEM_TYPE_HTTPAGENT && $type != ITEM_TYPE_SCRIPT) { - unset($update_item['timeout']); - } + if ($item_prototypes) { + $db_items = API::ItemPrototype()->get([ + 'output' => ['type', 'key_', 'value_type', 'templateid', 'authtype', 'allow_traps'], + 'selectHosts' => ['status'], + 'itemids' => $itemids, + 'preservekeys' => true + ] + $options); + } + else { + $db_items = API::Item()->get([ + 'output' => ['type', 'key_', 'value_type', 'templateid', 'flags', 'authtype', 'allow_traps'], + 'selectHosts' => ['status'], + 'itemids' => $itemids, + 'preservekeys' => true + ] + $options); + } + + $items = []; + + foreach ($itemids as $itemid) { + $db_item = $db_items[$itemid]; + + if ($item_prototypes) { + $db_item['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE; } - else if (array_key_exists('status', $input)) { - $items_to_update[] = ['itemid' => $id, 'status' => $input['status']]; + + $item = array_intersect_key($input, getSanitizedItemFields($input + $db_item)); + + if (array_key_exists('tags', $input)) { + $item['tags'] = $this->getTagsToUpdate($db_item, $tag_values); } - if ($update_item) { - $items_to_update[] = ['itemid' => $id] + $update_item; + if ($item) { + $items[] = ['itemid' => $itemid] + $item; } } - if ($items_to_update && !$this->updateItemOrPrototype($items_to_update)) { - throw new Exception(); + $result = true; + + if ($items) { + if ($item_prototypes) { + $response = API::ItemPrototype()->update($items); + } + else { + $response = API::Item()->update($items); + } + + if ($response === false) { + throw new Exception(); + } } } catch (Exception $e) { $result = false; - CMessageHelper::setErrorTitle($prototype ? _('Cannot update item prototypes') : _('Cannot update items')); + CMessageHelper::setErrorTitle( + $item_prototypes ? _('Cannot update item prototypes') : _('Cannot update items') + ); } - if (DBend($result)) { + if ($result) { $messages = CMessageHelper::getMessages(); - $output = ['title' => $prototype ? _('Item prototypes updated') : _('Items updated')]; + $output = ['title' => $item_prototypes ? _('Item prototypes updated') : _('Items updated')]; if (count($messages)) { $output['messages'] = array_column($messages, 'message'); @@ -371,6 +290,59 @@ class CControllerPopupMassupdateItem extends CController { } /** + * Get item tags to update or null if no tags to update found. + * + * @param array $db_item + * @param array $tag_values + * + * @return array + */ + private function getTagsToUpdate(array $db_item, array $tag_values): ?array { + $tags = []; + + switch ($this->getInput('mass_update_tags', ZBX_ACTION_ADD)) { + case ZBX_ACTION_ADD: + foreach ($db_item['tags'] as $db_tag) { + if (array_key_exists($db_tag['tag'], $tag_values) + && in_array($db_tag['value'], $tag_values[$db_tag['tag']])) { + unset($tag_values[$db_tag['tag']][$db_tag['value']]); + } + } + + foreach ($tag_values as $tag => $values) { + foreach ($values as $value) { + $tags[] = ['tag' => (string) $tag, 'value' => $value]; + } + } + + $tags = array_merge($db_item['tags'], $tags); + break; + + case ZBX_ACTION_REPLACE: + foreach ($tag_values as $tag => $values) { + foreach ($values as $value) { + $tags[] = ['tag' => (string) $tag, 'value' => $value]; + } + } + + CArrayHelper::sort($tags, ['tag', 'value']); + CArrayHelper::sort($db_item['tags'], ['tag', 'value']); + break; + + case ZBX_ACTION_REMOVE: + foreach ($db_item['tags'] as $db_tag) { + if (!array_key_exists($db_tag['tag'], $tag_values) + || !in_array($db_tag['value'], $tag_values[$db_tag['tag']])) { + $tags[] = ['tag' => $db_tag['tag'], 'value' => $db_tag['value']]; + } + } + break; + } + + return $tags; + } + + /** * Handle item mass update form initialization. * * @return CControllerResponse |