LS Call Reliability Monitoring Data source module.

Microsoft.LS.2013.Service.MonitoringServer.CDR.DSModuleType (DataSourceModuleType)

Element properties:

TypeDataSourceModuleType
IsolationAny
AccessibilityInternal
RunAsMicrosoft.LS.2013.RunAsAccount
OutputTypeSystem.PropertyBagData

Member Modules:

ID Module Type TypeId RunAs 
DS DataSource Microsoft.LS.2013.PropertyBag.Common.DS.ShellOut Default

Overrideable Parameters:

IDParameterTypeSelectorDisplay NameDescription
MinVolumeint$Config/MinVolume$Minimum number of calls per modalityMinimum number of calls per modality
MinUsersImpactedint$Config/MinUsersImpacted$Minimum number of Users ImpactedMinimum number of Users Impacted
AlertThresholdPercentagedouble$Config/AlertThresholdPercentage$Alert Threshold PercentageAlert Threshold Percentage
MinutesToQueryint$Config/MinutesToQuery$Minutes To QueryMinutes To Query
Frequencyint$Config/Frequency$FrequencyFrequency

Source Code:

<DataSourceModuleType ID="Microsoft.LS.2013.Service.MonitoringServer.CDR.DSModuleType" RunAs="Microsoft.LS.2013.RunAsAccount" Accessibility="Internal" Batching="false">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ClusterFqdn" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Frequency" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MinutesToQuery" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MinUsersImpacted" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MinVolume" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SQLInstance" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MirrorSQLInstance" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ReportingServiceUrl" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ScriptName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ScriptFileName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SprocName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ExtensionForReportUrl" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="EventId" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="CDRRuleEventId" type="xsd:int"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="Frequency" ParameterType="int" Selector="$Config/Frequency$"/>
<OverrideableParameter ID="MinutesToQuery" ParameterType="int" Selector="$Config/MinutesToQuery$"/>
<OverrideableParameter ID="AlertThresholdPercentage" ParameterType="double" Selector="$Config/AlertThresholdPercentage$"/>
<OverrideableParameter ID="MinUsersImpacted" ParameterType="int" Selector="$Config/MinUsersImpacted$"/>
<OverrideableParameter ID="MinVolume" ParameterType="int" Selector="$Config/MinVolume$"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<DataSource ID="DS" TypeID="Microsoft.LS.2013.PropertyBag.Common.DS.ShellOut">
<IntervalSeconds>$Config/Frequency$</IntervalSeconds>
<SyncTime/>
<ScriptName>$Config/ScriptName$</ScriptName>
<ScriptFileName>$Config/ScriptFileName$</ScriptFileName>
<ScriptBody><Script>
$ClusterFqdn = "$Config/ClusterFqdn$"
$MinutesToQuery = $Config/MinutesToQuery$
$AlertThresholdPercentage = $Config/AlertThresholdPercentage$
$MinUsersImpacted = $Config/MinUsersImpacted$
$MinVolume = $Config/MinVolume$
$SQLInstance = "$Config/SQLInstance$"
$MirrorSQLInstance = "$Config/MirrorSQLInstance$"
$ReportingServiceUrl = "$Config/ReportingServiceUrl$"
$SprocName = "$Config/SprocName$"
$ExtensionForReportUrl = "$Config/ExtensionForReportUrl$"
$CDRRuleEventId = $Config/CDRRuleEventId$

#############################################################################
# Parameters
#
# [ ClusterFqdn ] - Computer (FQDN) that Lync Monitoring Server is hosted on
# [ MinutesToQuery ] - Analyze CDR data for the last "MinutesToQuery" number of minutes.
# [ AlertThresholdPercentage ] - Consider Diagnosis IDs that cross this provided percentage value
# - of total call completions as "Alerts"
# [ MinUsersImpacted ] - Consider Minimum numbers of users that get impacted before raising Alert.
# [ MinVolume ] - Consider Minimum numbers of calls per modality before raising Alert.
# [ SQLInstance ] - SQL Instance where monitoring store is located. This Instance has the LcsCDR database.
# [ MirrorSQLInstance ] - SQL Mirror Instance where monitoring store is located. This Instance has the LcsCDR database.
# [ ReportingServiceUrl ] - The base URL of the Reporting Server.
#
#############################################################################

