Wednesday, May 26, 2010

My Regex Lessons (Probably 1 of many…)

OK, that previous post had a couple of minor problems.

First that regular expression – terribly unwieldy. I haven’t managed to shorten it fully yet, but this is marginally better:

"([\w\s:;… add whatever characters are valid here…]*)(?:\",|\"$)


Secondly, I wasn’t fully thinking through the matches/groups/captures hierarchy. I kept getting empty captures being reported back without understanding why I was getting them.



Updated code here:



open System
open System.Text.RegularExpressions

let (|ActiveRegex|_|) regex str =
let ms = Regex(regex).Matches(str)
if ms.Count > 0
then Some ((Seq.cast ms : Match seq))
else None

let matches s re =
match s with
| ActiveRegex re results -> results
| _ -> Seq.empty

let capturesSeq s p =
seq{
for m in matches s p ->
Seq.skip 1 (seq{for g in m.Groups -> g.Value})
}
|> Seq.concat

let csvRegex = "\"([\w\s:;~!@#$%\^&\*_<>,\.\\\/\|\[\]\{\}\(\)\-\+\?]*)(?:\",|\"$)"

let testLine = "\"31\",\"a 1\",\"b-2\",\"c+3\",\",.;~!@#$%^&*()\/?><,.|{}[]_+-\",\"\",\"14/05/2010 12:12:20 a.m.\",\"1: 2; 3. 4? 5[ 6] 7& 8*\",\"a,b\""

capturesSeq testLine csvRegex
|> Seq.iter (fun x -> printfn "%A" x)

Tuesday, May 25, 2010

Parsing CSV Escaped with Speech Marks

I just know this isn’t the right way to do this but what the heck it seems to work for me. 

The simple match is "([\w,]*)",+|"([\w,]*)"$ but I needed to account for all the other commonly occurring characters such as %&* etc. ( I would’ve thought I could just use .* but I can’t seem to get it to work.)

I’m using the F# Active Pattern approach to divvy up the matches – and returning the Match objects into the seq rather than breaking out the capture in the active pattern. This was more useful to me when I was using the matches later on in the code.

open System.Text.RegularExpressions

let (|ActiveRegex|_|) regex str =
let ms = Regex(regex).Matches(str)
if ms.Count > 0
then Some ([ for m in ms -> m ])
else None

let matches s re =
match s with
| ActiveRegex re results -> results
| _ -> []

let testLine = "\"31\",\"a 1\",\"b-2\",\"c+3\",\",.;~!@#$%^&*()\/?><,.|{}[]_+-\",\"\",\"14/05/2010 12:12:20 a.m.\",\"1: 2; 3. 4? 5[ 6] 7& 8*\",\"a,b\""

matches testLine "\"([\w\s:;~!@#$%\^&\*_<>,\.\\\/\|\[\]\{\}\(\)\-\+\?]*)\",+|\"([\w\s:;~!@#$%\^&\*_<>,\.\\\/\|\[\]\{\}\(\)\-\+\?]*)\"$"
let printMatches s p =
for m in matches s p do
seq{for g in m.Groups -> g}
|> Seq.skip 1
|> Seq.iter (fun x -> printfn "%A" x)

printMatches testLine "\"([\w\s:;~!@#$%\^&\*_<>,\.\\\/\|\[\]\{\}\(\)\-\+\?]*)\",+|\"([\w\s:;~!@#$%\^&\*_<>,\.\\\/\|\[\]\{\}\(\)\-\+\?]*)\"$"
  

Thursday, May 20, 2010

System Center Operations Manager Health Explorer Context

I’ve been wondering for a while why the Health Explorer doesn’t always display the context for a state change. I found one recent link through both Bing and Google that explains what’s going on: http://social.technet.microsoft.com/Forums/en-US/systemcenteressentials/thread/df725076-e307-4d63-9be9-5d875c6924b5/.

Basically, at Entity level if there are multiple sources for state change information then the UI doesn’t know how to represent that information so it just says “No context was available for this state change event” and it displays a bunch of state changes with no text… something like this:

image

What you need to do is drill down into Availability/Configuration/Performance etc and as you do you’ll get the state change context appearing.

Wednesday, May 19, 2010

Pascal’s Triangle in F#

Fun with F# :)

let rec PascalsTriangle = seq { 
yield [1];
for aLine in PascalsTriangle ->
List.append (1::Seq.toList (Seq.map (fun (x,y) -> x+y) (Seq.pairwise aLine))) ([1])
}



> PascalsTriangle |> Seq.take 10 |> Seq.toList;;
val it : int list list =
[[1]; [1; 1]; [1; 2; 1]; [1; 3; 3; 1]; [1; 4; 6; 4; 1]; [1; 5; 10; 10; 5; 1];
[1; 6; 15; 20; 15; 6; 1]; [1; 7; 21; 35; 35; 21; 7; 1];
[1; 8; 28; 56; 70; 56; 28; 8; 1]; [1; 9; 36; 84; 126; 126; 84; 36; 9; 1]]
>


 



I actually started this with a pipe forward approach, but ( at least with my knowledge of the syntax) it seemed longer to write down.



