一种基于iTop实现服务树的方法

如何建设CMDB 一文中提到,CMDB 如果不是单纯管资产,想要用在更多的运维场景上(比如 成本核算,报警接收人查询),那么就一定要以业务为中心,即建立 资源 -> 业务 -> 人员 这样的关系,避免 人员 和 资源直接关联,这样能够减少关系的维护成本(比如容易交接业务,只需变更业务负责人,通过关联就能找到对应资源)。

业务管理的优先级高于资源 CI,应首先实现。

实现方案

基于 iTop 现有模型,或许可以用 Organization, BusinessProcess, ApplicationSolution 来实现一个三级的业务树:

- Org
  - BussinessProcess-1
    - App-1
  - SubOrg
    - BussinessProcess-2
      - App-2

需要对 ApplicationSolution 做一点改造,将 BusinessProcess 和 ApplicationSolution 的 N:N 关系改为 1:N 关系,即 App 归属于固定的 BusinessProcess。

<class id="BusinessProcess">
	<fields>
		<field id="applicationsolutions_list" xsi:type="AttributeLinkedSet" _delta="redefine">
			<linked_class>ApplicationSolution</linked_class>
			<ext_key_to_me>businessprocess_id</ext_key_to_me>
		</field>
		<field id="code" xsi:type="AttributeString" _delta="define">
			<sql>code</sql>
			<is_null_allowed>true</is_null_allowed>
		</field>
	</fields>
	<presentation>
        ... 略
	</presentation>
</class>
<class id="ApplicationSolution">
	<fields>
		<field id="code" xsi:type="AttributeString" _delta="define">
			<sql>code</sql>
			<is_null_allowed>true</is_null_allowed>
		</field>
		<field id="businessprocess_id" xsi:type="AttributeExternalKey" _delta="define">
			<sql>businessprocess_id</sql>
			<target_class>BusinessProcess</target_class>
			<is_null_allowed>false</is_null_allowed>
			<on_target_delete>DEL_MANUAL</on_target_delete>
		</field>
		<field id="businessprocess_name" xsi:type="AttributeExternalField" _delta="define">
			<extkey_attcode>businessprocess_id</extkey_attcode>
			<target_attcode>name</target_attcode>
		</field>
		<field id="status" xsi:type="AttributeEnum" _delta="redefine">
			<values>
				<value id="production">production</value>
				<value id="implementation">implementation</value>
				<value id="stock">stock</value>
				<value id="obsolete">obsolete</value>
			</values>
			<sql>status</sql>
			<default_value>production</default_value>
			<is_null_allowed>true</is_null_allowed>
			<display_style>list</display_style>					
		</field>			
	</fields>
	<presentation>

         ..略
	</presentation>
	<relations>
		<relation id="impacts">
			<neighbours>
				<neighbour id="businessprocess" _delta="redefine">
					<attribute>businessprocess_id</attribute>
				</neighbour>
			</neighbours>
		</relation>
	</relations>
</class>

业务树展示

接下来要实现一个树形展示页面,方便查看并梳理业务结构。选择基于 jstree 来做。新建 ajax.render.php 提供 jstree 需要的 json 格式。jstree 的 json 结构如下面代码所示。

[
	{
		"id": "Organization:2",
		"text": "IT Department",
		"state": {
			"opened": true
		},
		"parent": "#",
		"a_attr": {
			"href": "http://test.annhe.net/itop/pages/UI.php?operation=details&class=Organization&id=2",
			"tooltiptitle": "组织:IT Department",
			"content": "<table width=\"200px\"><tr><td>状态</td><td>active</td></tr><tr><td>编码</td><td>IT</td></tr></table>"
		}
	},
	{
		"id": "BusinessProcess:45",
		"text": "运维自动化",
		"state": {
			"opened": true
		},
		"parent": "Organization:2",
		"a_attr": {
			"href": "http://test.annhe.net/itop/pages/UI.php?operation=details&class=BusinessProcess&id=45",
			"tooltiptitle": "业务流程:运维自动化",
			"content": "<table width=\"200px\"><tr><td>负责人</td><td></td></tr><tr><td>状态</td><td>active</td></tr><tr><td>编码</td><td></td></tr></table>"
		}
	},
	{
		"id": "ApplicationSolution:46",
		"text": "运维自动化 iTop",
		"state": {
			"opened": true
		},
		"parent": "#",
		"a_attr": {
			"href": "http://test.annhe.net/itop/pages/UI.php?operation=details&class=ApplicationSolution&id=46",
			"tooltiptitle": "应用方案:运维自动化 iTop",
			"content": "<table width=\"200px\"><tr><td>负责人</td><td></td></tr><tr><td>状态</td><td>production</td></tr><tr><td>编码</td><td></td></tr></table>"
		}
	}
]

核心代码:

function GetContacts($aContacts) {
	$aContactsTmp = array();
	foreach ($aContacts as $aContact) {
		$aContactsTmp[] = $aContact['lnkContactToFunctionalCI.contact_id_friendlyname'];
	}
	return implode(",", $aContactsTmp);
}

