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.

597 lines
18 KiB

<?php declare(strict_types = 0);
/*
** 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.
**/
require_once dirname(__FILE__).'/../../include/forms.inc.php';
/**
* Configuration host edit controller for full-page form.
*/
class CControllerHostEdit extends CController {
/**
* Edited host.
*
* @var ?array
*/
protected $host;
protected function init() {
$this->disableCsrfValidation();
}
protected function checkInput(): bool {
$fields = [
'hostid' => 'db hosts.hostid',
'groupids' => 'array_db hosts_groups.groupid',
'clone' => 'in 1',
'host' => 'db hosts.host',
'visiblename' => 'db hosts.name',
'description' => 'db hosts.description',
'status' => 'db hosts.status|in '.implode(',', [HOST_STATUS_MONITORED,
HOST_STATUS_NOT_MONITORED
]),
'proxyid' => 'db hosts.proxyid',
'interfaces' => 'array',
'mainInterfaces' => 'array',
'groups' => 'array',
'tags' => 'array',
'templates' => 'array_db hosts.hostid',
'add_templates' => 'array_db hosts.hostid',
'ipmi_authtype' => 'in '.implode(',', [IPMI_AUTHTYPE_DEFAULT, IPMI_AUTHTYPE_NONE, IPMI_AUTHTYPE_MD2,
IPMI_AUTHTYPE_MD5, IPMI_AUTHTYPE_STRAIGHT, IPMI_AUTHTYPE_OEM,
IPMI_AUTHTYPE_RMCP_PLUS
]),
'ipmi_privilege' => 'in '.implode(',', [IPMI_PRIVILEGE_CALLBACK, IPMI_PRIVILEGE_USER,
IPMI_PRIVILEGE_OPERATOR, IPMI_PRIVILEGE_ADMIN, IPMI_PRIVILEGE_OEM
]),
'ipmi_username' => 'db hosts.ipmi_username',
'ipmi_password' => 'db hosts.ipmi_password',
'show_inherited_macros' => 'in 0,1',
'tls_connect' => 'db hosts.tls_connect|in '.implode(',', [HOST_ENCRYPTION_NONE,
HOST_ENCRYPTION_PSK, HOST_ENCRYPTION_CERTIFICATE
]),
'tls_accept' => 'db hosts.tls_accept|ge 0|le '.
(0 | HOST_ENCRYPTION_NONE | HOST_ENCRYPTION_PSK | HOST_ENCRYPTION_CERTIFICATE),
'tls_subject' => 'db hosts.tls_subject',
'tls_issuer' => 'db hosts.tls_issuer',
'tls_psk_identity' => 'db hosts.tls_psk_identity',
'tls_psk' => 'db hosts.tls_psk',
'inventory_mode' => 'db host_inventory.inventory_mode|in '.implode(',', [HOST_INVENTORY_DISABLED,
HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC
]),
'host_inventory' => 'array',
'macros' => 'array',
'valuemaps' => 'array'
];
$ret = ($this->validateInput($fields) && $this->checkCloneSourceHostId());
if (!$ret) {
$this->setResponse(new CControllerResponseFatal());
}
return $ret;
}
/**
* Check if source hostid is given to clone host.
*
* @return bool
*/
protected function checkCloneSourceHostId(): bool {
if ($this->hasInput('clone')) {
return $this->hasInput('hostid');
}
return true;
}
protected function checkPermissions(): bool {
if (!$this->checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS)) {
return false;
}
if ($this->hasInput('hostid')) {
$hosts = API::Host()->get([
'output' => [],
'hostids' => $this->getInput('hostid'),
'editable' => true,
'limit' => 1
]);
if (!$hosts) {
return false;
}
}
return true;
}
protected function doAction(): void {
$clone_hostid = null;
if ($this->hasInput('hostid')) {
if ($this->hasInput('clone')) {
$clone_hostid = $this->getInput('hostid');
$this->host = ['hostid' => null];
}
else {
$hosts = API::Host()->get([
'output' => ['hostid', 'host', 'name', 'status', 'description', 'proxyid', 'ipmi_authtype',
'ipmi_privilege', 'ipmi_username', 'ipmi_password', 'tls_connect', 'tls_accept', 'tls_issuer',
'tls_subject', 'flags', 'inventory_mode'
],
'selectDiscoveryRule' => ['itemid', 'name', 'parent_hostid'],
'selectHostGroups' => ['groupid'],
'selectHostDiscovery' => ['parent_hostid'],
'selectInterfaces' => ['interfaceid', 'type', 'main', 'available', 'error', 'details', 'ip', 'dns',
'port', 'useip'
],
'selectInventory' => array_column(getHostInventories(), 'db_field'),
'selectMacros' => ['hostmacroid', 'macro', 'value', 'description', 'type', 'automatic'],
'selectParentTemplates' => ['templateid', 'name', 'link_type'],
'selectTags' => ['tag', 'value', 'automatic'],
'selectValueMaps' => ['valuemapid', 'name', 'mappings'],
'hostids' => $this->getInput('hostid')
]);
$this->host = $hosts[0];
$this->host['groups'] = $this->host['hostgroups'];
unset($this->host['hostgroups']);
}
}
if (array_key_exists('interfaces', (array) $this->host) && $this->host['interfaces']) {
$interface_items = API::HostInterface()->get([
'output' => [],
'selectItems' => API_OUTPUT_COUNT,
'hostids' => [$this->host['hostid']],
'preservekeys' => true
]);
foreach ($this->host['interfaces'] as &$interface) {
if (!array_key_exists($interface['interfaceid'], $interface_items)) {
continue;
}
$interface['items'] = $interface_items[$interface['interfaceid']]['items'];
}
unset($interface);
}
$this->host = (array) $this->host + $this->getInputValues() + $this->getHostDefaultValues();
$data = [
'form_action' => $this->host['hostid'] ? 'host.update' : 'host.create',
'hostid' => $this->host['hostid'],
'clone' => $this->hasInput('clone') ? 1 : null,
'clone_hostid' => $clone_hostid,
'host' => $this->host,
'is_psk_edit' => $this->hasInput('tls_psk_identity') && $this->hasInput('tls_psk'),
'show_inherited_macros' => $this->getInput('show_inherited_macros', 0),
'allowed_ui_conf_templates' => CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_TEMPLATES),
'warnings' => [],
'user' => [
'debug_mode' => $this->getDebugMode()
]
];
// Rename fields according names of host edit form.
$data['host'] = CArrayHelper::renameKeys($data['host'], [
'name' => 'visiblename'
]);
// Display empty visible name if equal to host name.
if ($data['host']['host'] === $data['host']['visiblename']) {
$data['host']['visiblename'] = '';
}
// Prepare tags for edit form.
if (!$data['host']['tags']) {
$data['host']['tags'][] = ['tag' => '', 'value' => '', 'automatic' => ZBX_TAG_MANUAL];
}
else {
foreach ($data['host']['tags'] as &$tag) {
$tag += ['automatic' => ZBX_TAG_MANUAL];
}
unset($tag);
CArrayHelper::sort($data['host']['tags'],
[['field' => 'automatic', 'order' => ZBX_SORT_DOWN], 'tag', 'value']
);
}
$data['host']['macros'] = array_values(order_macros($data['host']['macros'], 'macro'));
if (!$data['host']['macros']) {
$data['host']['macros'][] = [
'type' => ZBX_MACRO_TYPE_TEXT,
'macro' => '',
'value' => '',
'description' => '',
'automatic' => ZBX_USERMACRO_MANUAL
];
}
foreach ($data['host']['macros'] as &$macro) {
if (array_key_exists('automatic', $macro) && $macro['automatic'] == ZBX_USERMACRO_AUTOMATIC) {
$macro['discovery_state'] = CControllerHostMacrosList::DISCOVERY_STATE_AUTOMATIC;
$macro['original'] = [
'value' => getMacroConfigValue($macro),
'description' => $macro['description'],
'type' => $macro['type']
];
}
else {
$macro['discovery_state'] = CControllerHostMacrosList::DISCOVERY_STATE_MANUAL;
}
unset($macro['automatic']);
}
unset($macro);
// Reset Secret text macros and set warning for cloned host.
if ($this->hasInput('clone')) {
foreach ($data['host']['macros'] as &$macro) {
if (array_key_exists('allow_revert', $macro) && array_key_exists('value', $macro)) {
$macro['deny_revert'] = true;
unset($macro['allow_revert']);
}
}
unset($macro);
}
if ($data['host']['hostid'] === null) {
$secret_macro_reset = false;
foreach ($data['host']['macros'] as &$macro) {
if ($macro['type'] == ZBX_MACRO_TYPE_SECRET && !array_key_exists('value', $macro)) {
$macro = [
'type' => ZBX_MACRO_TYPE_TEXT,
'value' => ''
] + $macro;
unset($macro['allow_revert']);
$secret_macro_reset = true;
}
}
unset($macro);
if ($secret_macro_reset) {
$data['warnings'][] = _('The cloned host contains user defined macros with type "Secret text". The value and type of these macros were reset.');
}
}
foreach ($data['host']['macros'] as &$macro) {
if ($macro['type'] == ZBX_MACRO_TYPE_SECRET
&& !array_key_exists('deny_revert', $macro) && !array_key_exists('value', $macro)) {
$macro['allow_revert'] = true;
}
}
unset($macro);
order_result($data['host']['valuemaps'], 'name');
$data['host']['valuemaps'] = array_values($data['host']['valuemaps']);
if ($this->hasInput('groupids')) {
$data['groupids'] = $this->getInput('groupids', []);
}
// Extend data for view.
$data['groups_ms'] = $this->hostGroupsForMultiselect($data['host']['groups'], $clone_hostid !== null);
unset($data['groups']);
if ($clone_hostid !== null && count($data['host']['groups']) != count($data['groups_ms'])) {
$data['warnings'][] = _("The host being cloned belongs to a host group you don't have write permissions to. Non-writable group has been removed from the new host.");
}
CArrayHelper::sort($data['host']['parentTemplates'], ['name']);
$this->extendLinkedTemplates($data['editable_templates']);
$this->extendProxies($data['proxies']);
$this->extendInventory($data['inventory_items'], $data['inventory_fields']);
$data['is_discovery_rule_editable'] = $this->host['discoveryRule']
&& API::DiscoveryRule()->get([
'output' => [],
'itemids' => $this->host['discoveryRule']['itemid'],
'editable' => true
]);
$response = new CControllerResponseData($data);
$response->setTitle(_('Configuration of host'));
$this->setResponse($response);
}
/**
* Function to prepare data for host group multiselect.
*
* @param array $groups
* @param bool $skip_non_editable Whether to include non-editable host groups into response.
*
* @return array
*/
protected function hostGroupsForMultiselect(array $groups, $skip_non_editable = false): array {
$groupids = [];
foreach ($groups as $group) {
if (array_key_exists('new', $group)) {
continue;
}
$groupids[] = $group['groupid'];
}
// Select all accessible host groups.
$groups_all = $groupids
? API::HostGroup()->get([
'output' => ['name'],
'groupids' => $groupids,
'preservekeys' => true
])
: [];
// Editable host groups.
$groups_rw = ($groups_all && CWebUser::getType() != USER_TYPE_SUPER_ADMIN)
? API::HostGroup()->get([
'output' => [],
'groupids' => array_keys($groups_all),
'editable' => true,
'preservekeys' => true
])
: [];
$groups_ms = [];
foreach ($groups as $group) {
if (array_key_exists('new', $group)) {
$groups_ms[] = [
'id' => $group['new'],
'name' => $group['new'].' ('._x('new', 'new element in multiselect').')',
'isNew' => true
];
}
elseif (array_key_exists($group['groupid'], $groups_all)) {
$is_editable = array_key_exists($group['groupid'], $groups_rw);
if (CWebUser::getType() != USER_TYPE_SUPER_ADMIN && $skip_non_editable && !$is_editable) {
continue;
}
$groups_ms[] = [
'id' => $group['groupid'],
'name' => $groups_all[$group['groupid']]['name'],
'disabled' => CWebUser::getType() != USER_TYPE_SUPER_ADMIN && !$is_editable
];
}
}
CArrayHelper::sort($groups_ms, ['name']);
return $groups_ms;
}
/**
* Function to prepare data for Linked templates list.
*
* @param array $editable_templates
*
* @return void
*/
protected function extendLinkedTemplates(?array &$editable_templates): void {
$editable_templates = $this->host['parentTemplates']
? API::Template()->get([
'output' => ['templateid'],
'templateids' => array_column($this->host['parentTemplates'], 'templateid'),
'editable' => true,
'preservekeys' => true
])
: [];
}
/**
* Function to select data for 'Monitored by proxy' field.
*
* @param array $proxies
*
* @return void
*/
protected function extendProxies(?array &$proxies): void {
if ($this->host['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
$proxies = $this->host['proxyid'] != 0
? API::Proxy()->get([
'output' => ['name', 'proxyid'],
'proxyids' => [$this->host['proxyid']],
'preservekeys' => true
])
: [];
}
else {
$proxies = API::Proxy()->get([
'output' => ['name', 'proxyid'],
'preservekeys' => true
]);
CArrayHelper::sort($proxies, ['name']);
}
$proxies = array_column($proxies, 'name', 'proxyid');
}
/**
* Function to prepare data of inventory fields and find items selected to populate each of inventory fields.
*
* @param array $inventory_items
* @param array $inventory_fields
*
* @return void
*/
protected function extendInventory(?array &$inventory_items, ?array &$inventory_fields): void {
// Select inventory fields and extend each field with details of database schema.
$db_fields = DB::getSchema('host_inventory');
$inventory_fields = array_map(function ($field) use ($db_fields) {
return $field += array_intersect_key($db_fields['fields'][$field['db_field']], [
'type' => null,
'length' => null
]);
}, getHostInventories());
// Select inventory items.
$inventory_items = $this->host['hostid']
? API::Item()->get([
'output' => ['inventory_link', 'itemid', 'name'],
'hostids' => $this->host['hostid'],
'filter' => [
'inventory_link' => array_keys($inventory_fields)
]
])
: [];
$inventory_items = zbx_toHash($inventory_items, 'inventory_link');
}
/**
* Returns array with post input values.
*
* @return array
*/
protected function getInputValues(): array {
$inputs = [];
if ($this->hasInput('clone')) {
$inputs['groups'] = [];
foreach ($this->getInput('groups', []) as $group) {
if (is_array($group) && array_key_exists('new', $group)) {
$inputs['groups'][$group['new']] = $group;
}
else {
$inputs['groups'][$group] = ['groupid' => $group];
}
}
$inputs['name'] = $this->getInput('visiblename', '');
$inputs['inventory'] = $this->getInput('host_inventory', []);
$this->getInputs($inputs, [
'host', 'description', 'status', 'proxyid', 'ipmi_authtype', 'ipmi_privilege', 'ipmi_username',
'ipmi_password', 'tls_connect', 'tls_accept', 'tls_subject', 'tls_issuer', 'tls_psk_identity',
'tls_psk', 'tags', 'inventory_mode', 'host_inventory'
]);
$field_add_templates = $this->getInput('add_templates', []);
$field_templates = $this->getInput('templates', []);
$linked_templates = API::Template()->get([
'output' => ['templateid', 'name'],
'templateids' => array_merge($field_add_templates, $field_templates),
'preservekeys' => true
]);
// Remove inherited macros data.
$macros = cleanInheritedMacros($this->getInput('macros', []));
// Remove empty new macro lines.
$macros = array_filter($macros, function ($macro) {
$keys = array_flip(['hostmacroid', 'macro', 'value', 'description']);
return (bool) array_filter(array_intersect_key($macro, $keys));
});
$inputs['macros'] = array_map(function ($macro) {
unset($macro['hostmacroid']);
return $macro + ['description' => ''];
}, $macros);
$inputs['valuemaps'] = array_map(function ($valuemap) {
unset($valuemap['valuemapid']);
return $valuemap;
}, $this->getInput('valuemaps', []));
$main_interfaces = $this->getInput('mainInterfaces', []);
$inputs['interfaces'] = $this->getInput('interfaces', []);
foreach ($inputs['interfaces'] as &$interface) {
$interface['main'] = (in_array($interface['interfaceid'], $main_interfaces))
? INTERFACE_PRIMARY
: INTERFACE_SECONDARY;
unset($interface['interfaceid'], $interface['items']);
}
unset($interface);
$inputs['parentTemplates'] = array_intersect_key($linked_templates, array_flip($field_templates));
// When cloning host, templates should be manually linked.
foreach ($inputs['parentTemplates'] as &$template) {
$template['link_type'] = TEMPLATE_LINK_MANUAL;
}
unset($template);
$inputs['add_templates'] = array_map(function ($tmpl) {
return CArrayHelper::renameKeys($tmpl, ['templateid' => 'id']);
}, array_intersect_key($linked_templates, array_flip($field_add_templates)));
}
elseif (!$this->host) {
// Prefill host groups when creating a new host.
$inputs['groups'] = $this->hasInput('groupids')
? zbx_toObject($this->getInput('groupids'), 'groupid')
: [];
}
return $inputs;
}
/**
* Returns array containing default values of all host edit form fields.
*
* @return array
*/
protected function getHostDefaultValues(): array {
return [
'hostid' => null,
'name' => '',
'host' => '',
'proxyid' => '0',
'status' => HOST_STATUS_MONITORED,
'ipmi_authtype' => IPMI_AUTHTYPE_DEFAULT,
'ipmi_privilege' => IPMI_PRIVILEGE_USER,
'ipmi_username' => '',
'ipmi_password' => '',
'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
'description' => '',
'tls_connect' => HOST_ENCRYPTION_NONE,
'tls_accept' => HOST_ENCRYPTION_NONE,
'tls_issuer' => '',
'tls_subject' => '',
'tls_psk_identity' => '',
'tls_psk' => '',
'tags' => [],
'groups' => [],
'parentTemplates' => [],
'discoveryRule' => [],
'interfaces' => [],
'macros' => [],
'inventory' => [],
'valuemaps' => [],
'inventory_mode' => CSettingsHelper::get(CSettingsHelper::DEFAULT_INVENTORY_MODE)
];
}
}