PowerShell Remoting Project Home

Tuesday, July 18, 2006

Runspace Remoting

Scott Hanselman is one of the early users and supporters of my PowerShell Remoting. He did an interesting post about Runspace Remoting. Briefly, he used export-clixml to serialize objects at server and used import-clixml to reconstruct PSObject at client. I have tried similar mechanism and failed. Actually Karl Prosser and I have been talking about using export-clixml/import-clixml a while ago . Great job, Scott!

Despite of my failure on serialization/de-serialization of PSObject, there is other reasons made me choose user interface remoting:
  1. Monad was designed to capable of user interface remoting. Monad engine will manage to display and format (out-default) remote objects. Monad engine can even truncate objects collection for you if there are too many objects in the pipline.
  2. Not all tasks require local objects exactly matching remote objects. For most remote scripting tasks, user interface remoting is good enough. User interface remoting deliver similar user experience as local shell. SSHD already proved itself successful story by similar mechanism. 
  3. User can manipulate "Real" remote objects via user interface remoting (No "Heisenberg Uncertainly Principle of PowerShell").
  4. Serialization/de-serialization method has its own limitations.
    • It can use a lot of resources and band-width. Consider the situation when multiple runspace ouput huge collection of objects at the same time, even with proper threading management server could still be overloaded.
    • What if you just need one simple property of an object with hundreds or thounds of properties, you would have to serialize all public properties of that object and transfer it to client.
    • It can loose some property associate with original objects.
  5. "Clustering" multiple servers is possible with user interface remoting. I am trying to "fork" single client input to multiple servers by simply add current client component to server component. (There are still some problems with this model)

PS: To Monad Team, a public helper API to serialize PSObject to string will be really helpful.

Have Fun

Tags:       



Thursday, July 13, 2006

How to: Using PowerShell through SSH

It is possible to use PowerShell through SSH. Read the story from The Hive Archive.

But there are certain limitations:
1. You have to install cygwin, sshd and ssh client like putty (Well I can certainly live with that. Actually they are always on my hard drive).
2. No prompt
3. some Raw UI might not work

I am really looking forward to the new version of PowerShell with buildin Remoting function. According to Jeffrey Snover : Our plans for remoting are to leverage WS-MGMT the remoting protocol recently standardized through the DMTF.

Have Fun

Tags:       



Wednesday, July 12, 2006

Why There Is an Out-Default Cmdlet

PowerShell have a couple of output control cmdlets:
> gcm out-*

