IIS snapin extensibility. Part 4.

Making super types

Today I will show you how to extend existing namespace types with your data. There are at least half a dozen ways to do this. PowerShell Extended Type System could be used to add extra properties into any type as NoteProperty, ScriptProperty or  CodeProperty. Easiest way to use this it to extend definition of type in the custom file 'types.ps1xml' or 'format.ps1xml'. Let's do this for types definition file. If you add property into format definition file, it will be added only to output, but not to the object. You could get more information about extending existing file if you type in PowerShell "help about_Types".

Open file 'IIsProvider.types.ps1xml' and find type definition for the site. We have two of them, one for objects produced by provider, another one -- for objects, produced from cmdlets. You need type marked as 'Microsoft.IIs.PowerShell.Framework.Site#site'. Type name in PowerShell ETS don't need to be exactly the same as in CLR, it should be unique string, we use markers after '#' to distinguish between types that are backed by the same generic CLR type.

Copy this definition into a separate file and remove everything between 'Members' tags, I will use name 'SuperSite.types.ps1xml'. Don't forget about XML header and common container. You should have this:

<?xml version="1.0" encoding="utf-8" ?>
<Types>
<Type>
    <Name>Microsoft.IIs.PowerShell.Framework.Site#site</Name>
    <Members>
    </Members>
  </Type>
</Types>

Let's add to site perf counter for total number of files this site sent since last service reset. This data is not part of configuration, therefore cmdlets from our snap-in are no help. We have use script, to obtain this counter. This script is as simple as

$filesCounter = new-object System.Diagnostics.PerformanceCounter `
    "Web Service", "Total Files Sent", "Default Web Site"

I will use existing file name to see, how it works. To see the value, just print this variable:

>$filesCounter
...
MachineName : .
RawValue : 3
...

To make sure it works, let's get one file and check counter again. I happen to have file 'smiley.gif' in this site, let's send request to the site for this file:

>get-weburl 'IIS:\Sites\Default Web Site\smiley.gif'

ResponseUri                 Status Description
-----------                 ------ -----------
http://localhost/smiley.gif OK     OK

>$filesCounter

...
RawValue : 4
...

OK, it works. Now, let's place this script into the type definition. We will define this property as read-only ScriptProperty and add it between  'Members' tags:

    <Members>
      <ScriptProperty>
        <Name>FilesSent</Name>
        <GetScriptBlock>
          $filesCounter = new-object System.Diagnostics.PerformanceCounter `
            "Web Service", "Total Files Sent", $this.Name
          $filesCounter.RawValue
        </GetScriptBlock>
      </ScriptProperty>
   <Members>

Inside script property we use variable $this that refers to the instance of type that PowerShell is processing -- we need name of this site to get counter. Now let's see it in action. To add new type definition into PowerShell, update types:

> Update-TypeData -prependpath .\SuperSite.types.ps1xml

This command loads our file and then reloads all type definitions, so our property will not be overwritten. To see our new super site, run 'get-item' | format-list *:

> get-item 'IIS:\Sites\Default Web Site' | format-list *
...
PSIsContainer : True
FilesSent : 4
name : Default Web Site
id : 1
...

It works!

Another way of extending namespace types is to add properties, defined as XPath queries, into type definition in namespace layout file. I already wrote about format of this file in earlier post, you could use that post as a reference. You could take a look, how 'application' type is defined there, to get an idea, where to put the query.

Both above methods of extending types are very flexible, but also quite expensive. Using script property means that every time this property is accessed on any instance of this type, PowerShell will execute the script block that you will place there. Using XPath queries is faster, but those queries will also be executed each time when namespace object is getting created. Before we released our snapin, I did a round of performance optimization. I found that having anything defined on site object beyond what is provided by managed type kills performance to orders of magnitude. In IIS we have very strict performance criteria for number of sites that could be available on one server computer. We require that all tools will be able to work on server with several thousand sites without significant degradation in performance and response time. To meet these requirements I first of all removed all properties, defined for site type in layout file and in types.ps1xml. That gave me biggest gain. Then I applied other types of optimization, that would be useless without cutting off all all extra calls to PowerShell and XPath engine. Interesting, that next biggest performance boost came from minimization of data marshalling between managed and native code. Only after that I started code and algorithm optimization.

 

Enjoy,

Sergei

No Comments