Public/Build-LMDataModel.ps1
<# .SYNOPSIS Generates a data model for ingest by PushMetrics .DESCRIPTION Using a sample device or list of Datasources, will generate a model that can be replayed for simulated device activity by Submit-LMDataModel .PARAMETER DatasourceNames An Array of Datasources to model, only used if not modeling a specific device. .PARAMETER IncludeModuleGraphs Include graph configuration data in generated model. .PARAMETER IncludeAlertThresholds Include alert threshold configuration data in generated model. .PARAMETER GenerateInstances If generating a model based on a datasource, enable instance generation .PARAMETER InstanceCount How many instances to generate when using GenerateInstances command, defaults to 1 if not set .PARAMETER SimulationType Simulation type to generate for the model. Currently not used will be worked on in the future .PARAMETER DeviceHostName HostName of the mock device that is generated by the model. .PARAMETER DeviceDisplayName DisplayName of the mock device that is generated by the model. .PARAMETER ModelDeviceHostName HostName of the source device that will be used as a reference for the generated model. .PARAMETER IncludeModelDeviceProperties Include reference device properties in generated model, includes instance level properties as well. .PARAMETER IncludeModelDeviceData Include a sample dataset from the model device for the previous 24 hours, this will be used to simulate data generation when submitting the model for ingest. .EXAMPLE Build-LMDataModel -ModelDeviceHostName "192.168.1.13" -IncludeAlertThresholds -IncludeModuleGraphs -DeviceHostName 1.1.1.17 -DeviceDisplayName LocalHost7 .INPUTS None. You cannot pipe objects to this command. .LINK Module repo: https://github.com/stevevillardi/Logic.Monitor.SE .LINK PSGallery: https://www.powershellgallery.com/packages/Logic.Monitor.SE #> Function Build-LMDataModel { [CmdletBinding(DefaultParameterSetName="ModelDatasources")] [OutputType([System.Collections.Generic.List[object]])] Param( [Parameter(Mandatory,ParameterSetName="ModelDatasources")] [System.Collections.Generic.List[object]]$DatasourceNames, [Switch]$IncludeModuleGraphs, [Switch]$IncludeAlertThresholds, [Parameter(ParameterSetName = 'GenerateInstances')] [Parameter(Mandatory,ParameterSetName="ModelDatasources")] [Switch]$GenerateInstances, [Parameter(ParameterSetName = 'GenerateInstances')] [Parameter(Mandatory,ParameterSetName="ModelDatasources")] [Int]$InstanceCount = 1, [ValidateSet("8to5","random","replication","replay_model")] [String]$SimulationType="random", [Parameter(Mandatory)] [String]$DeviceHostName, [String]$DeviceDisplayName = $DeviceHostName, [Parameter(Mandatory,ParameterSetName = 'ModelDevice')] [String]$ModelDeviceHostName, [Parameter(ParameterSetName = 'ModelDevice')] [Switch]$IncludeModelDeviceProperties, [Parameter(ParameterSetName = 'ModelDevice')] [String]$ModelDeviceInstanceCount = 24, [Parameter(ParameterSetName = 'ModelDevice')] [Switch]$IncludeModelDeviceData ) #If we have a model deivce make sure we can grab it from the connected portal fist If($ModelDeviceHostName){ $DatasourceNames = [System.Collections.Generic.List[object]]::New() $ModelDevice = Get-LMDevice -Name $ModelDeviceHostName | Select-Object -First 1 If($ModelDevice){ Write-Debug "Located model device, using device ($ModelDeviceHostName) as model candidate.(ID: $($ModelDevice.id))" $ModelDeviceDatasources = Get-LMDeviceDatasourceList -id $ModelDevice.Id | Where-Object {$_.instanceNumber -gt 0} $DatasourceNames.AddRange($ModelDeviceDatasources.datasourceName) } Else{ Write-Error "Unable to locate provided model device ($ModelDeviceHostName), ensure hostname is correct and try again." Return } } #If set to collect model data override simulation type to replay model If($IncludeModelDeviceData){$SimulationType = "replay_model"} $DataSourceModels = [System.Collections.Generic.List[object]]::New() $i = 0 $StepsCount = 5 $DSCount = ($DatasourceNames | Measure-Object).Count #Loop through each provided DSName to generate models Foreach($DatasourceName in $DatasourceNames){ Write-Progress -Activity "Generating Model for datasource: $DatasourceName" -Status "$([Math]::Floor($($i/$DSCount*100)))% Completed" -PercentComplete $($i/$DSCount*100) -Id 0 #Grab specified source DS for DatasourceName so we can model it for PushMetrics Write-Debug "Using provided datasource name ($DatasourceName) as model candidate." $DatasourceInfo = Get-LMDatasource -Name $DatasourceName $Datasource = $DatasourceInfo | Select-Object description,group,tags,name,displayName,collectInterval,hasMultiInstances,dataPoints #Do we have a valid DS to model If($Datasource){ #Extract what we need from the source datasource to use as basis for modeling $DatasourceDefenition = $Datasource | Select-Object description,tags,name,displayName,collectInterval,hasMultiInstances $Datapoints = $Datasource.datapoints $DatasourceGroupName = $Datasource.group $DatapointDefenition = [System.Collections.Generic.List[object]]::New() $DPCount = ($Datapoints | Measure-Object).Count #Estimate datapoint type for model generation Write-Progress -Activity "Processing $DPCount datapoint(s) for datasource: $DatasourceName" -ParentId 0 -Id 1 -Status "$(1/$StepsCount*100)% Completed" -PercentComplete $(1/$StepsCount*100) Foreach ($Datapoint in $Datapoints){ $MetricType = Resolve-MetricType -Datapoint $Datapoint.name If($Datapoint.postProcessorMethod -eq "expression"){ $Type = "complex" $ComplexDatapointFormula = $Datapoint.postProcessorParam } Else{ $Type = "standard" $ComplexDatapointFormula = "N/A" } Write-Debug "Detected $Type datapoint $($Datapoint.name) matching metric type: $MetricType" $DatapointDefenition.Add([PSCustomObject]@{ Name = $Datapoint.name MaxValue = $Datapoint.maxValue MinValue = $Datapoint.minValue Description = $Datapoint.description Type = $Type ComplexDatapointFormula = $ComplexDatapointFormula AlertForNoData = If($IncludeAlertThresholds){[Boolean]!$Datapoint.alertForNoData} AlertExpr = If($IncludeAlertThresholds){$Datapoint.alertExpr} AlertSubject = If($IncludeAlertThresholds){$Datapoint.alertSubject} AlertBody = If($IncludeAlertThresholds){$Datapoint.alertBody} MetricType = $MetricType }) } #Get Graph settings we will need to model our PushMetric DS after If($IncludeModuleGraphs){ Write-Progress -Activity "Exporting existing datasource overview graph defenition(s) for datasource: $DatasourceName" -ParentId 0 -Id 1 -Status "$(2/$StepsCount*100)% Completed" -PercentComplete $(2/$StepsCount*100) Write-Debug "Exporting existing datasource overview graph defenition(s) for model export." $DatasourceGraphModel = Get-LMDatasourceGraph -DatasourceName $Datasource.Name | Select-Object -ExcludeProperty id Write-Progress -Activity "Exporting existing datasource instance graph defenition(s) for datasource: $DatasourceName" -ParentId 0 -Id 1 -Status "$(3/$StepsCount*100)% Completed" -PercentComplete $(3/$StepsCount*100) Write-Debug "Exporting existing datasource instance graph defenition(s) for model export." $DatasourceOverviewGraphModel = Get-LMDatasourceOverviewGraph -DatasourceName $Datasource.Name | Select-Object -ExcludeProperty id } #Generate Instances if not specified $Instances = [System.Collections.Generic.List[object]]::New() Write-Progress -Activity "Generating instances for datasource: $DatasourceName" -ParentId 0 -Id 1 -Status "$(4/$StepsCount*100)% Completed" -PercentComplete $(4/$StepsCount*100) If($GenerateInstances -and [Boolean]$Datasource.hasMultiInstances){ Write-Debug "Generating $InstanceCount instance(s) for data model export." $Instances.AddRange($(Build-Instance -InstanceCount $InstanceCount -Datasource $DatasourceDefenition)) } ElseIf($GenerateInstances){ Write-Debug "Datasource $($Datasource.name) not enabled for multiple instances, default instance creation to single instance for data model export." $Instances.Add($(Build-Instance -SingleInstance -Datasource $DatasourceDefenition)) } ElseIf($ModelDeviceHostName -and $ModelDeviceDatasources){ $ModelInstances = Get-LMDeviceDatasourceInstance -DatasourceId $DatasourceInfo.Id -DeviceId $ModelDevice.Id Write-Debug "Datasource $($Datasource.name) is being model after device $($ModelDevice.DisplayName) , adding up to $ModelDeviceInstanceCount instances from pool of $(($ModelInstances | Measure-Object).Count) existing instance(s) to data model export." If($($ModelInstances | Measure-Object).Count -gt 1){ $Instances.AddRange($(Build-Instance -ModelInstance -InstanceList $($ModelInstances | Sort-Object -Property Id | Select-Object -First $ModelDeviceInstanceCount) -Datasource $DatasourceDefenition -IncludeModelData:$IncludeModelDeviceData -ModelDeviceHostName $ModelDeviceHostName)) } Else{ $Instances.Add($(Build-Instance -ModelInstance -InstanceList $ModelInstances -Datasource $DatasourceDefenition -IncludeModelData:$IncludeModelDeviceData -ModelDeviceHostName $ModelDeviceHostName)) } } Write-Progress -Activity "Adding datasource: $DatasourceName to model export" -ParentId 0 -Id 1 -Status "$(5/$StepsCount*100)% Completed" -PercentComplete $(5/$StepsCount*100) #Combine our model info together for export $DataSourceModel = [PSCustomObject]@{ DatasourceName = $DatasourceName DatasourceGroupName = $DatasourceGroupName Instances = $Instances Defenition = $DatasourceDefenition Datapoints = $DatapointDefenition OverviewGraphs = $DatasourceOverviewGraphModel Graphs = $DatasourceGraphModel } } Write-Debug "Exporting data model ($DatasourceName)." $DataSourceModels.Add($DataSourceModel) $i++ } $PropertiesHash = @{} If($IncludeModelDeviceProperties){ $Properties = $($ModelDevice.autoProperties + $ModelDevice.customProperties) | Where-Object {$_.name -notmatch "auto.snmp.|auto.network.|snmp.|auto.lm.|.pass|.community|.key|.authToken|.privToken|.token|.cert|.secret|.user|system.|.id" -and $null -ne $_.value -and $_.value -ne ""} Foreach ($Prop in $Properties){ $Prop.name = "autodiscovery." + $Prop.name.replace("auto.","") $PropertiesHash.Add($Prop.name,$Prop.value.trim()) } } $DeviceModel = [PSCustomObject]@{ HostName = $DeviceHostName DisplayName = $DeviceDisplayName Properties = [PSCustomObject]$PropertiesHash SimulationType = $SimulationType Datasources = $DataSourceModels } Return $DeviceModel } Function Resolve-MetricType { Param( [Parameter(Mandatory)] [Object]$Datapoint ) Switch -Regex ($Datapoint){ '[Pp]kts|[Bb]ps|[Mm]bps|[Rr]ate|[Tt]ime|[Dd]uration' {Return "Rate"} '[Pp]ercent|[Uu]til' {Return "Percentage"} '[Ww]rite|[Rr]ead|[Ii]ops' {Return "IO-Latency"} '[Ff]ree|[Uu]sed|[Rr]eserved|[Aa]vailable' {Return "SpaceUsage"} '[Ss]tatus|[Ss]tate|[Ee]nabled' {Return "Status"} default {Return "Count"} } } Function Build-Instance { Param( [Switch]$SingleInstance, [Switch]$ModelInstance, [System.Collections.Generic.List[object]]$InstanceList, [Int]$InstanceCount, [Boolean]$IncludeModelData, [String]$ModelDeviceHostName, $Datasource ) $Instances = [System.Collections.Generic.List[object]]::New() #If single instance just return datasource name as instance If($SingleInstance){ Write-Debug "Single instance datasource detected, skipping instance generation usings datasource name as instance" $Instances.Add([PSCustomObject]@{ Name = ($Datasource.name -replace '[#\\/();=&]', '_') DisplayName = ($Datasource.displayName -replace '[#\\/;=]', '_') Description = "" Properties = @{} Type = "SingleInstance" Data = @() }) Return $Instances } If($ModelInstance){ #Add generated instance to instance list Write-Debug "Instances found for export: ($(($InstanceList[0..9].Name -Join ","))...)" Foreach($Instance in $InstanceList){ $Data = [System.Collections.Generic.List[object]]::New() If($IncludeModelData){ Write-Debug "Extracting last 24 hours of model device data for instance ($($Instance.name))" $InstanceData = Get-LMDeviceData -DeviceName $ModelDeviceHostName -DatasourceName $Datasource.name -InstanceName $Instance.name -StartDate (Get-Date).AddHours(-24) -EndDate (Get-Date) If($InstanceData){ $DataCount = ($InstanceData | Measure-Object).Count Write-Debug "Added $DataCount time series metrics to data model for ($($Instance.name))" $Data.AddRange($InstanceData) } Else{ Write-Debug "No recent instance data found for ($($Instance.name))" } } If($IncludeModelDeviceProperties){ $PropertiesHash = @{} $Properties = $($Instance.customProperties + $Instance.autoProperties) | Where-Object {$_.name -notmatch "auto.snmp.|auto.network.|snmp.|auto.lm.|.pass|.community|.key|.authToken|.privToken|.token|.cert|.secret|.user|system.|.id" -and $null -ne $_.value -and $_.value -ne ""} Foreach ($Prop in $Properties){ $Prop.name = "autodiscovery." + $Prop.name.replace("auto.","") $PropertiesHash.Add($Prop.name,$Prop.value.trim()) } } $Instances.Add([PSCustomObject]@{ Name = (($Instance.name -replace '[#\\/;=&]', '_') -replace '[\[\]{}()<>]','') DisplayName = If($Instance.displayName){(($Instance.displayName -replace '[#\\;=]', '_') -replace '[\[\](){}<>]','')}Else{(($Datasource.displayName -replace '[#\\;=]', '_') -replace '[\[\]{}()<>]','')} Description = $Instance.description Properties = [PSCustomObject]$PropertiesHash Type = "DeviceModeled" Data = $Data }) } Return $Instances } Else{ If($Datasource.tags){ $Type = Switch -Regex ($Datasource.tags){ 'inode|disk|drive' {"Disk"} 'filesystem|file' {"FileSystem"} 'core|cpu|processor' {"CPU"} 'vlan' {"VLAN"} 'chassis|array|cluster|node' {"Node"} 'pdu|ups|battery' {"Battery"} 'psu|power' {"PowerSupply"} 'temperature|voltage' {"Sensor"} 'interface|port' {"Interface"} 'ec2|compute engine|vm' {"Server"} default {"Instance"} } } Else{ $Type = "Unknown" } Write-Debug "Datasource instance type detected as $Type, using type for instance generation" #For multiple instances loop through and generate insance types For (($i = 1); $i -le $InstanceCount; $i++){ #Generate instance name based on tags and dp names $Name = $Type + '{0:d2}' -f $i $DisplayName = $Type + '{0:d2}' -f $i $Description = "" #Add generated instance to instance list $Instances.Add([PSCustomObject]@{ Name = ($Name -replace '[#\\;=]', '_') DisplayName = ($DisplayName -replace '[#\\;=]', '_') Description = $Description Properties = @{} Type = $Type Data = @() }) } } Return $Instances } |