let rec PascalsTriangle1 = seq { 
yield [1];
for aLine in PascalsTriangle1 ->
let
newLine =
aLine
|> Seq.pairwise
|> Seq.map (fun (x,y) -> x+y)
|> Seq.toList
List.append (1::newLine) [1]
}

Friday, May 14, 2010

Discovering the presence of a database in a System Center Powershell script

I’m pretty sure there’s a better way to do this using the native SQL Server management pack but this worked for me (peppered with debugging statements). It looks for a database called staging and if it finds it, it returns the discovery information back.

      <Discovery ID="B.Staging.DiscoverProcessingComponent" Enabled="true" Target="B.Staging.ComputerRole" ConfirmDelivery="true" Remotable="true" Priority="Normal">
<
Category>Discovery</Category>
<
DiscoveryTypes>
<
DiscoveryClass TypeID="B.Staging.ProcessingComponent" />
</
DiscoveryTypes>
<
DataSource ID="PSScript" TypeID="Windows!Microsoft.Windows.TimedPowerShell.DiscoveryProvider">
<
IntervalSeconds>180</IntervalSeconds>
<
SyncTime />
<
ScriptName>DiscoverBStagingProcessingComponent.ps1</ScriptName>
<
ScriptBody><![CDATA[
param($sourceId, $managedEntityId, $computerName)

$api = New-Object -comObject 'MOM.ScriptAPI'
$api.LogScriptEvent("Processing discovery",101,2,"Created MOM.ScriptAPI with param sourceId = $sourceId , managedEntityId = $managedEntityId , computerName = $computerName ")

$discoveryData = $api.CreateDiscoveryData(0, $sourceId, $managedEntityId)
$api.LogScriptEvent("Processing discovery",101,2,"Executed CreateDiscoveryData")

[system.reflection.assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null

$s=new-object('Microsoft.SqlServer.Management.Smo.Server') $computerName
$dbs=$s.databases
$dbs | get-member -membertype property
$staging = ($dbs | select name | where {$_.name -eq 'staging'})."Name"
$api.LogScriptEvent("Processing discovery",101,2,"Found anything: $staging")

$instance = $discoveryData.CreateClassInstance("$MPElement[Name='B.Staging.ProcessingComponent']$")
$api.LogScriptEvent("Processing discovery",101,2,"Executed CreateClassInstance")

if ($staging -eq 'staging') {
$instance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $computerName)
$instance.AddProperty("$MPElement[Name='B.Staging.ProcessingComponent']/B.Staging.ProcessingComponentKey$", "staging")

$api.LogScriptEvent("Processing discovery",101,2,"In the staging branch")

} else {
$instance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", "")
$instance.AddProperty("$MPElement[Name='B.Staging.ProcessingComponent']/B.Staging.ProcessingComponentKey$", "")

$api.LogScriptEvent("Processing discovery",101,2,"In the not-staging branch")

}

$api.LogScriptEvent("Processing discovery",101,2,"About to AddInstance")

$discoveryData.AddInstance($instance)

$api.LogScriptEvent("Processing discovery",101,2,"About to return discoveryData")

$discoveryData

]]></ScriptBody>
<
Parameters>
<
Parameter>
<
Name>sourceID</Name>
<
Value>$MPElement$</Value>
</
Parameter>
<
Parameter>
<
Name>managedEntityID</Name>
<
Value>$Target/Id$</Value>
</
Parameter>
<
Parameter>
<
Name>computerName</Name>
<
Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
</
Parameter>
</
Parameters>
<
TimeoutSeconds>300</TimeoutSeconds>
</
DataSource>
</
Discovery>

Lessons in System Center Operations Manager Discovery

Some quick notes before I forget them.

General Hints

  • Run through the Management Pack Authoring Guide first. It’s useful but I feel it didn’t give me enough of an explanation of what’s actually going on under the covers. Much of the inner workings of system center remain a mystery to me.
  • Setup a single all in one dev environment to allow yourself the chance to really play around when you’re not sure what’s going on. Then load on an agent onto your normal desktop workstation/laptop and test discoveries out on that machine at the same time as you begin deploying your management packs into the production environment.
  • Get a copy of Savision Live Maps – a 5 diagram copy is freely available. It has a simple, easy to use interface for finding your class instances and testing out your discoveries. And, I’ve just discovered – there’s a new version out – v5 is RTM - gotta try it.

Registry Discovery

  • Use the filtered registry discovery not the unfiltered discovery. This ensures you can get the Build Event Expression page which enables you to test for the existence of a registry key.

WMI Discovery

  • Use wbemtest.exe to try out queries and drill down into the CIM classes.
  • Documentation to help you with WMI is again weak. Classic example – at time of writing this there’s a community comment to highlight the fact that the SQL Server 2008 ComputerManagement namespace is incorrectly labelled (should be root\Microsoft\SqlServer\ComputerManagement10 – you can verify by using select * from __namespace under root\Microsoft\SqlServer).

Powershell Script Discovery

Thursday, May 06, 2010

Adding SCOM Class Properties to the DGML

