Name
global.DiscoveryWindows2012StorageSensor
Description
Processes WMI results for Windows disks and file systems. Maps NAS and SAN dependencies if possible.
Script
/**
* Processes WMI results for Windows disks and file systems. Maps NAS and SAN dependencies if possible.
*
* @since fuji
* @author roy.laurie
*/
var DiscoveryWindows2012StorageSensor = Class.create();
/* MSFT_Disk.BusType => cmdb_ci_disk.interface */
DiscoveryWindows2012StorageSensor._DISK_BUS_TYPE_INTERFACES = {
1: 'scsi',
2: 'atapi',
3: 'ata',
4: 'ieee1394',
5: 'ssa',
6: 'fc',
7: 'usb',
8: 'raid',
9: 'iscsi',
10: 'sas',
11: 'sata',
12: 'sd',
13: 'mmc',
17: 'nvme'
};
DiscoveryWindows2012StorageSensor.prototype = Object.extendsObject(DiscoveryWindowsStorageMultiSensor, {
init: function() {
var arr = [ 'MSFT_Disk', 'MSFT_Volume', 'MSFT_Partition', 'MSFT_DiskToPartition', 'MSFT_PartitionToVolume', 'MSFT_iSCSISession', 'MSFT_iSCSISessionToDisk', 'Win32_DiskDrive', 'Win32_MappedLogicalDisk','Win32_LogicalDisk' ];
DiscoveryWindowsStorageMultiSensor.prototype.init.call(this, arr);
},
/**
* Processes CMDB updates for storage devices.
* @param Ci deviceCi
* @param {} result The probe result
*/
_processStorageDevices: function(deviceCi, result) {
var ciSchema = this.getCiSchema();
var storageReconciler = new DiscoveryStorageAllocationReconciler({ ciSchema: ciSchema });
for (var i = 0; i < result.MSFT_Disk.length; ++i) {
var disk = result.MSFT_Disk[i];
if (this.isDebugging())
Debug.logObject('MSFT_Disk', disk);
// find the corresponding Win32_DiskDrive for various info
// currently needed to get scsi host,bus,target,lun. a 2012 method would be preferred.
// currently "scrape" matching agaist name
var diskDrive = null;
for (var j = 0; j < result.Win32_DiskDrive.length; ++j) {
var drive = result.Win32_DiskDrive[j];
var nameRegex = 'PHYSICALDRIVE' + disk.Number + '$';
if (drive.Name.match(nameRegex)) {
diskDrive = drive;
break;
}
}
if (diskDrive === null) {
DiscoveryLogger.warn('Physical drive not found for logical disk with serial ' +
disk.SerialNumber + ' (' + disk.FriendlyName + ')',
'Discovery',
this.getEccQueueId());
continue;
}
var deviceInterface = this._getDeviceInterfaceForBusType(disk.BusType);
var table = this._getDiskTableForInterface(deviceInterface);
var makeModel = MakeAndModelJS.fromNames(disk.Manufacturer, disk.Model, 'hardware');
var diskCi = ciSchema.createCi(table, {
name: 'Disk #' + disk.Number, // may change across restarts
device_id: disk.ObjectId,
storage_type: this._getStorageTypeForInterface(deviceInterface),
description: disk.FriendlyName,
serial_number: disk.SerialNumber,
operational_status: ( disk.OperationalStatus === '1' ? 1 : 2 ), // 1: operational, 2: non-operational
manufacturer: ''+makeModel.getManufacturerSysID(),
model_id: ''+makeModel.getModelNameSysID(),
computer: deviceCi,
device_interface: deviceInterface,
device_bus_id: diskDrive.SCSIBus,
device_lun: diskDrive.SCSILogicalUnit,
device_target_id: diskDrive.SCSITargetId,
device_host_id: diskDrive.SCSIPort,
device_name: diskDrive.Name // Used for reconcilation
});
var size = new StorageDataSize(disk.Size, 'B');
size.applyToStorageDeviceSize(diskCi);
deviceCi.addReferral(diskCi);
// process SAN data
switch(table) {
case 'cmdb_ci_disk':
// populate deprecated fields until fuji+2 release
diskCi.data.drive_type = diskCi.data.storage_type;
break;
case 'cmdb_ci_iscsi_disk':
var iscsiSessionResult = this._findIscsiSessionResult(disk.ObjectId, result);
if (iscsiSessionResult === null) {
this.warn("Unable to create a relationship between ISCSI Disk: " + diskCi.data.name + " and storage server volume due to empty IscsiSessionResult");
break;
}
diskCi.data.iqn = iscsiSessionResult.TargetNodeAddress;
diskCi.data.initiator_iqn = iscsiSessionResult.InitiatorNodeAddress;
// attempt to relate to upstream volume on array
try {
storageReconciler.createISCSIDiskCiToVolumeRel(diskCi);
} catch (e) {
// It is possible they have not discovered storage so not warning without debug on
if (this.isDebugging())
this.warn(e);
}
break;
case 'cmdb_ci_fc_disk':
//throw 'FC not implemented';
}
if (this.isDebugging())
Debug.logObject('Disk CI ' + i, diskCi.toShallowObj());
}
},
_findIscsiSessionResult: function(diskObjectId, result) {
for (var i = 0; i < result.MSFT_iSCSISessionToDisk.length; ++i) {
var sessionToDisk = result.MSFT_iSCSISessionToDisk[i];
var matches = sessionToDisk.Disk.match(/\.ObjectId="(.*?)"$/);
if (matches === null)
continue;
var diskId = matches[1].replace(/\\\\/g, '\\');
if (diskId !== diskObjectId)
continue;
matches = sessionToDisk.iSCSISession.match(/\.SessionIdentifier="(.*?)"$/);
if (matches === null)
continue;
var sessionId = matches[1].replace(/\\\\/g, '\\');
for (var j = 0; j < result.MSFT_iSCSISession.length; ++j) {
var session = result.MSFT_iSCSISession[j];
if (session.SessionIdentifier === sessionId)
return session;
}
}
return null;
},
_processPartitions: function(deviceCi, result) {
var disks = deviceCi.getReferrals('cmdb_ci_storage_device');
var ciSchema = this.getCiSchema();
//Create disks map
var disksMap = {};
for (var i = 0; i < disks.length; ++i) {
disksMap[disks[i].data.device_id.toLowerCase()] = disks[i];
}
//Create partition-to-disk map
var partitionToDiskMap = {};
var matches = null;
var partitionObjectId = '';
var diskObjectId = '';
for (i = 0; i < result.MSFT_DiskToPartition.length; ++i) {
matches = result.MSFT_DiskToPartition[i].Partition.match(/\.ObjectId="(.*)"/);
if (matches === null)
continue;
partitionObjectId = matches[1].replace(/\\\\/g, '\\').replace(/\\\"/g,'"').toLowerCase();
matches = result.MSFT_DiskToPartition[i].Disk.match(/\.ObjectId="(.*)"/);
if (matches === null)
continue;
diskObjectId = matches[1].replace(/\\\\/g, '\\').replace(/\\\"/g,'"').toLowerCase();
partitionToDiskMap[partitionObjectId] = disksMap[diskObjectId];
}
for (var i = 0; i < result.MSFT_Partition.length; ++i) {
var partition = result.MSFT_Partition[i];
if (this.isDebugging())
Debug.logObject('MSFT_Partition', partition);
// find the associated disk
var diskCi = (partition.ObjectId)?partitionToDiskMap[partition.ObjectId.toLowerCase()]:null;
//Fallback to original behavior in case no disk was found
if (!diskCi) {
for (var j = 0; j < disks.length; ++j) {
if (disks[j].data.device_id === partition.DiskId) {
diskCi = disks[j];
break;
}
}
}
if (!diskCi) {
DiscoveryLogger.warn('MSFT_Disk not found MSFT_Partition', 'Discovery', this.getEccQueueId());
continue;
}
var partitionCi = ciSchema.createCi('cmdb_ci_disk_partition', {
name: diskCi.data.name + ' Partition #' + partition.PartitionNumber,
computer: deviceCi,
disk: diskCi,
partition_number: partition.PartitionNumber,
operational_status: ( partition.OperationalStatus === '1' ? 1 : 2 ),
start_offset: partition.Offset,
end_offset: parseInt(partition.Size) + parseInt(partition.Offset),
object_id: partition.ObjectId || "" // Used for Windows 2016
});
var size = new StorageDataSize(partition.Size, 'B');
size.applyToDiskPartitionSize(partitionCi);
diskCi.addReferral(partitionCi);
deviceCi.addReferral(partitionCi);
if (this.isDebugging())
Debug.logObject('Partition CI ' + i, partitionCi.toShallowObj());
}
},
_processFileSystems: function(deviceCi, result) {
var ciSchema = this.getCiSchema();
var volumeToDiskMapper = new DiscoveryVolumeToDiskMapper({ debug: this.isDebugging() });
var volumeSerialNumbers = {};
for (var i = 0; i < result.Win32_LogicalDisk.length; i++) {
var vSerialInfo = result.Win32_LogicalDisk[i];
var volId = vSerialInfo.DeviceID.trim();
volumeSerialNumbers[volId] = vSerialInfo.VolumeSerialNumber;
}
var mountPoints = this._getVolumeIdsToMountPoints(result);
for (i = 0; i < result.MSFT_Volume.length; ++i) {
var volume = result.MSFT_Volume[i];
if (this.isDebugging())
Debug.logObject('MSFT_Volume', volume);
// remote file systems will use the NAS child table
var table = ( volume.DriveType === '4' ? 'cmdb_ci_nas_file_system' : 'cmdb_ci_file_system' ); // 4 = "Remote"
//TODO: There is a bug in PowerShell/WMI processing that is returning single characters as their decimal representation. Needs fixin'.
var absolutePath = null;
if (gs.nil(volume.DriveLetter) || volume.DriveLetter === '0') {
absolutePath = null;
} else if (isNaN(volume.DriveLetter)) {
absolutePath = volume.DriveLetter + ':\\';
} else {
absolutePath = String.fromCharCode(volume.DriveLetter) + ':\\';
}
var volumeSerialNumber = null;
if (volume.DriveLetter && volumeSerialNumbers.hasOwnProperty(volume.DriveLetter + ':'))
volumeSerialNumber = volumeSerialNumbers[volume.DriveLetter + ':'];
// Get real mount point by the volume id
var volumeId = this._getCleanVolumeId(volume.ObjectId);
var mountPoint = absolutePath;
if (mountPoints && mountPoints[volumeId])
mountPoint = mountPoints[volumeId];
// create a new file system ci. reconciled by name and computer.
var fileSystemCi = ciSchema.createCi(table, {
computer: deviceCi,
mount_point: mountPoint,
name: ( absolutePath !== null ? absolutePath : volume.FileSystemLabel ),
media_type: this._getMediaTypeForVolume(volume.DriveType),
file_system: this._getFileSystemTypeForVolume(volume.FileSystem),
serial_number: volumeSerialNumber,
label: volume.FileSystemLabel
});
var size = new StorageDataSize(volume.Size, 'B');
size.applyToStorageVolumeSize(fileSystemCi);
var free_space = new StorageDataSize(volume.SizeRemaining, 'B');
free_space.applyToStorageVolumeFreeSpace(fileSystemCi);
deviceCi.addReferral(fileSystemCi); // reconcile against other file systems found on the device
// relate the provided_by back to this ci if found
fileSystemCi.data.provided_by = this._findVolumeProvider(volume, result.MSFT_PartitionToVolume, deviceCi);
if (fileSystemCi.data.provided_by !== null) {
fileSystemCi.data.provided_by.addReferral(fileSystemCi);
volumeToDiskMapper.relateStorageDevice(fileSystemCi);
}
if (this.isDebugging())
Debug.logObject('File System CI', fileSystemCi.toShallowObj());
}
},
/**
* Finds the matching volume for the WMI results.
*/
_findVolumeProvider: function(volumeResult, partitionTovolumeResults, deviceCi) {
// iterate through partition-to-volume mapping and find by volume object id
var partitionMaps = [];
var diskId, offset, partitionObjectId, matches, i;
for (i = 0; i < partitionTovolumeResults.length; ++i) {
var partitionToVolume = partitionTovolumeResults[i];
matches = partitionToVolume.Volume.match(/.ObjectId="(.*?)"$/);
if (matches === null)
continue;
// In case of 2016 we need to remove '\"' as well
var objectId = matches[1].replace(/\\\\/g, '\\').replace(/\\\"/g,'"');
// Sometimes the case is different
if (objectId.toLowerCase() === volumeResult.ObjectId.toLowerCase())
partitionMaps.push(partitionToVolume);
}
//TODO: support raid - multiple partitions
if (partitionMaps.length !== 1)
return null;
matches = partitionMaps[0].Partition.match(/.DiskId="(.*?)",Offset="(\d+)"$/);
// If we have no matches, we need to check fot Windows 2016 format
if (!matches){
matches = partitionMaps[0].Partition.match(/.ObjectId="(.*?)"$/);
if (!matches)
return null;
// Set partitionObjectId based on Windows 2016 format
partitionObjectId = matches[1].replace(/\\\\/g, '\\').replace(/\\\"/g,'"');
}
else {
// Set diskId & offset based on Windows 2012 format
diskId = matches[1].replace(/\\\\/g, '\\');
offset = matches[2].replace(/\\\\/g, '\\');
}
var partitions = deviceCi.getReferrals('cmdb_ci_disk_partition');
for (i = 0; i < partitions.length; ++i) {
var partitionCi = partitions[i];
var diskCi = partitionCi.data.disk;
// Try to find match using Windows 2012 variables. If not found, try using 2016 values
if (partitionCi.data.start_offset === offset && diskCi.data.device_id == diskId)
return partitionCi;
else if (partitionCi.data.object_id.toLowerCase() === (partitionObjectId && partitionObjectId.toLowerCase())){
return partitionCi;
}
}
return null;
},
_getDeviceInterfaceForBusType: function(busType) {
if (typeof DiscoveryWindows2012StorageSensor._DISK_BUS_TYPE_INTERFACES[busType] === 'undefined')
return null;
return DiscoveryWindows2012StorageSensor._DISK_BUS_TYPE_INTERFACES[busType];
},
type: 'DiscoveryWindows2012StorageSensor'
});
Sys ID
f030902037912100dcd48c00dfbe5d63