#CONSTANTS###################################################################
#############################################################################

#LOGS &amp; EXCEPTIONS###########################################################
$CDR_DSMODULE_RUN_SUCCESS = "CDR DataSource Module run execution completed for '{0}'`n"
#############################################################################

#############################################################################
# Functions for Database Connections
#############################################################################

# Set the Threshold Settings
function CDR_GetAlertSettings($StartUTCTime, $EndUTCTime, $AlertThresholdPercentage, $MinUsersImpacted, $MinVolume)
{
# The XML string that will be passed to sproc that queries CDR database
$AlertSettingXML =
"
&lt;AlertSetting&gt;
&lt;Settings
StartTime=`"" + $StartUTCTime+"`"
EndTime=`"" + $EndUTCTime+"`"
AlertThresholdPercentage=`""+ $AlertThresholdPercentage + "`"
MinUsersImpacted=`"" + $MinUsersImpacted + "`"
MinVolume=`"" + $MinVolume + "`"
/&gt;
&lt;/AlertSetting&gt;
"
$AlertSettingXML
}

# return a string with two digits.
# make it start with "0" if the input string contains only one digit
function CDR_TwoDigits( $sDigits )
{
$s = "0" + $sDigits
if ( $s.length -lt 2 )
{
"00"
}
else
{
$s.substring($s.length-2, 2)
}
}

# Format Example : 1997-07-16T19:20:30
function CDR_FormatUTCTime( $d )
{
$d.Year.ToString() + "-" + (CDR_TwoDigits $d.Month) + "-" + (CDR_TwoDigits $d.Day) + "T" +
(CDR_TwoDigits $d.Hour) + ":" + (CDR_TwoDigits $d.Minute) + ":" + (CDR_TwoDigits $d.Second)
}

function CDR_EncodeUrl([string] $url)
{
[System.Web.HttpUtility]::UrlEncode($url)
}


# Open a query context with database instance and alert setting xml string
function CDR_DB_OpenQueryContext($DbInst, $MirrorDbInst, $AlertSettingsXML )
{
# Setup connection string, query string
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server="+$DbInst+";Database=LcsCDR;Integrated Security=True"
if(-not ([System.String]::IsNullOrEmpty($MirrorDbInst)))
{
$SqlConnection.ConnectionString += ";Failover Partner="+$MirrorDbInst
}

$SqlCmd = $SqlConnection.CreateCommand()
$SqlCmd.CommandTimeout = 100000


$SqlCmd.CommandText = "exec {0} @AlertSettingsXML=@AlertSettingsXMLArg" -f $SprocName

# Bind the AlertSetting parameter as Xml type
$SqlCmd.Parameters.Add("@AlertSettingsXMLArg", [System.Data.SqlDbType]::NVarChar ).Value = $AlertSettingsXML

# Execute Command
$SqlConnection.Open()
$SqlDataReader = $SqlCmd.ExecuteReader()

# Encapsulate connection and reader to query context.
$QueryContext = @( [ref]$SqlConnection, [ref]$SqlDataReader )
$QueryContext
}

# Get sql data reader from the query context.
function CDR_DB_GetDataReaderReference($QueryContext ) {
$RefSqlDataReader = $QueryContext[1]
$RefSqlDataReader
}

# Close connection and reader in the query context.
function CDR_DB_CloseQueryContext($QueryContext ) {
$QueryContext[1].Value.Close() # SqlDataReader
$QueryContext[0].Value.Close() # SqlConnection
}
#############################################################################

# Column ID for each column of the result set of QoeGetScomInstance sproc
# See dev\server\qoe\aggregator\sql\perfcountergeneration.sql for the resultset of QoeGetScomInstance sproc
$CDR_SPROC_COLUMN_ID_MAP =
@{
"Media"=0;
"DiagnosticId"=1;
"DistinctUsers"=2;
"NumberOfCalls"=3;
"VolumePerModality"=4;
"ReasonString"=5;
"Description"=6;
"PercentageOfVolume"=7;
}

[System.Reflection.Assembly]::LoadWithPartialName("System.Web") &gt; $null