Updated transform for SCOM/OpsMgr management pack diagramming with DGML. This time I added in the SCOM class attributes as properties on DGML nodes – perhaps I should’ve used categories?? The legend in the visual studio viewer then lets you highlight parts of the class model based upon properties.

scr1

Next thing would be to include attributes about relationships as properties on the links. Also, what about referencing other system center management packs?

<?xmlversion="1.0" encoding="utf-8"?>
<
xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://schemas.microsoft.com/vs/2009/dgml" version="1.0">
    <
xsl:outputmethod="xml" indent="yes"/>
    <
xsl:templatematch="/">
        <
DirectedGraph>
            <
Nodes>
                <
xsl:apply-templatesselect="ManagementPack/TypeDefinitions/EntityTypes/ClassTypes/ClassType" />
            </
Nodes>
            <
Links>
                <
xsl:apply-templatesselect="ManagementPack/TypeDefinitions/EntityTypes/RelationshipTypes/RelationshipType" />
            </
Links>
            <
Categories>
            </
Categories>
            <
Properties>
                <
PropertyId="Accessibility" Label="Accessibility" Description="Accessibility" DataType="System.String" />
                <
PropertyId="Abstract" Label="Abstract" Description="Whether this is an abstract class" DataType="System.Boolean" />
                <
PropertyId="Base" Label="Base" Description="Whether this is a base class" DataType="System.String"/>
                <
PropertyId="Hosted" Label="Hosted" Description="Whether this is hosted" DataType="System.Boolean"/>
                <
PropertyId="Singleton" Label="Singleton" Description="Whether this is singleton" DataType="System.Boolean"/>
            </
Properties>
        </
DirectedGraph>
    </
xsl:template>
    <
xsl:templatematch="ManagementPack/TypeDefinitions/EntityTypes/ClassTypes/ClassType">
        <
xsl:elementname="Node">
            <
xsl:attributename="Id">
                <
xsl:value-ofselect="@ID"/>
            </
xsl:attribute>
            <
xsl:attributename="Accessibility">
                <
xsl:value-ofselect="@Accessibility"/>
            </
xsl:attribute>
            <
xsl:attributename="Abstract">
                <
xsl:value-ofselect="@Abstract"/>
            </
xsl:attribute>
            <
xsl:attributename="Base">
                <
xsl:value-ofselect="@Base"/>
            </
xsl:attribute>
            <
xsl:attributename="Hosted">
                <
xsl:value-ofselect="@Hosted"/>
            </
xsl:attribute>
            <
xsl:attributename="Singleton">
                <
xsl:value-ofselect="@Singleton"/>
            </
xsl:attribute>
        </
xsl:element>
    </
xsl:template>

    <
xsl:templatematch="ManagementPack/TypeDefinitions/EntityTypes/RelationshipTypes/RelationshipType">
        <
Link>
            <
xsl:attributename="Source">
                <
xsl:value-ofselect="Source"/>
            </
xsl:attribute>
            <
xsl:attributename="Target">
                <
xsl:value-ofselect="Target"/>
            </
xsl:attribute>
        </
Link>
    </
xsl:template>
</
xsl:stylesheet>

Monday, May 03, 2010

WMI Web Site Discovery with System Center Operations Manager

Learning how to perform a discovery with WMI under SCOM/OpsMgr isn’t easy – documentation is appalling and there’s few examples on the net. Here’s my, eventually, successful attempt with some lessons learned along the way.

First off a quick description of the original problem – I want to discover multiple instances of web services running on servers.

After much, much time spent building a single server SCOM/OpsMgr development environment (just exactly what do people call this product anyway?) and figuring my way tentatively through SCOM and the SCOM Authoring Console I defined a class based on Microsoft.Windows.LocalApplication. If you ever have any issues with this stuff a good place to go is the Operations Manager Authoring discussion board – one of my questions and the helpful response from Elizabeth 1978 is here.

clip_image002[17]

Then I created a discovery based upon a wmi query against the root\webadministration namespace to retrieve a list of sites. (Try it out in Powershell first: get-wmiobject –query “select * from site” –namespace root\webadministration.)

 clip_image002[19]

 

Now try to simulate this (learning to use the simulate tool as an experiment in itself – remember to load the MP into SCOM then click the arrow heads to attach to your development RMS so expressions can be resolved)… and it doesn’t work.

clip_image002[21]

clip_image002[23]

clip_image002[29]

Strange. Then by trial and error I find that the asterisk doesn’t allow SCOM to pick up the data element for the name from the WMI query. Basically it seems to need you to identify each value you want returned in the WMI query to be sure you can map it in the Authoring Console mapper screen.

So this works:

clip_image002[25]

You can check by running a simulation.

clip_image002[27]

And to be doubly sure I imported the management pack into the SCOM environment with a monitor that targeted the class and checked when the ASP.NET Apps v4.0.30319 Requests/Sec counter was in excess of 10 requests/second.

As a web service I used a wcf service – just a simple hello world affair (in F# – why is there no template in visual studio??) and executed from a powershell console.

1..1000 | foreach-object {(new-object system.net.webclient).downloadstring(“http://localhost/service.svc”) }