CommandType     Name                            Definition
-----------     ----                            ----------
Cmdlet          Out-Default                     Out-Default [-InputObject <P...
Cmdlet          Out-File                        Out-File [-FilePath] <String...
Cmdlet          Out-Host                        Out-Host [-Paging] [-InputOb...
Cmdlet          Out-Null                        Out-Null [-InputObject <PSOb...
Cmdlet          Out-Printer                     Out-Printer [[-Name] <String...
Cmdlet          Out-String                      Out-String [-Stream] [-Width...
Out-Default cmdlet is one of the most mysterious one.
> help out-default

NAME
    Out-Default

SYNOPSIS
    The default controller of output.

DETAILED DESCRIPTION
    The standard treatment at the end of a pipeline is to send all objects to o
    ut-default.  Out-default then sends them all to format-default. It takes th
    e objects that return and sends them to the default destination.  For this
    reason, it is functionally equivalent to out-host but is not called from th
e console.
The help message does not help at all. Luckily enough, Jeffrey Snover has a blog entry talking about this cmdlet.  In summary, It will "figure out how to format and output the object stream" and send them to host via Out-host cmdlet.

Here is my two cents:
1. Out-Default cmdlet is NOT for interactive console User.
Every interactive command from console will have Out-Default appended automatically by Monad engine. Add Out-Default in the middle of pipline could even cause unexpected output.
> gps | format-table
> gps | out-default | format-table      # format-table will not work.
2. Out-Default cmdlet is for Monad hosting application.
To have a full-blown Monad hosting application, you have to create your own "Host" (System.Management.Automation.Host.PSHost) which implement interface to process output (If you don't have your PSHost output interface implemented, your will get an exception when Out-default finally called out-host.). Every interactive command from user input should have Out-Default appended. Then the output of user command were assessed by out-default, formatted by format-* cmdlet, and eventually sent to user interface by out-host cmdlet.
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;

namespace HostingExample
{
    class TestHost
    {
        static void Main(string[] args)
        {
            string command = "gps";
            PSHost myhost = new MyHost();
            Runspace myRunspace = RunspaceFactory.CreateRunspace(myhost);
            myRunspace.Open();
            Pipeline pipeline1 = myRunspace.CreatePipeline(command, true);
            pipeline1.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
            pipeline1.Commands.Add("out-default");
            Pipeline1.Invoke();
        }
    }

    Class MyHost : PSHost
    {
            // MyHost class should be derived from PSHost abstract class.
    }
}
Although you don't have to (and probably should not) type "Out-Default" at the end of your command, it is always there working for you. Given this reason, Out-Default cmdlet is registered and loaded as default cmdlets.

You see it is there, you do not use it, but it is definitely important.

Tags:       



Sunday, July 02, 2006

Naive and Generic Object Collections in Powershell

I believed that you must have dealt with collections in Powershell Scriting. PowerShell has a nice way to support naive object array (object[]):

>$a = 1,2,3   #comma were interpreted as object[]
>$a.gettype().AssemblyQualifiedName
System.Object[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

>$a= 1..10   # .. were interpreted as continuous filled object[]
>$a.gettype().AssemblyQualifiedName
System.Object[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
For naive object ArrayList/stack/Queue, you have to use new-object cmdlet
> $a = new-object System.Collections.Stack
> $a.GetType().AssemblyQualifiedName
System.Collections.Stack, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKey
Token=b77a5c561934e089
> $a.Push($([System.Net.IPAddress] "192.168.0.1"))
Most of time, you are fine with naive object model because PowerShell will do the type conversion for you.
>$a= 1..10
>$a |gm
   TypeName: System.Int32

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     System.Int32 CompareTo(Int32 value), System.Int32 Com...
Equals      Method     System.Boolean Equals(Object obj), System.Boolean Equ...
GetHashCode Method     System.Int32 GetHashCode()
GetType     Method     System.Type GetType()
GetTypeCode Method     System.TypeCode GetTypeCode()
ToString    Method     System.String ToString(), System.String ToString(IFor...
But sometimes it is not good enough. You probably want to use generic object model.

For generic array (like System.Net.IPAddress[]), it requires a conversion:
>$a = ($([System.Net.IPAddress] "192.168.0.1"),$([System.Net.IPAddress] "127.0.0.1") )
>$a.gettype().AssemblyQualifiedName
System.Object[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
>$b= [System.Net.IPAddress[]] $a
> $b.GetType().AssemblyQualifiedName
System.Net.IPAddress[], System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
For generic collection (like System.Collections.ObjectModel.Collection<System.Net.IPAddress>), it becomes a little nasty (it requires Assembly Qualified Name) :
>$a = New-Object System.Collections.ObjectModel.Collection"`1[[System.Net.IPAddress, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
> $a.GetType().AssemblyQualifiedName
System.Collections.ObjectModel.Collection`1[[System.Net.IPAddress, System, Vers
ion=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Vers
ion=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
> $a.Add($([System.Net.IPAddress] "192.168.0.1"))
What about generic Stack?
> $a = New-Object System.Collections.Generic.Stack"`1[[System.Net.IPAddres
s, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
> $a.GetType().AssemblyQualifiedName
System.Collections.Generic.Stack`1[[System.Net.IPAddress, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
> $a.Push($([System.Net.IPAddress] "192.168.0.1"))
> $a.pop()
IPAddressToString : 192.168.0.1
Address           : 16820416
AddressFamily     : InterNetwork
ScopeId           :
IsIPv6Multicast   : False
IsIPv6LinkLocal   : False
IsIPv6SiteLocal   : False

Have Fun

Tags: