在 如何建设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 库,实现了鼠标移动展示更多信息的功能。
参考资料
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
关于 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
各种各样的