function GetNode($oObj) {
	$sClass = get_class($oObj);
	switch ($sClass) {
		case 'Organization':
			if($oObj->Get("parent_id") > 0) {
				$sParent = "Organization:" . $oObj->Get("parent_id");
			} else {
				$sParent = "#";
			}
			break;
		case 'BusinessProcess':
			$sParent = "Organization:" . $oObj->Get("org_id");
			break;
		case 'ApplicationSolution':
			$sParent = "BusinessProcess:" . $oObj->Get("businessprocess_id");
			break;
		default:
			$sParent = "#";
			break;
	}

	$aNode = array(
		"id" => $sClass . ":" . $oObj->GetKey(),
		"text" => $oObj->GetName(),
		"state" => array("opened"=>True),
		"parent" => $sParent,
		"a_attr" => array(
			"href" => utils::GetAbsoluteUrlAppRoot() . "pages/UI.php?operation=details&class=" . $sClass . "&id=" . $oObj->GetKey(),
			"tooltiptitle" => MetaModel::GetName($sClass) . ":" . $oObj->GetName()
		)
	);

	$sContent = '<table width="200px">';
	if($sClass != "Organization") {
		$sContent .= "<tr><td>负责人</td><td>" . GetContacts($oObj->Get("contacts_list")->ToDBObjectSet()->ToArrayOfValues()) . "</td></tr>";
	}
	$sContent .= "<tr><td>状态</td><td>" . $oObj->Get("status") . "</td></tr>";
	$sContent .= "<tr><td>编码</td><td>" . $oObj->Get("code") . "</td></tr></table>";
	$aNode['a_attr']['content'] = $sContent;
	if($sClass == "BusinessProcess") {
		$aNode['icon'] = "fa fa-list";
	}
	if($sClass == "ApplicationSolution") {
		$aNode['icon'] = "fa fa-cog";
	}
	return $aNode;
}
function GetAppTree($sOrg = NULL) {
	$aTree = array();
	foreach(array("Organization", "BusinessProcess", "ApplicationSolution") as $sClass) {
		$sOQL = "SELECT " . $sClass;
		if($sOrg) {
			if($sClass == "Organization") {
				$sOQL .= " WHERE id=" . $sOrg;
			} else {
				$sOQL .= " WHERE org_id=" . $sOrg;
			}
		}

		$oSearch = DBObjectSearch::FromOQL_AllData($sOQL);
		$oSet = new DBObjectSet($oSearch, array(), array());
	
		while ($oObj = $oSet->Fetch()) {
			$aTree[] = GetNode($oObj);
		}		
	}
	return $aTree;
}

$sOrg = utils::ReadParam('org', '', false, 'raw_data');

$oP = new ajax_page('');
$oP->SetContentType('application/json');
$oP->add(json_encode(GetAppTree($sOrg)));
$oP->output();

然后新建 ui.php,用来展示业务树,代码比较简单:

<?php

if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
if (!defined('APPROOT')) require_once(__DIR__.'/../../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/itopwebpage.class.inc.php');
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
require_once(APPROOT.'/application/startup.inc.php');
LoginWebPage::DoLogin(); // Check user rights and prompt if needed

$oP = new iTopWebPage(Dict::S('UI:AppTree:Title'));
$oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/');
$oP->SetBreadCrumbEntry('ui-tool-org', Dict::S('Menu:AppTree'), Dict::S('Menu:AppTree+'), '', utils::GetAbsoluteUrlAppRoot().'images/wrench.png');

$oP->add_linked_stylesheet(utils::GetAbsoluteUrlModulesRoot() . "opsitop-main/libs/jstree/themes/default/style.min.css");
$oP->add_linked_script(utils::GetAbsoluteUrlModulesRoot() . "opsitop-main/libs/jstree/jstree.min.js");

$oP->add('<h1>' . Dict::S('UI:AppTree:Title') . '</h1>');
$oP->add('<div id="app-jstree" class="demo"></div>');

$oP->add_ready_script('
$("#app-jstree").jstree({
	"core" :{
		"data" : {
			"url" : "' . utils::GetAbsoluteUrlModulesRoot() . 'opsitop-main/ajax.render.php",
			"dataType" : "json"
		}
	}
}).bind(
	"select_node.jstree", function(e, data) {window.open(data.node.a_attr.href);}
).bind(
	"ready.jstree", function() {
		$(".jstree-anchor").each(function(){
			$(this).qtip({
				content : {
					text: $(this).attr("content"),
					title: $(this).attr("tooltiptitle")
				}
			});
		});
	}
);
');

$oP->output();

业务树效果

效果如图。用 iTop 已经包含的 qtip 库,实现了鼠标移动展示更多信息的功能。

iTop 实现业务树

参考资料

1. https://www.jstree.com/docs/json/
2. https://www.js-tutorials.com/jquery-tutorials/jstree-example-href-jstree-search-node-scrollbar/
3. https://stackoverflow.com/questions/33805104/adding-qtip2-tooltips-to-each-node-of-a-jstree

One thought on “一种基于iTop实现服务树的方法

  1. 关于 ApplicationSolution 和 BusinessProcess 的讨论,可能此文的修改并不合适。

    【吐槽】*** 2021/6/23 10:30:53
    请教一下,itop中的应用方案和业务流程这两个CI的实际应用场景,大佬帮忙解惑一二
    我的理解(感觉理解的不对):
    1、应用方案,比如考勤系统、人事系统等具体的应用,包含了服务器(虚机或物理)、操作系统、中间件、数据库等
    2、业务流程,是应用方案的合集,比如综合办公系统,生产业务系统这一类
    【群主】广州-*** 2021/6/23 10:36:48
    应用方案是由几个子系统组合起来的业务系统
    【群主】广州-*** 2021/6/23 10:37:17
    业务流程,是由一个或多个业务系统支撑的业务处理流程。这里实现了IT支持业务的概念。
    【群主】广州-*** 2021/6/23 10:37:41
    支持了业务流程,才实现了IT的价值
    【活跃】版主-北京-*** 2021/6/23 10:37:48
    子系统是不是没有对应模型
    【活跃】版主-北京-*** 2021/6/23 10:38:33
    如果要搞一个类似服务树的概念,怎么做比较好?
    【群主】广州-*** 2021/6/23 10:38:38
    DB实例、中间件实例、APP应用实例,都是子系统呀
    【群主】广州-*** 2021/6/23 10:38:44
    各种各样的

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注