在 如何建设CMDB 一文中提到,CMDB 如果不是单纯管资产,想要用在更多的运维场景上(比如 成本核算,报警接收人查询),那么就一定要以业务为中心,即建立 资源 -> 业务 -> 人员
这样的关系,避免 人员 和 资源直接关联,这样能够减少关系的维护成本(比如容易交接业务,只需变更业务负责人,通过关联就能找到对应资源)。
业务管理的优先级高于资源 CI,应首先实现。
实现方案
基于 iTop 现有模型,或许可以用 Organization, BusinessProcess, ApplicationSolution 来实现一个三级的业务树:
1 2 3 4 5 6 |
- Org - BussinessProcess-1 - App-1 - SubOrg - BussinessProcess-2 - App-2 |
需要对 ApplicationSolution 做一点改造,将 BusinessProcess 和 ApplicationSolution 的 N:N 关系改为 1:N 关系,即 App 归属于固定的 BusinessProcess。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<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 结构如下面代码所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
[ { "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>" } } ] |
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
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
,用来展示业务树,代码比较简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?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 2 3 |
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
各种各样的