1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22require_once dirname(__FILE__).'/include/config.inc.php'; 23require_once dirname(__FILE__).'/include/hosts.inc.php'; 24require_once dirname(__FILE__).'/include/items.inc.php'; 25require_once dirname(__FILE__).'/include/forms.inc.php'; 26 27$page['title'] = _('Configuration of host prototypes'); 28$page['file'] = 'host_prototypes.php'; 29$page['scripts'] = ['effects.js', 'class.cviewswitcher.js', 'multiselect.js']; 30 31require_once dirname(__FILE__).'/include/page_header.php'; 32 33// VAR TYPE OPTIONAL FLAGS VALIDATION EXCEPTION 34$fields = [ 35 'hostid' => [T_ZBX_INT, O_NO, P_SYS, DB_ID, '(isset({form}) && ({form} == "update"))'], 36 'parent_discoveryid' => [T_ZBX_INT, O_MAND, P_SYS, DB_ID, null], 37 'host' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, 'isset({add}) || isset({update})', _('Host name')], 38 'name' => [T_ZBX_STR, O_OPT, null, null, 'isset({add}) || isset({update})'], 39 'status' => [T_ZBX_INT, O_OPT, null, IN([HOST_STATUS_NOT_MONITORED, HOST_STATUS_MONITORED]), null], 40 'inventory_mode' => [T_ZBX_INT, O_OPT, null, IN([HOST_INVENTORY_DISABLED, HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]), null], 41 'templates' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, null], 42 'add_template' => [T_ZBX_STR, O_OPT, null, null, null], 43 'add_templates' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, null], 44 'group_links' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, null], 45 'group_prototypes' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, null], 46 'unlink' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 47 'group_hostid' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 48 'show_inherited_macros' => [T_ZBX_INT, O_OPT, null, IN([0,1]), null], 49 // actions 50 'action' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, 51 IN('"hostprototype.massdelete","hostprototype.massdisable",'. 52 '"hostprototype.massenable"' 53 ), 54 null 55 ], 56 'add' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 57 'update' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 58 'clone' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 59 'delete' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 60 'cancel' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 61 'form' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 62 'form_refresh' => [T_ZBX_INT, O_OPT, null, null, null], 63 // sort and sortorder 64 'sort' => [T_ZBX_STR, O_OPT, P_SYS, IN('"name","status"'), null], 65 'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null] 66]; 67check_fields($fields); 68 69// permissions 70if (getRequest('parent_discoveryid')) { 71 $discoveryRule = API::DiscoveryRule()->get([ 72 'itemids' => getRequest('parent_discoveryid'), 73 'output' => API_OUTPUT_EXTEND, 74 'selectHosts' => ['flags'], 75 'editable' => true 76 ]); 77 $discoveryRule = reset($discoveryRule); 78 if (!$discoveryRule || $discoveryRule['hosts'][0]['flags'] == ZBX_FLAG_DISCOVERY_CREATED) { 79 access_deny(); 80 } 81 82 if (getRequest('hostid')) { 83 $hostPrototype = API::HostPrototype()->get([ 84 'hostids' => getRequest('hostid'), 85 'output' => API_OUTPUT_EXTEND, 86 'selectGroupLinks' => API_OUTPUT_EXTEND, 87 'selectGroupPrototypes' => API_OUTPUT_EXTEND, 88 'selectTemplates' => ['templateid', 'name'], 89 'selectParentHost' => ['hostid'], 90 'selectInventory' => ['inventory_mode'], 91 'editable' => true 92 ]); 93 $hostPrototype = reset($hostPrototype); 94 if (!$hostPrototype) { 95 access_deny(); 96 } 97 } 98} 99else { 100 access_deny(); 101} 102 103/* 104 * Actions 105 */ 106// add templates to the list 107if (getRequest('add_template')) { 108 foreach (getRequest('add_templates', []) as $templateId) { 109 $_REQUEST['templates'][$templateId] = $templateId; 110 } 111} 112// unlink templates 113elseif (getRequest('unlink')) { 114 foreach (getRequest('unlink') as $templateId => $value) { 115 unset($_REQUEST['templates'][$templateId]); 116 } 117} 118elseif (isset($_REQUEST['delete']) && isset($_REQUEST['hostid'])) { 119 DBstart(); 120 $result = API::HostPrototype()->delete([getRequest('hostid')]); 121 $result = DBend($result); 122 123 if ($result) { 124 uncheckTableRows($discoveryRule['itemid']); 125 } 126 show_messages($result, _('Host prototype deleted'), _('Cannot delete host prototypes')); 127 128 unset($_REQUEST['hostid'], $_REQUEST['form']); 129} 130elseif (isset($_REQUEST['clone']) && isset($_REQUEST['hostid'])) { 131 unset($_REQUEST['hostid']); 132 if (hasRequest('group_prototypes')) { 133 foreach ($_REQUEST['group_prototypes'] as &$groupPrototype) { 134 unset($groupPrototype['group_prototypeid']); 135 } 136 unset($groupPrototype); 137 } 138 $_REQUEST['form'] = 'clone'; 139} 140elseif (hasRequest('add') || hasRequest('update')) { 141 DBstart(); 142 143 $newHostPrototype = [ 144 'host' => getRequest('host', ''), 145 'name' => (getRequest('name', '') === '') ? getRequest('host', '') : getRequest('name', ''), 146 'status' => getRequest('status', HOST_STATUS_NOT_MONITORED), 147 'groupLinks' => [], 148 'groupPrototypes' => [], 149 'templates' => getRequest('templates', []) 150 ]; 151 152 // If 'inventory' is present, API requires 'inventory_mode' to have a value, but templated prototypes don't have it. 153 if (hasRequest('inventory_mode')) { 154 $newHostPrototype['inventory']['inventory_mode'] = getRequest('inventory_mode'); 155 } 156 157 // API requires 'templateid' property. 158 if ($newHostPrototype['templates']) { 159 $newHostPrototype['templates'] = zbx_toObject($newHostPrototype['templates'], 'templateid'); 160 } 161 162 // add custom group prototypes 163 foreach (getRequest('group_prototypes', []) as $groupPrototype) { 164 if (!$groupPrototype['group_prototypeid']) { 165 unset($groupPrototype['group_prototypeid']); 166 } 167 168 if ($groupPrototype['name'] !== '') { 169 $newHostPrototype['groupPrototypes'][] = $groupPrototype; 170 } 171 } 172 173 if (getRequest('hostid')) { 174 $newHostPrototype['hostid'] = getRequest('hostid'); 175 176 if (!$hostPrototype['templateid']) { 177 // add group prototypes based on existing host groups 178 $groupPrototypesByGroupId = zbx_toHash($hostPrototype['groupLinks'], 'groupid'); 179 unset($groupPrototypesByGroupId[0]); 180 foreach (getRequest('group_links', []) as $groupId) { 181 if (isset($groupPrototypesByGroupId[$groupId])) { 182 $newHostPrototype['groupLinks'][] = [ 183 'groupid' => $groupPrototypesByGroupId[$groupId]['groupid'], 184 'group_prototypeid' => $groupPrototypesByGroupId[$groupId]['group_prototypeid'] 185 ]; 186 } 187 else { 188 $newHostPrototype['groupLinks'][] = [ 189 'groupid' => $groupId 190 ]; 191 } 192 } 193 } 194 else { 195 unset($newHostPrototype['groupPrototypes'], $newHostPrototype['groupLinks']); 196 } 197 198 $newHostPrototype = CArrayHelper::unsetEqualValues($newHostPrototype, $hostPrototype, ['hostid']); 199 $result = API::HostPrototype()->update($newHostPrototype); 200 201 show_messages($result, _('Host prototype updated'), _('Cannot update host prototype')); 202 } 203 else { 204 $newHostPrototype['ruleid'] = getRequest('parent_discoveryid'); 205 206 // add group prototypes based on existing host groups 207 foreach (getRequest('group_links', []) as $groupId) { 208 $newHostPrototype['groupLinks'][] = [ 209 'groupid' => $groupId 210 ]; 211 } 212 213 $result = API::HostPrototype()->create($newHostPrototype); 214 215 show_messages($result, _('Host prototype added'), _('Cannot add host prototype')); 216 } 217 218 $result = DBend($result); 219 220 if ($result) { 221 uncheckTableRows($discoveryRule['itemid']); 222 unset($_REQUEST['itemid'], $_REQUEST['form']); 223 } 224} 225// GO 226elseif (hasRequest('action') && str_in_array(getRequest('action'), ['hostprototype.massenable', 'hostprototype.massdisable']) && hasRequest('group_hostid')) { 227 $status = (getRequest('action') == 'hostprototype.massenable') ? HOST_STATUS_MONITORED : HOST_STATUS_NOT_MONITORED; 228 $update = []; 229 230 DBstart(); 231 foreach ((array) getRequest('group_hostid') as $hostPrototypeId) { 232 $update[] = [ 233 'hostid' => $hostPrototypeId, 234 'status' => $status 235 ]; 236 } 237 238 $result = API::HostPrototype()->update($update); 239 $result = DBend($result); 240 241 $updated = count($update); 242 243 $messageSuccess = _n('Host prototype updated', 'Host prototypes updated', $updated); 244 $messageFailed = _n('Cannot update host prototype', 'Cannot update host prototypes', $updated); 245 246 if ($result) { 247 uncheckTableRows($discoveryRule['itemid']); 248 } 249 show_messages($result, $messageSuccess, $messageFailed); 250} 251elseif (hasRequest('action') && getRequest('action') == 'hostprototype.massdelete' && getRequest('group_hostid')) { 252 DBstart(); 253 $result = API::HostPrototype()->delete(getRequest('group_hostid')); 254 $result = DBend($result); 255 256 if ($result) { 257 uncheckTableRows($discoveryRule['itemid']); 258 } 259 show_messages($result, _('Host prototypes deleted'), _('Cannot delete host prototypes')); 260} 261 262if (hasRequest('action') && hasRequest('group_hostid') && !$result) { 263 $host_prototypes = API::HostPrototype()->get([ 264 'output' => [], 265 'hostids' => getRequest('group_hostid'), 266 'editable' => true 267 ]); 268 uncheckTableRows($discoveryRule['itemid'], zbx_objectValues($host_prototypes, 'hostid')); 269} 270 271$config = select_config(); 272 273/* 274 * Display 275 */ 276if (hasRequest('form')) { 277 $data = [ 278 'discovery_rule' => $discoveryRule, 279 'host_prototype' => [ 280 'hostid' => getRequest('hostid', 0), 281 'templateid' => getRequest('hostid') ? $hostPrototype['templateid'] : 0, 282 'host' => getRequest('host'), 283 'name' => getRequest('name'), 284 'status' => getRequest('status', HOST_STATUS_NOT_MONITORED), 285 'templates' => [], 286 'inventory' => [ 287 'inventory_mode' => getRequest('inventory_mode', $config['default_inventory_mode']) 288 ], 289 'groupPrototypes' => getRequest('group_prototypes', []) 290 ], 291 'groups' => [], 292 'show_inherited_macros' => getRequest('show_inherited_macros', 0), 293 'templates' => [] 294 ]; 295 296 // add already linked and new templates 297 $data['host_prototype']['templates'] = API::Template()->get([ 298 'output' => ['templateid', 'name'], 299 'templateids' => getRequest('templates', []) 300 ]); 301 302 // add parent host 303 $parentHost = API::Host()->get([ 304 'output' => API_OUTPUT_EXTEND, 305 'selectGroups' => ['groupid', 'name'], 306 'selectInterfaces' => API_OUTPUT_EXTEND, 307 'selectMacros' => ['macro', 'value'], 308 'hostids' => $discoveryRule['hostid'], 309 'templated_hosts' => true 310 ]); 311 $parentHost = reset($parentHost); 312 $data['parent_host'] = $parentHost; 313 314 if (getRequest('group_links')) { 315 $data['groups'] = API::HostGroup()->get([ 316 'output' => ['groupid', 'name'], 317 'groupids' => getRequest('group_links'), 318 'editable' => true, 319 'preservekeys' => true 320 ]); 321 } 322 323 if ($parentHost['proxy_hostid']) { 324 $proxy = API::Proxy()->get([ 325 'output' => ['host', 'proxyid'], 326 'proxyids' => $parentHost['proxy_hostid'], 327 'limit' => 1 328 ]); 329 $data['proxy'] = reset($proxy); 330 } 331 332 if (!hasRequest('form_refresh')) { 333 if ($data['host_prototype']['hostid'] != 0) { 334 // When opening existing host prototype, display all values from database. 335 $data['host_prototype'] = array_merge($data['host_prototype'], $hostPrototype); 336 337 if (!array_key_exists('inventory_mode', $data['host_prototype']['inventory'])) { 338 $data['host_prototype']['inventory']['inventory_mode'] = HOST_INVENTORY_DISABLED; 339 } 340 341 $groupids = zbx_objectValues($data['host_prototype']['groupLinks'], 'groupid'); 342 $data['groups'] = API::HostGroup()->get([ 343 'output' => ['groupid', 'name'], 344 'groupids' => $groupids, 345 'preservekeys' => true 346 ]); 347 348 $n = 0; 349 foreach ($groupids as $groupid) { 350 if (!array_key_exists($groupid, $data['groups'])) { 351 $postfix = (++$n > 1) ? ' ('.$n.')' : ''; 352 $data['groups'][$groupid] = [ 353 'groupid' => $groupid, 354 'name' => _('Inaccessible group').$postfix, 355 'inaccessible' => true 356 ]; 357 } 358 } 359 } 360 else { 361 // Set default values for new host prototype. 362 $data['host_prototype']['status'] = HOST_STATUS_MONITORED; 363 } 364 } 365 366 $data['templates'] = makeHostPrototypeTemplatesHtml($data['host_prototype']['hostid'], 367 getHostPrototypeParentTemplates([$data['host_prototype']]) 368 ); 369 370 // Select writable templates 371 $templateids = zbx_objectValues($data['host_prototype']['templates'], 'templateid'); 372 $data['host_prototype']['writable_templates'] = []; 373 374 if ($templateids) { 375 $data['host_prototype']['writable_templates'] = API::Template()->get([ 376 'output' => ['templateid'], 377 'templateids' => $templateids, 378 'editable' => true, 379 'preservekeys' => true 380 ]); 381 } 382 383 // order linked templates 384 CArrayHelper::sort($data['host_prototype']['templates'], ['name']); 385 386 // render view 387 $itemView = new CView('configuration.host.prototype.edit', $data); 388 $itemView->render(); 389 $itemView->show(); 390} 391else { 392 $sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name')); 393 $sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP)); 394 395 CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR); 396 CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR); 397 398 $data = [ 399 'form' => getRequest('form'), 400 'parent_discoveryid' => getRequest('parent_discoveryid'), 401 'discovery_rule' => $discoveryRule, 402 'sort' => $sortField, 403 'sortorder' => $sortOrder 404 ]; 405 406 // get items 407 $data['hostPrototypes'] = API::HostPrototype()->get([ 408 'discoveryids' => $data['parent_discoveryid'], 409 'output' => API_OUTPUT_EXTEND, 410 'selectTemplates' => ['templateid', 'name'], 411 'editable' => true, 412 'sortfield' => $sortField, 413 'limit' => $config['search_limit'] + 1 414 ]); 415 416 order_result($data['hostPrototypes'], $sortField, $sortOrder); 417 418 $url = (new CUrl('host_prototypes.php')) 419 ->setArgument('parent_discoveryid', $data['parent_discoveryid']); 420 421 $data['paging'] = getPagingLine($data['hostPrototypes'], $sortOrder, $url); 422 423 $data['parent_templates'] = getHostPrototypeParentTemplates($data['hostPrototypes']); 424 425 // Fetch templates linked to the prototypes. 426 $templateids = []; 427 foreach ($data['hostPrototypes'] as $hostPrototype) { 428 $templateids = array_merge($templateids, zbx_objectValues($hostPrototype['templates'], 'templateid')); 429 } 430 $templateids = array_keys(array_flip($templateids)); 431 432 $linkedTemplates = API::Template()->get([ 433 'output' => ['templateid', 'name'], 434 'templateids' => $templateids, 435 'selectParentTemplates' => ['hostid', 'name'] 436 ]); 437 $data['linkedTemplates'] = zbx_toHash($linkedTemplates, 'templateid'); 438 439 foreach ($data['linkedTemplates'] as $linked_template) { 440 foreach ($linked_template['parentTemplates'] as $parent_template) { 441 $templateids[] = $parent_template['templateid']; 442 } 443 } 444 445 // Select writable template IDs. 446 $data['writable_templates'] = []; 447 448 if ($templateids) { 449 $data['writable_templates'] = API::Template()->get([ 450 'output' => ['templateid'], 451 'templateids' => $templateids, 452 'editable' => true, 453 'preservekeys' => true 454 ]); 455 } 456 457 // render view 458 $itemView = new CView('configuration.host.prototype.list', $data); 459 $itemView->render(); 460 $itemView->show(); 461} 462 463require_once dirname(__FILE__).'/include/page_footer.php'; 464