/** * Checks if any of the given host global macros already exist. If the macros are updated and * the "globalmacroid" field is set, the method will only fail, if a macro with a different globalmacroid exists. * Assumes the "macro", "hostmacroid" fields are valid. * * @param array $globalmacros * @param int $globalmacros[]['globalmacroid'] * @param string $globalmacros[]['macro'] * @param string $globalmacros[]['value'] * * @throws APIException if any of the given macros already exist. */ protected function checkIfGlobalMacrosDontRepeat(array $globalmacros) { if (!$globalmacros) { return; } $macro_names = []; $user_macro_parser = new CUserMacroParser(); // Parse each macro, get unique names and, if context exists, narrow down the search. foreach ($globalmacros as $globalmacro) { $user_macro_parser->parse($globalmacro['macro']); $macro_name = $user_macro_parser->getMacro(); $context = $user_macro_parser->getContext(); if ($context === null) { $macro_names['{$' . $macro_name] = true; } else { // Narrow down the search for macros with contexts. $macro_names['{$' . $macro_name . ':'] = true; } } // When updating with empty array, don't select any data from database. $db_macros = API::getApiService()->select('globalmacro', ['output' => ['globalmacroid', 'macro'], 'search' => ['macro' => array_keys($macro_names)], 'searchByAny' => true]); $existing_macros = []; // Collect existing unique macro names and their contexts. foreach ($db_macros as $db_macro) { $user_macro_parser->parse($db_macro['macro']); $macro_name = $user_macro_parser->getMacro(); $context = $user_macro_parser->getContext(); $existing_macros[$macro_name][$db_macro['globalmacroid']] = $context; } // Compare each macro name and context to existing one. foreach ($globalmacros as $globalmacro) { $user_macro_parser->parse($globalmacro['macro']); $macro_name = $user_macro_parser->getMacro(); $context = $user_macro_parser->getContext(); if (array_key_exists($macro_name, $existing_macros) && in_array($context, $existing_macros[$macro_name], true)) { foreach ($existing_macros[$macro_name] as $globalmacroid => $existing_macro_context) { if ((!array_key_exists('globalmacroid', $globalmacro) || bccomp($globalmacro['globalmacroid'], $globalmacroid) != 0) && $context === $existing_macro_context) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Macro "%1$s" already exists.', $globalmacro['macro'])); } } } } }
/** * Merge list of inherited and host-level macros. * * Returns an array like: * array( * '{$MACRO}' => array( * 'macro' => '{$MACRO}', * 'type' => 0x03, <- MACRO_TYPE_INHERITED, MACRO_TYPE_HOSTMACRO or MACRO_TYPE_BOTH * 'value' => 'effective value', * 'hostmacroid' => 7532, <- optional * 'template' => array( <- optional * 'value' => 'template-level value' * 'templateid' => 10001, * 'name' => 'Template OS Linux' * ), * 'global' => array( <- optional * 'value' => 'global-level value' * ) * ) * ) * * @param array $host_macros the list of host macros * @param array $inherited_macros the list of inherited macros (the output of the getInheritedMacros() function) * * @return array */ function mergeInheritedMacros(array $host_macros, array $inherited_macros) { $user_macro_parser = new CUserMacroParser(); foreach ($inherited_macros as &$inherited_macro) { $inherited_macro['type'] = MACRO_TYPE_INHERITED; $inherited_macro['value'] = array_key_exists('template', $inherited_macro) ? $inherited_macro['template']['value'] : $inherited_macro['global']['value']; } unset($inherited_macro); /* * Global macros and template macros are overwritten by host macros. Macros with contexts require additional * checking for contexts, since {$MACRO:} is the same as {$MACRO:""}. */ foreach ($host_macros as &$host_macro) { if (array_key_exists($host_macro['macro'], $inherited_macros)) { $host_macro = array_merge($inherited_macros[$host_macro['macro']], $host_macro); unset($inherited_macros[$host_macro['macro']]); } else { /* * Cannot use array dereferencing because "$host_macro['macro']" may contain invalid macros * which results in empty array. */ if ($user_macro_parser->parse($host_macro['macro']) == CParser::PARSE_SUCCESS) { $hst_macro = $user_macro_parser->getMacro(); $hst_context = $user_macro_parser->getContext(); if ($hst_context === null) { $host_macro['type'] = 0x0; } else { $match_found = false; foreach ($inherited_macros as $inherited_macro => $inherited_values) { // Safe to use array dereferencing since these values come from database. $user_macro_parser->parse($inherited_macro); $inh_macro = $user_macro_parser->getMacro(); $inh_context = $user_macro_parser->getContext(); if ($hst_macro === $inh_macro && $hst_context === $inh_context) { $match_found = true; $host_macro = array_merge($inherited_macros[$inherited_macro], $host_macro); unset($inherited_macros[$inherited_macro]); break; } } if (!$match_found) { $host_macro['type'] = 0x0; } } } else { $host_macro['type'] = 0x0; } } $host_macro['type'] |= MACRO_TYPE_HOSTMACRO; } unset($host_macro); foreach ($inherited_macros as $inherited_macro) { $host_macros[] = $inherited_macro; } return $host_macros; }
/** * Get macros with values. * * @param array $data * @param array $data[<id>] any identificator * @param array $data['hostids'] the list of host ids; [<hostid1>, ...] * @param array $data['macros'] the list of user macros to resolve, ['<usermacro1>' => null, ...] * * @return array */ protected function getUserMacros(array $data) { // User macros. $hostids = []; foreach ($data as $element) { foreach ($element['hostids'] as $hostid) { $hostids[$hostid] = true; } } if (!$hostids) { return $data; } /* * @var array $host_templates * @var array $host_templates[<hostid>] array of templates */ $host_templates = []; /* * @var array $host_macros * @var array $host_macros[<hostid>] * @var array $host_macros[<hostid>][<macro>] macro base without curly braces * @var string $host_macros[<hostid>][<macro>]['value'] base macro value (without context); can be null * @var array $host_macros[<hostid>][<macro>]['contexts'] context values; ['<context1>' => '<value1>', ...] */ $host_macros = []; $user_macro_parser = new CUserMacroParser(); do { $db_hosts = API::Host()->get(['hostids' => array_keys($hostids), 'templated_hosts' => true, 'output' => ['hostid'], 'selectParentTemplates' => ['templateid'], 'selectMacros' => ['macro', 'value']]); $hostids = []; foreach ($db_hosts as $db_host) { $host_templates[$db_host['hostid']] = zbx_objectValues($db_host['parentTemplates'], 'templateid'); foreach ($db_host['macros'] as $db_macro) { if ($user_macro_parser->parse($db_macro['macro']) != CParser::PARSE_SUCCESS) { continue; } $macro = $user_macro_parser->getMacro(); $context = $user_macro_parser->getContext(); if (!array_key_exists($db_host['hostid'], $host_macros)) { $host_macros[$db_host['hostid']] = []; } if (!array_key_exists($macro, $host_macros[$db_host['hostid']])) { $host_macros[$db_host['hostid']][$macro] = ['value' => null, 'contexts' => []]; } if ($context === null) { $host_macros[$db_host['hostid']][$macro]['value'] = $db_macro['value']; } else { $host_macros[$db_host['hostid']][$macro]['contexts'][$context] = $db_macro['value']; } } } foreach ($db_hosts as $db_host) { // Only unprocessed templates will be populated. foreach ($host_templates[$db_host['hostid']] as $templateid) { if (!array_key_exists($templateid, $host_templates)) { $hostids[$templateid] = true; } } } } while ($hostids); $all_macros_resolved = true; $user_macro_parser = new CUserMacroParser(); foreach ($data as &$element) { $hostids = []; foreach ($element['hostids'] as $hostid) { $hostids[$hostid] = true; } $hostids = array_keys($hostids); natsort($hostids); foreach ($element['macros'] as $usermacro => &$value) { if ($user_macro_parser->parse($usermacro) == CParser::PARSE_SUCCESS) { $value = $this->getHostUserMacros($hostids, $user_macro_parser->getMacro(), $user_macro_parser->getContext(), $host_templates, $host_macros); if ($value['value'] === null) { $all_macros_resolved = false; } } else { // This macro cannot be resolved. $value = ['value' => $usermacro, 'value_default' => null]; } } unset($value); } unset($element); if (!$all_macros_resolved) { // Global macros. $db_global_macros = API::UserMacro()->get(['output' => ['macro', 'value'], 'globalmacro' => true]); /* * @var array $global_macros * @var array $global_macros[<macro>] macro base without curly braces * @var string $global_macros[<macro>]['value'] base macro value (without context); can be null * @var array $global_macros[<macro>]['contexts'] context values; ['<context1>' => '<value1>', ...] */ $global_macros = []; foreach ($db_global_macros as $db_global_macro) { if ($user_macro_parser->parse($db_global_macro['macro']) == CParser::PARSE_SUCCESS) { $macro = $user_macro_parser->getMacro(); $context = $user_macro_parser->getContext(); if (!array_key_exists($macro, $global_macros)) { $global_macros[$macro] = ['value' => null, 'contexts' => []]; } if ($context === null) { $global_macros[$macro]['value'] = $db_global_macro['value']; } else { $global_macros[$macro]['contexts'][$context] = $db_global_macro['value']; } } } foreach ($data as &$element) { foreach ($element['macros'] as $usermacro => &$value) { if ($value['value'] === null && $user_macro_parser->parse($usermacro) == CParser::PARSE_SUCCESS) { $macro = $user_macro_parser->getMacro(); $context = $user_macro_parser->getContext(); if (array_key_exists($macro, $global_macros)) { if ($context !== null && array_key_exists($context, $global_macros[$macro]['contexts'])) { $value['value'] = $global_macros[$macro]['contexts'][$context]; } elseif ($global_macros[$macro]['value'] !== null) { if ($context === null) { $value['value'] = $global_macros[$macro]['value']; } elseif ($value['value_default'] === null) { $value['value_default'] = $global_macros[$macro]['value']; } } } } } unset($value); } unset($element); } foreach ($data as &$element) { foreach ($element['macros'] as $usermacro => &$value) { if ($value['value'] !== null) { $value = $value['value']; } elseif ($value['value_default'] !== null) { $value = $value['value_default']; } else { // Unresolved macro. $value = $usermacro; } } unset($value); } unset($element); return $data; }