# Now compute the StartTime and EndTime. Set the current time (in UTC).
$CurrentUTCTime = [System.DateTime]::UtcNow

# Subtract 15 sec from the endtime to avoid collisions with calls that are being inserted currently
$EndUTCTime = $CurrentUTCTime.AddSeconds(-15)
TRACE ("EndUTCTime is " + $EndUTCTime)

# The query window is go past 'MinutesToQuery' number of minutes.
$StartUTCTime = $EndUTCTime.AddMinutes( -1 * $MinutesToQuery )
TRACE ("StartUTCTime is " + $StartUTCTime)

$AlertSettingsXML = CDR_GetAlertSettings $StartUTCTime $EndUTCTime $AlertThresholdPercentage $MinUsersImpacted $MinVolume
TRACE ("AlertSettingsXML is " + $AlertSettingsXML)

TRACE ("Querying the CDR DB : " + $SQLInstance)
$QueryContext = CDR_DB_OpenQueryContext $SQLInstance $MirrorSQLInstance $AlertSettingsXML
TRACE ("CDR_DB_OpenQueryContext after")

# Caution : Should use [ref] type for SqlDataReader.
# Otherwise SqlDataReader reads all records from resultset once
# (1)it is assigned to a varible or
# (2)passed as an argument to a function
$RefDataReader = CDR_DB_GetDataReaderReference $QueryContext
TRACE ("CDR_DB_GetDataReaderReference after")

# Create an Operations Manager EventLog reference.
$EventLog = New-Object System.Diagnostics.EventLog("Operations Manager")
# Set the Source to Health Service Script. This is used in the expression in the rule.
$EventLog.Source = "Health Service Script"
# Create an EventInstance reference. We pass a Task Category of None ( as indicated by 0 )
$EventInstance = New-Object System.Diagnostics.EventInstance($CDRRuleEventId, 0 , $EVENT_INFORMATION)


$lastDiagID = 0 ;
$records = New-Object System.Collections.ArrayList
do
{
while( $RefDataReader.Value.Read() )
{
TRACE ("Inside $RefDataReader.Value.Read()")


$columnName = "Media"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
TRACE ("Row Media is '" + $value)
$Media = $value

$columnName = "DiagnosticId"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
TRACE ("Row DiagnosticId is '" + $value)
$DiagnosticId = $value

$columnName = "DistinctUsers"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
TRACE ("Row DistinctUsers is '" + $value)
$DistinctUsers = $value

$columnName = "NumberOfCalls"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
TRACE ("Row NumberOfCalls is '" + $value)
$NumberOfCalls = $value

$columnName = "VolumePerModality"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
$VolumePerModality = $value
TRACE ("Row VolumePerModality is '" + $value)

$columnName = "ReasonString"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
# set a default reason string to begin with
# ( Reason strings are not always populated )
if([system.string]::IsNullOrEmpty($value))
{
$value = "[No Reason String Available]"
}

TRACE ("Row ReasonString is '" + $value)
$ReasonString = $value

$columnName = "Description"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
TRACE ("Row Description is '" + $value)
$Description = $value

$columnName = "PercentageOfVolume"
$value = $RefDataReader.Value[ $CDR_SPROC_COLUMN_ID_MAP[ $columnName ] ]
TRACE ("Row PercentageOfVolume is '" + $value)
$PercentageOfVolume = $value

if($lastDiagId -ne $DiagnosticId)
{
$lastDiagId = $DiagnosticId;
TRACE ("Last Diag Id is " + $lastDiagId)

$record = New-Object System.Collections.HashTable;
$record.Add("DiagnosticId", $DiagnosticId) ;
$record.Add("ReasonString", $ReasonString) ;
$record.Add("Description", $Description) ;

$modalities = New-Object System.Collections.ArrayList;
$record.Add("Modalities", $modalities);

$records.Add($record)
}

$modality = New-Object System.Collections.HashTable ;
$modality.Add("Media", $Media) ;
$modality.Add("DistinctUsers", $DistinctUsers) ;
$modality.Add("NumberOfCalls", $NumberOfCalls) ;
$modality.Add("VolumePerModality", $VolumePerModality) ;
$modality.Add("PercentageOfVolume", $PercentageOfVolume) ;
$modalities.Add($modality);


}
} while ($RefDataReader.Value.NextResult())

