Day 8: The
Final Product
Yesterday we
created our custom empty object. Today
we send the final set of data to the PowerShell pipeline by completing the
final two steps:
4.
Read the log records into the object.
5.
Send to the pipeline.
For this
final phase of this project, we will be adding the function New-LogObject to our cmdlet. In this function, we will place the
properties from the log file record into a PowerShell object and send these
objects into the pipeline.
Function
New-LogObject
{
Param ($PropList, $RecordIndex,
$EmptyObject,
$Log)
# Creates the
final object for each log record and returns the
#
Custom.LogObject back to the calling statement.
# Create
an instance of Custome.Logobject
$Obj
= $EmptyObject
# Cycle
through each record and populate the object.
ForEach($R in $RecordIndex)
{
#
Write the slot information.
$Pos
= $Log[$R.Start].Indexof("#")
$Value
= $Log[$R.Start].Substring($Pos+1)
$Pos
= $Value.IndexOf("
")
$Obj.slot = $Value.SubString(0,$Pos).Trim()
#
Cycle through each property of this record.
#
Convert the property name to the same format as those in
# the
$PropList using the Format-PropertyName function.
#
Compare the property names. If a match
is found, then
# add
the data to the property in the object.
For($X=$R.Start+1;$X -lt $R.End; $X++)
{
#
Examine only lines from the record that are not blank.
If
(($Log[$X].length -gt 0) -and ($Log[$X] -like "*:*"))
{
$PropName
= Format-PropertyName
-Name $Log[$X]
ForEach
($P in
$PropList.Prop)
{
If ($PropName -eq $P)
{
# Extract the data.
$String = $Log[$X].Trim()
$Pos = $String.IndexOF(":")
$Value = $String.Substring($Pos+1).Trim()
$Obj.$P = $Value
} # End: If ($PropName -eq $P)
} #
End: ForEach ($P in $PropList.Prop)
} #
End: If (($Log[$X].length -gt 0) -and ($Log[$X] -like "*:*"))
} #
End: For($X=$R.Start+1;$X -lt $R.End; $X++)
#
Return the object to the calling statement.
Write-Output
$Obj
}
} # End:
Function New-LogObject
We pass the
objects that we created in the previous functions and the log itself to the New-LogObject function. We then cycle through each record using
the values in the $RecordIndex object. The first item in each record contains the Slot property value. 4 lines of code is dedicated to filtering out
this value from the string and placing it in the Slot property. Next each
line is examine. If the line is not
empty and matches the pattern “*:*” then the line is consider to have a
property and a value. The Format-PropertyName
function is used to extract the property name. The ForEach
loop will cycle through each property name in the $PropList object until a match is found with the property currently
being examined in the log. The value of
this property is extracted from the log file and placed in the corresponding
property in the object. Once all the
properties in the $PropList object
has been compared, the object is returned and sent to the PowerShell Pipeline.
Here is the
full code:
Function Import-MyLog2
{
#
-----------------------------------------------------------------------
Function
Get-Log
{
# Reads the
log file into memory. Will display an
error if the
# file cannot
be found.
Try
{
Get-Content
-path "\\Server1\Log\Data2."
`
-ErrorAction Stop |
Write-Output
}
Catch
{
Write-Error
"The data file is not present"
BREAK
}
} # End:
Function Get-Log
#
-----------------------------------------------------------------------
Function
Get-RecordIndex
{
Param ($Log)
# Determines
the index number of where each record starts and ends.
# Returns an
object with the start and end index number of
# each
record.
# Array
to hold all of the objects.
$Object
= @()
# Find
the start of each record.
For($X=0;$X -lt $Log.Count; $X++)
{
If($Log[$X] -like "Server
Blade #* Information:")
{
$Obj
= New-Object
-TypeName PSObject
$Obj
| Add-Member
-NotePropertyName "Start"
`
-NotePropertyValue $X
$Obj
| Add-Member
-NotePropertyName "End"
`
-NotePropertyValue $Null
$Object
+= $Obj
}
} # End:
For($X=0;$X -lt $Log.Count; $X++)
# Derive
the end of each record.
For
($X = 0; $X -lt $Object.Count - 1; $X++)
{
$Object[$X].End = $Object[$X+1].Start -1
} # End:
For ($X = 0; $X -lt $Object.Count; $X++)
# Add the
final End value as the end of the log file.
$Object[$Object.count-1].End = $Log.count
# Return
the object to the calling statement.
Write-Output
$Object
} # End:
Function Get-RecordIndex
#
-----------------------------------------------------------------------
Function
Format-PropertyName
{
Param ($Name)
# Used to get
the proper name format for the list of properties and also
# While
creating the actual data for the final objects.
$Value
= $Name.Trim()
$Pos
= $Value.IndexOf(":")
Write-Output
$Value.Substring(0,$Pos).Replace(" ","")
} # End:
Function Format-PropertyName
#
-----------------------------------------------------------------------
Function
Get-PropertyList
{
Param($RecordIndex,
$Log)
# To help
build a more flexible object, we need to identify possible
# property
names. This function will cycle through
each record and use
# a rule to
identify the name of a property. Our rule
is any string
# other than
the string that starts the record that starts with blank
# characters,
has text, and the it will end at the first ":". All spaces
# in the
property names will also be removed.
# Array
to hold all of the objects.
$Object
= @()
ForEach
($R in
$RecordIndex)
{
For
($X=$R.Start+1;$X -lt $R.End;$X++)
{
#
Examine only lines from the record that are not blank.
If
(($Log[$X].length -gt 0) -and ($Log[$X] -like "*:*"))
{
$Obj
= New-Object
-TypeName PSObject
$Obj
| Add-Member
-MemberType NoteProperty
`
-Name "Prop"
`
-Value
(Format-PropertyName -Name
$Log[$X])
$Object
+= $Obj
}
} #
End: For ($X=$R.Start;$X -lt $R.End;$X++)
} # End:
ForEach ($R in $RecordIndex)
# Add the
"Slot" property to record the slot number.
$Obj
= New-Object
-TypeName PSObject
$Obj
| Add-Member
-MemberType NoteProperty
`
-Name "Prop"
`
-Value "Slot"
$Object
+= $Obj
# Filter the list of protential properties to make sure
that they
# are all
unique.
$Object
= $Object
| Select-Object
-Property Prop
-Unique
# Return
the object to the calling statement.
Write-Output
$Object
} # End:
Function Get-PropertyList
#
-----------------------------------------------------------------------
Function
Get-Record
{
Param ($Log,$RecordIndex)
# Array
to hold all of the objects.
$Object
= @()
ForEach($R in $RecordIndex)
{
$Obj
= New-Object
-TypeName PSObject
$String
= $Null
For($X=$R.Start;$X -lt $R.End; $X++)
{
$String
+= $Log[$X]
}
$Object
+= $String
}
# Return
the object to the calling statement.
Write-Output
$Object
} # End:
Function Get-Record
#
-----------------------------------------------------------------------
Function
New-EmptyObject
{
Param ($PropList)
# Creates an
empty object to be used for each instance of the final
# object.
This ensures a consistent set of members for the object.
$Obj
= New-Object
-TypeName psobject
# Change
the type name.
$Obj.pstypenames.insert(0,'Custom.LogObject')
ForEach
($P in
$Proplist.Prop)
{
$Obj
| Add-Member
-NotePropertyName "$($P)" `
-NotePropertyValue $Null
} # End:
ForEach ($P in $Proplist.Prop)
# Return
the object to the calling statement.
Write-Output
$Obj
} # End
Function New-EmptyObject
#
-----------------------------------------------------------------------
Function
New-LogObject
{
Param ($PropList, $RecordIndex,
$EmptyObject,
$Log)
# Creates the
final object for each log record and returns the
#
Custom.LogObject back to the calling statement.
# Create
an instance of Custome.Logobject
$Obj
= $EmptyObject
# Cycle through each record and populate the object.
ForEach($R in $RecordIndex)
{
#
Write the slot information.
$Pos
= $Log[$R.Start].Indexof("#")
$Value
= $Log[$R.Start].Substring($Pos+1)
$Pos
= $Value.IndexOf("
")
$Obj.slot = $Value.SubString(0,$Pos).Trim()
#
Cycle through each property of this record.
#
Convert the property name to the same format as those in
# the
$PropList using the Format-PropertyName function.
#
Compare the property names. If a match
is found, then
# add
the data to the property in the object.
For($X=$R.Start+1;$X -lt $R.End; $X++)
{
#
Examine only lines from the record that are not blank.
If
(($Log[$X].length -gt 0) -and ($Log[$X] -like "*:*"))
{
$PropName
= Format-PropertyName
-Name $Log[$X]
ForEach
($P in
$PropList.Prop)
{
If ($PropName -eq $P)
{
# Extract the data.
$String = $Log[$X].Trim()
$Pos = $String.IndexOF(":")
$Value
= $String.Substring($Pos+1).Trim()
$Obj.$P = $Value
} # End: If ($PropName -eq $P)
} #
End: ForEach ($P in $PropList.Prop)
} #
End: If (($Log[$X].length -gt 0) -and ($Log[$X] -like "*:*"))
} #
End: For($X=$R.Start+1;$X -lt $R.End; $X++)
#
Return the object to the calling statement.
Write-Output
$Obj
}
} # End:
Function New-LogObject
#
-----------------------------------------------------------------------
# Load the
log into memory
$Log = Get-Log
# Extract the
records
$RecordIndex
= Get-RecordIndex
-Log $Log
# Extract the
propertyl list.
$PropList = Get-PropertyList -RecordIndex
$RecordIndex -Log
$Log
# Create an
empty object to use as our template using our $PropList.
$EmptyObj
= New-EmptyObject
-PropList $PropList
# Build the
final object.
New-LogObject
-PropList $PropList
`
-RecordIndex
$RecordIndex `
-EmptyObject
$EmptyObj `
-Log
$Log
} #End: Function Import-MyLog1
Here is the output:
ServerBladeType : No Server Blade Installed
Type :
Manufacturer :
ProductName :
PartNumber :
SystemBoardSparePartNumber :
SerialNumber :
UUID :
ServerName :
AssetTag :
ROMVersion :
CPU1 :
CPU2 :
Memory :
NIC1 :
NIC2
:
NIC3 :
NIC4 :
iSCSI1 :
iSCSI2 :
Mezzanine2 :
Port1 :
Port2
:
ManagementProcessorInformation :
Name :
FirmwareVersion :
IPAddress :
MACAddress :
PowerManagementControllerVersion :
FLBAdapter1 :
EthernetFlexNIC(NIC1)LOM1 :
EthernetFlexNIC(NIC3)LOM1 :
EthernetFlexNIC(NIC5)LOM1 :
EthernetFlexNIC(NIC7)LOM1 :
EthernetFlexNIC(NIC2)LOM1 :
EthernetFlexNIC(NIC4)LOM1 :
EthernetFlexNIC(NIC6)LOM1 :
EthernetFlexNIC(NIC8)LOM1 :
Slot : 7
ServerBladeType : No Server Blade Installed
Type : iLO2
Manufacturer : HP
ProductName : ProLiant BL480c G1
PartNumber : 404707-B21
SystemBoardSparePartNumber : 438453-001
SerialNumber : 9999999999
UUID :
37343034-3730-4247-3837-999999999999
ServerName : SRV000001
AssetTag : [Unknown]
ROMVersion : I14 10/25/2010
CPU1 : Dual-Core Intel
Xeon 3000 MHz
CPU2 : Dual-Core Intel
Xeon 3000 MHz
Memory : 49152 MB
NIC1 :
00:19:BB:34:61:1C
NIC2 :
00:19:BB:34:41:24
NIC3 :
00:19:BB:29:92:37
NIC4 :
00:19:BB:29:92:38
iSCSI1 : 00:19:BB:34:61:1D
iSCSI2 : 00:19:BB:34:41:25
Mezzanine2 : QLogic QMH2462 4Gb FC
HBA for HP c-Class BladeSystem
Port1 :
50:06:0b:00:00:6b:d3:9c
Port2
: 50:06:0b:00:00:6b:d3:9e
ManagementProcessorInformation :
Name : SRV000001-i
FirmwareVersion : 2.12 Jul 16 2012
IPAddress : 10.199.10.240
MACAddress : 00:19:AA:34:21:99
PowerManagementControllerVersion : 0.5
FLBAdapter1 :
EthernetFlexNIC(NIC1)LOM1 :
EthernetFlexNIC(NIC3)LOM1 :
EthernetFlexNIC(NIC5)LOM1 :
EthernetFlexNIC(NIC7)LOM1 :
EthernetFlexNIC(NIC2)LOM1 :
EthernetFlexNIC(NIC4)LOM1 :
EthernetFlexNIC(NIC6)LOM1 :
EthernetFlexNIC(NIC8)LOM1 :
Slot : 8
ServerBladeType : Bay Subsumed
Type : iLO2
Manufacturer : HP
ProductName : ProLiant BL480c G1
PartNumber : 404707-B21
SystemBoardSparePartNumber : 438453-001
SerialNumber : 9999999999
UUID : 37343034-3730-4247-3837-999999999999
ServerName : SRV000001
AssetTag : [Unknown]
ROMVersion : I14 10/25/2010
CPU1 : Dual-Core Intel
Xeon 3000 MHz
CPU2 : Dual-Core Intel Xeon 3000
MHz
Memory : 49152 MB
NIC1 :
00:19:BB:34:61:1C
NIC2 :
00:19:BB:34:41:24
NIC3 :
00:19:BB:29:92:37
NIC4 :
00:19:BB:29:92:38
iSCSI1 : 00:19:BB:34:61:1D
iSCSI2 : 00:19:BB:34:41:25
Mezzanine2 : QLogic QMH2462 4Gb FC
HBA for HP c-Class BladeSystem
Port1 : 50:06:0b:00:00:6b:d3:9c
Port2 :
50:06:0b:00:00:6b:d3:9e
ManagementProcessorInformation :
Name : SRV000001-i
FirmwareVersion : 2.12 Jul 16 2012
IPAddress : 10.199.10.240
MACAddress : 00:19:AA:34:21:99
PowerManagementControllerVersion : 0.5
FLBAdapter1 :
EthernetFlexNIC(NIC1)LOM1 :
EthernetFlexNIC(NIC3)LOM1 :
EthernetFlexNIC(NIC5)LOM1 :
EthernetFlexNIC(NIC7)LOM1 :
EthernetFlexNIC(NIC2)LOM1 :
EthernetFlexNIC(NIC4)LOM1 :
EthernetFlexNIC(NIC6)LOM1 :
EthernetFlexNIC(NIC8)LOM1 :
Slot : 9
ServerBladeType : Bay Subsumed
Type : iLO2
Manufacturer : HP
ProductName : ProLiant BL480c G1
PartNumber : 404707-B21
SystemBoardSparePartNumber : 438453-001
SerialNumber : 9999999999
UUID :
37343034-3730-4247-3837-999999999999
ServerName : SRV000001
AssetTag : [Unknown]
ROMVersion : I14 10/25/2010
CPU1 : Dual-Core Intel
Xeon 3000 MHz
CPU2 : Dual-Core Intel
Xeon 3000 MHz
Memory : 49152 MB
NIC1 :
00:19:BB:34:61:1C
NIC2 :
00:19:BB:34:41:24
NIC3 :
00:19:BB:29:92:37
NIC4 :
00:19:BB:29:92:38
iSCSI1 : 00:19:BB:34:61:1D
iSCSI2 : 00:19:BB:34:41:25
Mezzanine2
: QLogic QMH2462 4Gb FC
HBA for HP c-Class BladeSystem
Port1 :
50:06:0b:00:00:6b:d3:9c
Port2 :
50:06:0b:00:00:6b:d3:9e
ManagementProcessorInformation :
Name : SRV000001-i
FirmwareVersion : 2.12 Jul 16 2012
IPAddress : 10.199.10.240
MACAddress : 00:19:AA:34:21:99
PowerManagementControllerVersion : 0.5
FLBAdapter1 :
EthernetFlexNIC(NIC1)LOM1 :
EthernetFlexNIC(NIC3)LOM1 :
EthernetFlexNIC(NIC5)LOM1 :
EthernetFlexNIC(NIC7)LOM1 :
EthernetFlexNIC(NIC2)LOM1 :
EthernetFlexNIC(NIC4)LOM1 :
EthernetFlexNIC(NIC6)LOM1 :
EthernetFlexNIC(NIC8)LOM1 :
Slot : 10
ServerBladeType : Bay Subsumed
Type : iLO4
Manufacturer : HP
ProductName : ProLiant BL460c Gen8
PartNumber : 641016-B21
SystemBoardSparePartNumber : 719592-001
SerialNumber : 9999999999
UUID :
30313436-3631-5A43-3332-99999999999
ServerName : SRV0000002
AssetTag : [Unknown]
ROMVersion : I31 08/20/2012
CPU1 : Intel(R) Xeon(R)
CPU E5-2609 0 @ 2.40GHz (4 cores)
CPU2 : Intel(R) Xeon(R)
CPU E5-2609 0 @ 2.40GHz (4 cores)
Memory : 16384 MB
NIC1 :
00:19:BB:34:61:1C
NIC2 :
00:19:BB:34:41:24
NIC3 :
00:19:BB:29:92:37
NIC4 :
00:19:BB:29:92:38
iSCSI1 : 00:19:BB:34:61:1D
iSCSI2 : 00:19:BB:34:41:25
Mezzanine2 : QLogic QMH2462 4Gb FC
HBA for HP c-Class BladeSystem
Port1 :
50:06:0b:00:00:6b:d3:9c
Port2 : 50:06:0b:00:00:6b:d3:9e
ManagementProcessorInformation :
Name : SRV0000002_i
FirmwareVersion : 1.10 Jul 17 2012
IPAddress : 20.199.8.40
MACAddress
: 10:61:4B:AD:C2:A7
PowerManagementControllerVersion : 3.0
FLBAdapter1 : HP FlexFabric 10Gb
2-port 554FLB Adapter
EthernetFlexNIC(NIC1)LOM1 : 1-a
6C:3B:E5:A9:DB:B0
EthernetFlexNIC(NIC3)LOM1 : 1-b
6C:3B:E5:A9:DB:B1
EthernetFlexNIC(NIC5)LOM1 : 1-c
6C:3B:E5:A9:DB:B2
EthernetFlexNIC(NIC7)LOM1 : 1-d
6C:3B:E5:A9:DB:B3
EthernetFlexNIC(NIC2)LOM1 : 2-a
6C:3B:E5:A9:DB:B4
EthernetFlexNIC(NIC4)LOM1 : 2-b
6C:3B:E5:A9:DB:B5
EthernetFlexNIC(NIC6)LOM1 : 2-c
6C:3B:E5:A9:DB:B6
EthernetFlexNIC(NIC8)LOM1 : 2-d
6C:3B:E5:A9:DB:B7
Slot
Here is the member
information for the returned object:
PS C:\> Import-MyLog2 | Get-Member
TypeName:
Custom.LogObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
AssetTag NoteProperty AssetTag=null
CPU1 NoteProperty CPU1=null
CPU2 NoteProperty CPU2=null
EthernetFlexNIC(NIC1)LOM1 NoteProperty EthernetFlexNIC(NIC1)LOM1=null
EthernetFlexNIC(NIC2)LOM1 NoteProperty EthernetFlexNIC(NIC2)LOM1=null
EthernetFlexNIC(NIC3)LOM1 NoteProperty EthernetFlexNIC(NIC3)LOM1=null
EthernetFlexNIC(NIC4)LOM1 NoteProperty EthernetFlexNIC(NIC4)LOM1=null
EthernetFlexNIC(NIC5)LOM1 NoteProperty EthernetFlexNIC(NIC5)LOM1=null
EthernetFlexNIC(NIC6)LOM1 NoteProperty EthernetFlexNIC(NIC6)LOM1=null
EthernetFlexNIC(NIC7)LOM1 NoteProperty EthernetFlexNIC(NIC7)LOM1=null
EthernetFlexNIC(NIC8)LOM1 NoteProperty EthernetFlexNIC(NIC8)LOM1=null
FirmwareVersion NoteProperty FirmwareVersion=null
FLBAdapter1 NoteProperty FLBAdapter1=null
IPAddress NoteProperty IPAddress=null
iSCSI1
NoteProperty iSCSI1=null
iSCSI2 NoteProperty iSCSI2=null
MACAddress NoteProperty MACAddress=null
ManagementProcessorInformation NoteProperty
ManagementProcessorInformation=null
Manufacturer NoteProperty Manufacturer=null
Memory
NoteProperty Memory=null
Mezzanine2 NoteProperty Mezzanine2=null
Name NoteProperty Name=null
NIC1 NoteProperty NIC1=null
NIC2 NoteProperty NIC2=null
NIC3
NoteProperty NIC3=null
NIC4 NoteProperty NIC4=null
PartNumber NoteProperty PartNumber=null
Port1 NoteProperty Port1=null
Port2 NoteProperty Port2=null
PowerManagementControllerVersion NoteProperty PowerManagementControllerVersion=null
ProductName NoteProperty ProductName=null
ROMVersion NoteProperty ROMVersion=null
SerialNumber NoteProperty SerialNumber=null
ServerBladeType NoteProperty System.String
ServerBladeType=No Server Blade Installed
ServerName NoteProperty ServerName=null
Slot NoteProperty
System.String Slot=7
SystemBoardSparePartNumber NoteProperty SystemBoardSparePartNumber=null
Type NoteProperty Type=null
UUID NoteProperty UUID=null
And finaly, here is an
example sending this data into the pipeline:
PS C:\> Import-MyLog2 | Where iscsi1 -ne $null |
Select-Object -Property Slot, iSCSI1, SerialNumber | FT -AutoSize
Slot iSCSI1
SerialNumber
---- ------
------------
8 00:19:BB:34:61:1D
9999999999
9 00:19:BB:34:61:1D
9999999999
10 00:19:BB:34:61:1D
9999999999
11 00:19:BB:34:61:1D
9999999999
Remember that you will
have to determine the rules to:
1.
Determine the start and end of each log.
2.
Determine how to extract a property name:
3.
Determine how to extract a property value.
Follow the steps in this
series and you will be able to utilize the log files in text format just as
easily as you can utilize log files in CSV or XML format.
Comments
I have a question though, I am trying to use this script but I want to use a static 'PropertyList' so all the sheets have the same amount of columns and headers.
How would I approach this?
Take a look at what I posted today (http://www.mctexpert.blogspot.com/2013/10/discover-who-is-logged-onto-your-clients.html)
Take a look at the function CustomObj. You can pre-define a object with the properties that you want. The logic to get information from your text based log files into your custom objects properties will have to come from you. Look at the rest of the example to see how I did it.
Jason
One suggestion if you wanted to improve on it would be to turn the property/value delimiter into a variable
I also turned a few other things into variables such as the file path and name as I will be looping through a lot of files and passing them in. I am also grabbing the date which was in the filename and storing as a property..