- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎10-02-2015 06:58 AM
We have an issue with "duplicate" CIs in our CMDB due to our integration point with Netcool event monitoring. We have rules set up that during the Incident creation, it will attempt to match the alerted device to our CMDB populated by Discovery. Failing to due so, it will create a new CI in the default cmdb_ci table with only the device name.
One our issues is that on occasion, Netcool will generate a ticket on a device before Discovery auto-creates it for us. All of a sudden, I now have two CIs in different tables. Incident and Change (because support groups see the name and think "Oh, that's what I wanted" without looking at the Class or Duplicate status) tracking by CI record begins to break down and chaos ensues. Well, maybe not that bad, but I just hate dirty data when I know it can be fixed.
After too much exposition, here's the meat of the situation: how can I leverage the existing CI Verification and Merge CI action to combine my temporary Netcool CI into my good Discovery CI while keeping the integrity of the CIs Affected (task_ci) table. Testing is showing (Eureka Patch 11) that I can consolidate the record by laying the good one on top of the other, but it kills off all related lists. Is there a way I can modify the action to directly merge the two together specifically for this related list?
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎10-02-2015 07:58 AM
This is something that i have been working on to help other with as i have seen it before also. Once you have changes, incidents, or even sometime BSM related items attached it becomes a challenge to just "clean up" the records and move on. One method I was working on was updating the merge ci action to also include going out and finding all related table/ relationships and updating those with the right CI along with the actual merger. I think there might be some more logic to put into it, but here is what I ended up changing the processing script to. This code logic was captured from the folks who created the Knowledge15 material for discovery around deleting related items from a CI. I have just merely twisted it to update those references. I think there is some testing that is still needed, but I am happy to pass it along if you want to take a look on this one.
Really for your scenario I would build some more logic around your integration to set the unverified field to true if it does insert a record, but also I ended up making some changes to the UI page to expand my cleanup.
HTML: Here i changed the table to look at all tables since for you it could be a server, but the record was in cmdb_ci
<g:ui_form>
<input type="hidden" id="cancelled" name="cancelled" value="false"/>
<input type="hidden" id="ci" name="ci" value="${sysparm_sys_id}"/>
<table style="height:100px;" width="100%">
<tr>
<td style="width:100px; text-align:right;"><label for="${jvar_ci_query}" onclick="" dir="">${gs.getMessage('CI to Merge:')}</label>$[SP]</td>
<td><g:ui_reference name="QUERY:unverified=false^ORunverifiedISEMPTY" table="cmdb_ci"/></td> <!--updated table ref -->
</tr>
<tr id="dialogbuttons">
<td colspan="2" align="right">
<g:dialog_buttons_ok_cancel ok="return actionOK();" cancel="cancel();"/>
</td>
</tr>
</table>
</g:ui_form>
Client Script: I added a new hidden element to the form for the table that the ci is in.
function cancel() {
var c = gel('cancelled');
c.value = "true";
GlideDialogWindow.get().destroy();
}
function actionOK() {
var ci_id = gel('QUERY:unverified=false^ORunverifiedISEMPTY').value;
if (ci_id == '') {
alert(getMessage("Please select a CI to merge this CI into"));
return false;
} else {
var form = document.forms['form.' + '${sys_id}'];
addInput(form, "HIDDEN", "new_ci", ci_id);
addInput(form, "HIDDEN", "table", '${sysparm_class_name}'); //new line
return true;
}
}
Processing Script: Here i am using that code i adapted from K15 to merge the related list items that would normally be orphaned.
if (cancelled == "false" && new_ci != "") {
mergeRelated(ci,new_ci,table);// new line
new AssetandCI().mergeCI(ci, new_ci);
response.sendRedirect("cmdb_ci.do?sys_id=" + new_ci);
} else{
response.sendRedirect("cmdb_ci.do?sys_id=" + ci);
}
//new function call
function mergeRelated(oldCI, newCI, tableName) {
// get a list of tables that are parent class to this one
var tu1 = new TableUtils(tableName);
var parentList= j2js(tu1.getTables());
var parentQueryList = [];
// Build an encoded query string
for (var i = 0; i < parentList.length; i++)
parentQueryList.push('reference=' + parentList[i]);
var parentQuery = parentQueryList.join('^OR');
// gs.print('parentQuery=' + parentQuery);
var dGr = new GlideRecord('sys_dictionary');
dGr.addQuery('internal_type', 'reference');
dGr.addEncodedQuery(parentQuery);
dGr.orderBy('name');
dGr.query();
// gs.print('count=' + dGr.getRowCount());
while (dGr.next()) {
// Now query the known table/field to see if they reference the current CI
var gr = new GlideRecord(dGr.getValue('name'));
gr.addQuery(dGr.getValue('element'), oldCI); // eg. cmdb_alias.cmdb_ci=your sys_id
gr.query();
while (gr.next()) {
// Just blank out the CI field
gr.setValue(dGr.getValue('element'), newCI);
gr.update();
}
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎10-02-2015 07:58 AM
This is something that i have been working on to help other with as i have seen it before also. Once you have changes, incidents, or even sometime BSM related items attached it becomes a challenge to just "clean up" the records and move on. One method I was working on was updating the merge ci action to also include going out and finding all related table/ relationships and updating those with the right CI along with the actual merger. I think there might be some more logic to put into it, but here is what I ended up changing the processing script to. This code logic was captured from the folks who created the Knowledge15 material for discovery around deleting related items from a CI. I have just merely twisted it to update those references. I think there is some testing that is still needed, but I am happy to pass it along if you want to take a look on this one.
Really for your scenario I would build some more logic around your integration to set the unverified field to true if it does insert a record, but also I ended up making some changes to the UI page to expand my cleanup.
HTML: Here i changed the table to look at all tables since for you it could be a server, but the record was in cmdb_ci
<g:ui_form>
<input type="hidden" id="cancelled" name="cancelled" value="false"/>
<input type="hidden" id="ci" name="ci" value="${sysparm_sys_id}"/>
<table style="height:100px;" width="100%">
<tr>
<td style="width:100px; text-align:right;"><label for="${jvar_ci_query}" onclick="" dir="">${gs.getMessage('CI to Merge:')}</label>$[SP]</td>
<td><g:ui_reference name="QUERY:unverified=false^ORunverifiedISEMPTY" table="cmdb_ci"/></td> <!--updated table ref -->
</tr>
<tr id="dialogbuttons">
<td colspan="2" align="right">
<g:dialog_buttons_ok_cancel ok="return actionOK();" cancel="cancel();"/>
</td>
</tr>
</table>
</g:ui_form>
Client Script: I added a new hidden element to the form for the table that the ci is in.
function cancel() {
var c = gel('cancelled');
c.value = "true";
GlideDialogWindow.get().destroy();
}
function actionOK() {
var ci_id = gel('QUERY:unverified=false^ORunverifiedISEMPTY').value;
if (ci_id == '') {
alert(getMessage("Please select a CI to merge this CI into"));
return false;
} else {
var form = document.forms['form.' + '${sys_id}'];
addInput(form, "HIDDEN", "new_ci", ci_id);
addInput(form, "HIDDEN", "table", '${sysparm_class_name}'); //new line
return true;
}
}
Processing Script: Here i am using that code i adapted from K15 to merge the related list items that would normally be orphaned.
if (cancelled == "false" && new_ci != "") {
mergeRelated(ci,new_ci,table);// new line
new AssetandCI().mergeCI(ci, new_ci);
response.sendRedirect("cmdb_ci.do?sys_id=" + new_ci);
} else{
response.sendRedirect("cmdb_ci.do?sys_id=" + ci);
}
//new function call
function mergeRelated(oldCI, newCI, tableName) {
// get a list of tables that are parent class to this one
var tu1 = new TableUtils(tableName);
var parentList= j2js(tu1.getTables());
var parentQueryList = [];
// Build an encoded query string
for (var i = 0; i < parentList.length; i++)
parentQueryList.push('reference=' + parentList[i]);
var parentQuery = parentQueryList.join('^OR');
// gs.print('parentQuery=' + parentQuery);
var dGr = new GlideRecord('sys_dictionary');
dGr.addQuery('internal_type', 'reference');
dGr.addEncodedQuery(parentQuery);
dGr.orderBy('name');
dGr.query();
// gs.print('count=' + dGr.getRowCount());
while (dGr.next()) {
// Now query the known table/field to see if they reference the current CI
var gr = new GlideRecord(dGr.getValue('name'));
gr.addQuery(dGr.getValue('element'), oldCI); // eg. cmdb_alias.cmdb_ci=your sys_id
gr.query();
while (gr.next()) {
// Just blank out the CI field
gr.setValue(dGr.getValue('element'), newCI);
gr.update();
}
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎11-25-2015 02:18 PM
Sorry it took so long to review through this. We are in the middle of upgrading to Fuji and have been otherwise occupied. I was able to add your scripts into my dev instance and things look very promising. I'm hoping after the upgrade I can implement this update set into our Sandbox environment and test against actual CI data from Production.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎05-17-2018 10:35 AM
Thanks for sharing this. Glad to see I am not alone in seeing the shortcomings of the out of box Merge capabilities. I have planned to take this on as well, and including the same features you have added here, and additional ones as well.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎01-23-2019 03:00 PM
//new function call
var oldCI = '29348klsdfdsnf';
var newCI = '23094sklfldsklks';
var tableName = new GlideRecord('cmdb_ci_linux_server');
function mergeRelated(oldCI, newCI, tableName) {
// get a list of tables that are parent class to this one
var tu1 = new TableUtils(tableName);
var parentList= j2js(tu1.getTables());
var parentQueryList = [];
// Build an encoded query string
for (var i = 0; i < parentList.length; i++)
parentQueryList.push('reference=' + parentList[i]);
var parentQuery = parentQueryList.join('^OR');
gs.print('parentQuery=' + parentQuery);
var dGr = new GlideRecord('sys_dictionary');
dGr.addQuery('internal_type', 'reference');
dGr.addEncodedQuery(parentQuery);
dGr.orderBy('name');
dGr.query();
gs.print('count=' + dGr.getRowCount());
while (dGr.next()) {
// Now query the known table/field to see if they reference the current CI
var gr = new GlideRecord(dGr.getValue('name'));
gr.addQuery(dGr.getValue('element'), oldCI); // eg. cmdb_alias.cmdb_ci=your sys_id
gr.query();
while (gr.next()) {
// Just blank out the CI field
gr.setValue(dGr.getValue('element'), newCI);
gr.update();
}
}
}