foreach($record in $records)
{
TRACE ("Last Diag Id is " + $lastDiagId)
$diagnosticId = $record["DiagnosticId"];
TRACE ("diagnosticId = " + $diagnosticId)
$reasonString = $record["ReasonString"];
TRACE ("reasonString = " + $reasonString)
$description = "Calls ( with Diagnosis code '" + $DiagnosticId + "' ) are failing at higher than expected rates.`n" +
"`n" +
"Here are the details for this Call Failure Alert:`n" +
"Diagnosis Code = " + $diagnosticId + "`n" +
"Diagnosis Reason = " + $reasonString + "`n" +
"Diagnosis Description = " + $record["Description"] + "`n" +
"`n" +
"The failure happens for the following modalities:" + "`n" ;
$modalities = $record["Modalities"]
foreach($modality in $modalities)
{
$description += "Media Type = " + $modality["Media"] + "`n" +
"Distinct Users Impacted = " + $modality["DistinctUsers"] + "`n" +
"Total Calls = " + $modality["VolumePerModality"] + "`n" +
"Failed Calls = " + $modality["NumberOfCalls"] + "`n" +
"Failure Percentage = " + $modality["PercentageOfVolume"] + "`n"
}


TRACE ("Description = " + $description)

# TODO:NAFIX
# Remove the hardcoding. Report name needs to be externalized
# Report parameter names need to be externalized
# 1 is conf, 5 is MediaId Audio, and 2 is Error Category of Unexpected errors.
# This need to be read from some common place
if([system.string]::IsNullOrEmpty($ReportingServiceUrl))
{
$reportUrlMessage = "Reporting URL is not available because the reporting pack is not installed.`n" +
"To see trouble-shooting reports, please install the reporting pack.`n"
}
else
{
$url = $null
if([System.Uri]::TryCreate($ReportingServiceUrl, [System.UriKind]::RelativeOrAbsolute, [ref]$url))
{
if($url -ne $null -and $url.Segments.Length -gt 2)
{
$ReportingServiceUrl = $url.Scheme + "://" + $url.Authority + $url.Segments[0] + $url.Segments[1]

if($ReportingServiceUrl[$ReportingServiceUrl.Length - 1] -eq '/')
{
$ReportingServiceUrl = $ReportingServiceUrl.Remove($ReportingServiceUrl.Length - 1)
}
}
}

$reportUrlMessage = "Please copy/paste this URL into your browser to get a detailed information about this failure alert:`n" +
$ReportingServiceUrl +
"/Pages/ReportViewer.aspx?/LyncServerReports/Reports_Content/Failure%20Distribution%20Report" +
"&amp;PeriodStartUTC=" + (CDR_EncodeUrl (CDR_FormatUTCTime $StartUTCTime) ) +
"&amp;PeriodEndUTC=" + (CDR_EncodeUrl (CDR_FormatUTCTime $EndUTCTime) ) +
"&amp;Filter_MsDiagId=" + $diagnosticId

}
TRACE ("reportUrlMessage = " + $reportUrlMessage)

$displayName = "Call Failure alert for Diagnosis code: " + $diagnosticId + ". (" + $reasonString + ")" ;
TRACE ("DisplayName = " + $DisplayName)

# Create an EventData Array.
# Individual event data elements can then used to set AlertParameters in the Alerting Rule.
$eventData = @(
$displayName ,
$description ,
$reportUrlMessage ,
$diagnosticId ,
$reasonString ,
$record["Description"]
)

TRACE ("Logging an event in Operations Manager log with all this info. Event Id = " + $CDRRuleEventId)
$EventLog.WriteEvent($EventInstance, $eventData)
TRACE ("Logged the event in Operations Manager log.")

}


CDR_DB_CloseQueryContext $QueryContext
TRACE ("CDR_DB_CloseQueryContext after")

TRACE ($CDR_DSMODULE_RUN_SUCCESS -f $ClusterFqdn)
</Script></ScriptBody>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<EventId>$Config/EventId$</EventId>
<EventParam1/>
<EventParam2/>
<EventParam3/>
</DataSource>
</MemberModules>
<Composition>
<Node ID="DS"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
</DataSourceModuleType>