Why the fancy pants title? A Microsoft recruiter asked for my resume for some load testing related stuff and my blog is on my resume so I figured I needed some high falutin' titles. Hopefully they don't read anything but the titles. :-)
Anyway...
I'm getting to the point where I need to be able to invoke a load test after the nightly build and automagically run a load test. Now, I know, right now you're saying to yourself, "Hey, a nightly build doesn't constitute a continuous integration system!" And I agree, however, "Continuous Integration System" sounds much more impressive than just "nightly build." ;-)
Back to the important stuff: How am I going to invoke a Load Test on another machine after the nightly build is done? Right now I am working everything out with that super-duper handy utility, psexec from the UberBrains at SysInternals (now part of Microsoft).
I have recently build a stand alone server that I had installed SQL Server 2005, VS2008 TSTE and my custom LoadTestResults database where I store summary information from runs that I will be reporting from. On that server which I will refer to as my Load Test Controller, I setup a machine account user account by the name of TestRunner. During my trial and errors I found that I had to add TestRunner to the Administrators group but I need to go back and play around with that some to see if there is a more secure way to invoke the nightly automagic load test.
On the Load Test Controller I have two batch files: trigger.bat and loadtest.bat.
trigger.bat:
8<----------------------------
@echo off
echo Cry havoc and let slip loose the dogs of loadtest!
start C:\LoadTest\Trigger\loadtest.bat
---------------------------->8
The start command allows the async execution of the second batch file loadtest.bat, where the load test is really started.
loadtest.bat:
8<----------------------------
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"
cd "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\"
MSTest.exe /testcontainer:c:\LoadTest\LoadTests\MyLoadTest\MyLoadTest\MyReallyCoolLoadTest.loadtest
---------------------------->8
I can call trigger.bat from psexec with any automated process that can shell out to the command line or execute a batch file. Here is an example of starting a load test on the Load Test Controller using psexec from the command line:
8<----------------------------
C:\>psexec \\loadtestbox -u testrunner -p testrunner cmd /c c:\loadtest\trigger\trigger.bat
PsExec v1.92 - Execute processes remotely
Copyright (C) 2001-2007 Mark Russinovich
Sysinternals - www.sysinternals.com
Cry havoc and let slip loose the dogs of loadtest!
c:\loadtest\trigger\trigger.bat exited on loadtestbox with error code 0.
---------------------------->8
Even though psexec exit back to the command line, the load test is actually running on my Load Test Controller:
I was having problems getting results to be automagically saved to the Load Test Results Repository. I figured the SQL connection string was stored in the VS2008 solution but I had to open up VS2008 on my Load Test Controller machine and manually add the connection string for my TestRunner profile. After that, the LoadTest database is updated with each execution. w00t!
Now it's off to add the automagic reporting SP calls via osql.
Tuesday, September 30, 2008
Saturday, September 6, 2008
DHCP problems with Win2k8
Last weekend I brought home the Portable Server of Powah! home to do some work over the weekend and when I fired things up I wasn't getting an IP addressed assigned by the DHCP server on my FreeBSD box that is the gateway to the intarweb here at the house.
I thought that very odd and when I checked /var/log/dhcpd.log it appeared that a request was going out for an IP address and being sent back but was never accepted by Win2k8. How odd...
Here is what I saw in the logs:
8<---------------------------------
Sep 6 14:42:09 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:10 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:13 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:13 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:22 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:22 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:39 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:39 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
--------------------------------->8
There should have been an DHCPACK after the first two messages. Now this really confused me as I have had the Portable Server of Powah! home many times and had never had any problems getting an IP address before.
But, through playing around I found out that the wireless interface could get an IP address lickitysplit and being the slacker that I am, I kept on trucking with the wifi so that I could get my work done over the holiday.
I once again brought home the Portable Server of Powah! and once again, no IP address. This made me very sad as I wanted to have a wired connection to use the Gigabit goodness, that and it was just odd that I couldn't get an IP address.
I bowed down before the Great Google and queried it's great knowledge and came across this KB. Here is the problem in a nutshell: "Windows Vista cannot obtain an IP address from certain routers or from certain non-Microsoft DHCP servers"
If you follow the directions of the KB the problem can be fixed but they leave out a very important part of the puzzle. Basically, you drill down into the registry to where the various TCP/IP settings are stored and you have to find the GUID of the interface. Since you don't have "friendly" interface names like xl0, xl1 (FreeBSD interface drivers for 3Com NICs) or even friendlier Linux names like eth0, eth1, etc this becomes quite a chore.
Here is what I was looking at:
At this point I'm doing a Kelly-esque "What the hell?" Which one of these GUIDs represents my physical NIC and which one represents my virtual NIC that Hyper-V uses? Do I modify the entry for the physical NIC or the Hyper-V NIC? Confusion abounds!
I manually assigned an IP address and found the GUID entry that represented the Hyper-V virtual NIC and made the changes in the Registry that the above mentioned KB required.
I closed regedit, attempted to do a ipconfig /release && ipconfig /renew and watched the /var/log/dhcpd.log as Win2K8 tried to get an IP address with no love. Then I remembered, this is Windows...
I reboot the machine and sure enough, as it is firing up I see this entry come across /var/log/dhcpd.log:
8<-------------------------------
Sep 6 14:44:48 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:44:49 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:44:49 firewall dhcpd: DHCPREQUEST for 10.0.0.204 (10.0.0.1) from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:44:49 firewall dhcpd: DHCPACK on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
------------------------------->8
Hazzah! It works!
Apparently during an update something got tweaked and I could no longer pull an IP address from the FreeBSD dhcpd and had to apply this KB hack to get things to work. Or it could have been when I found how to get the Client for Microsoft Networks working with Hyper-V again. No matter, something got tweaked and now it is fixed which is good 'nuff for me. I'm easy like that.
I thought that very odd and when I checked /var/log/dhcpd.log it appeared that a request was going out for an IP address and being sent back but was never accepted by Win2k8. How odd...
Here is what I saw in the logs:
8<---------------------------------
Sep 6 14:42:09 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:10 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:13 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:13 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:22 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:22 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:39 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:42:39 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
--------------------------------->8
There should have been an DHCPACK after the first two messages. Now this really confused me as I have had the Portable Server of Powah! home many times and had never had any problems getting an IP address before.
But, through playing around I found out that the wireless interface could get an IP address lickitysplit and being the slacker that I am, I kept on trucking with the wifi so that I could get my work done over the holiday.
I once again brought home the Portable Server of Powah! and once again, no IP address. This made me very sad as I wanted to have a wired connection to use the Gigabit goodness, that and it was just odd that I couldn't get an IP address.
I bowed down before the Great Google and queried it's great knowledge and came across this KB. Here is the problem in a nutshell: "Windows Vista cannot obtain an IP address from certain routers or from certain non-Microsoft DHCP servers"
If you follow the directions of the KB the problem can be fixed but they leave out a very important part of the puzzle. Basically, you drill down into the registry to where the various TCP/IP settings are stored and you have to find the GUID of the interface. Since you don't have "friendly" interface names like xl0, xl1 (FreeBSD interface drivers for 3Com NICs) or even friendlier Linux names like eth0, eth1, etc this becomes quite a chore.
Here is what I was looking at:
At this point I'm doing a Kelly-esque "What the hell?" Which one of these GUIDs represents my physical NIC and which one represents my virtual NIC that Hyper-V uses? Do I modify the entry for the physical NIC or the Hyper-V NIC? Confusion abounds!
I manually assigned an IP address and found the GUID entry that represented the Hyper-V virtual NIC and made the changes in the Registry that the above mentioned KB required.
I closed regedit, attempted to do a ipconfig /release && ipconfig /renew and watched the /var/log/dhcpd.log as Win2K8 tried to get an IP address with no love. Then I remembered, this is Windows...
I reboot the machine and sure enough, as it is firing up I see this entry come across /var/log/dhcpd.log:
8<-------------------------------
Sep 6 14:44:48 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:44:49 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:44:49 firewall dhcpd: DHCPREQUEST for 10.0.0.204 (10.0.0.1) from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
Sep 6 14:44:49 firewall dhcpd: DHCPACK on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0
------------------------------->8
Hazzah! It works!
Apparently during an update something got tweaked and I could no longer pull an IP address from the FreeBSD dhcpd and had to apply this KB hack to get things to work. Or it could have been when I found how to get the Client for Microsoft Networks working with Hyper-V again. No matter, something got tweaked and now it is fixed which is good 'nuff for me. I'm easy like that.
Friday, September 5, 2008
Extracting Perfmon data from the VS2008 Load Test Repository
Ok. You are performing load tests with VS2008 TSTE and you've enabled the logging of perfmon metrics in your test by tweaking the "Timing Details Storage" property of the Run Settings in your load test. Every 5 seconds the perfmon metrics you specified are being stored to the load test repository and after the run you get some metrics. That's great and all but what if you want more detailed statistics such as average AND standard deviation.
Or better yet! You want at that data to generate graphs of the metrics over the duration of the test. Sure, you can do this by firing up good 'ol Perfmon and logging the values or you can just get them from your load test repository with some SQL. Why have Perfmon logging when VS2008 already does this for you?
I have a need for just such an action and I've been working on the SQL statements to extract the data I need. It's not complete, but this is a major step forward to allowing me to finish up the SPs I want to collect the data dynamically for consumption by managers and other QA folks.
Looking at the handy dandy schema of the load test repository (located here for all to gander) I have created some SQL statements that take care of the leg work for me.
In the above example, the reference to LoadTestRunId is hard coded to a previous run that I have in my results repository. For all my other SPs I use the GUID of the test and will convert the above SQL statement to reference GUID all in good time. The query above returns results like this:
The first column is the InstanceId of the perfmon counter and the information for the counter is split up into three separate columns (CategoryName, CounterName and InstanceName) but the column that ties them all together is the InstanceId.
Ok. That's fine and dandy but what about graphing the metrics over the duration of the load test?
Well, that's another SQL query I wrote that is in the alpha stage of development. In the above example I displayed 15 out of 466 metrics that were logged. Let's choose to graph the .NET CLR Memory Gen 2 heap size for w3wp. This particular metric happens to be InstanceId #3 this time around and we can use it in the SQL statement below to extract the data that can eventually be used to graph the results.
This query yields the results of:
and so on and so on until all 722 metrics are displayed. We can take those 722 metrics and copypasta into Excel and generate a quick and dirty graph:
Viola! Quick and dirty graph from Excel!
I'll need to change the column output to show more details on the metrics to display all three columns to better identify the perfmon metric and also display the time that the metric was taken.
After I do all that I'll need to write some code to generate graphs on the fly. I'm thinking that this project on SourceForge will fit the bill nicely and there seems to be a bunch of examples out on the 'net how to use ZedGraph to it's fullest.
Oh yeah! Almost forgot. I need to double check the results between the first query and the individual information for InstanceId 3 metric (the .NET CLR Gen 2 heap size for w3wp). The first query output says the average value is 197119053.001387. Take the average of output that I did the copypasta into Excel gonkulates to an average of 197119053.4. The difference between the two? A measly 0.355. I'm thinking that is good 'nuff for government work.
Now I just need to finish up the queries and get them into SPs and integrate them with the other code that I've written that does automagic before/after comparison of transaction response times (more on that at a later date).
Or better yet! You want at that data to generate graphs of the metrics over the duration of the test. Sure, you can do this by firing up good 'ol Perfmon and logging the values or you can just get them from your load test repository with some SQL. Why have Perfmon logging when VS2008 already does this for you?
I have a need for just such an action and I've been working on the SQL statements to extract the data I need. It's not complete, but this is a major step forward to allowing me to finish up the SPs I want to collect the data dynamically for consumption by managers and other QA folks.
Looking at the handy dandy schema of the load test repository (located here for all to gander) I have created some SQL statements that take care of the leg work for me.
1: SELECT LoadTestPerformanceCounterInstance.InstanceId,
2: LoadTestPerformanceCounterCategory.CategoryName,
3: LoadTestPerformanceCounter.CounterName,
4: LoadTestPerformanceCounterInstance.InstanceName, LoadTestPerformanceCounterCategory.MachineName,
5: AVG(LoadTestPerformanceCounterSample.ComputedValue) AS AverageValue,
6: stdev(LoadTestPerformanceCounterSample.ComputedValue) as StdDev,
7: count(LoadTestPerformanceCounterSample.ComputedValue) as Count
8: FROM LoadTestPerformanceCounterCategory INNER JOIN
9: LoadTestPerformanceCounter INNER JOIN
10: LoadTestPerformanceCounterInstance ON LoadTestPerformanceCounter.LoadTestRunId = LoadTestPerformanceCounterInstance.LoadTestRunId AND
11: LoadTestPerformanceCounter.CounterId = LoadTestPerformanceCounterInstance.CounterId ON
12: LoadTestPerformanceCounterCategory.CounterCategoryId = LoadTestPerformanceCounter.CounterCategoryId AND
13: LoadTestPerformanceCounterCategory.LoadTestRunId = LoadTestPerformanceCounter.LoadTestRunId INNER JOIN
14: LoadTestPerformanceCounterSample ON LoadTestPerformanceCounterInstance.LoadTestRunId = LoadTestPerformanceCounterSample.LoadTestRunId AND
15: LoadTestPerformanceCounterInstance.InstanceId = LoadTestPerformanceCounterSample.InstanceId
16: WHERE (LoadTestPerformanceCounter.LoadTestRunId = 83)
17: GROUP BY LoadTestPerformanceCounterCategory.CategoryName, LoadTestPerformanceCounter.CounterName, LoadTestPerformanceCounterInstance.InstanceName,
18: LoadTestPerformanceCounterInstance.InstanceId, LoadTestPerformanceCounterCategory.MachineName
19: ORDER BY LoadTestPerformanceCounterInstance.InstanceId
In the above example, the reference to LoadTestRunId is hard coded to a previous run that I have in my results repository. For all my other SPs I use the GUID of the test and will convert the above SQL statement to reference GUID all in good time. The query above returns results like this:
1: 0 .NET CLR Memory Gen 0 heap size w3wp 10.0.0.11 944824777.209431 185400315.203616 721
2: 1 .NET CLR Memory Large Object Heap size w3wp 10.0.0.11 86994000.5298197 29625345.9051784 721
3: 2 .NET CLR Memory # Gen 0 Collections w3wp 10.0.0.11 132.608876560333 69.5129454689118 721
4: 3 .NET CLR Memory Gen 2 heap size w3wp 10.0.0.11 197119053.001387 78824416.0154383 721
5: 4 .NET CLR Memory Allocated Bytes/sec w3wp 10.0.0.11 63521791.4160888 91335026.9218956 721
6: 5 .NET CLR Memory # Gen 2 Collections w3wp 10.0.0.11 29.5104022191401 12.1351287581502 721
7: 6 .NET CLR Memory Promoted Memory from Gen 0 w3wp 10.0.0.11 42540355.2815534 13356693.0175676 721
8: 7 .NET CLR Memory # Induced GC w3wp 10.0.0.11 0 0 721
9: 8 .NET CLR Memory Gen 0 Promoted Bytes/Sec w3wp 10.0.0.11 418557.084366331 1120132.98732717 721
10: 9 .NET CLR Memory Promoted Memory from Gen 1 w3wp 10.0.0.11 21740368.0721221 25126733.8606902 721
11: 10 .NET CLR Memory # GC Handles w3wp 10.0.0.11 3750.73370319001 216.191892870829 721
12: 11 .NET CLR Memory # Gen 1 Collections w3wp 10.0.0.11 70.0693481276005 35.4698332819129 721
13: 12 .NET CLR Memory Gen 1 heap size w3wp 10.0.0.11 62394980.9237171 31034211.4233198 721
14: 13 .NET CLR Memory Finalization Survivors w3wp 10.0.0.11 8217.1040221914 3024.02383909951 721
15: 14 .NET CLR Memory Promoted Finalization-Memory from Gen 0 w3wp 10.0.0.11 11415345.6768377 11434973.2174913 721
16: 15 .NET CLR Memory % Time in GC w3wp 10.0.0.11 3.48545611573799 6.73310861817717 721
The first column is the InstanceId of the perfmon counter and the information for the counter is split up into three separate columns (CategoryName, CounterName and InstanceName) but the column that ties them all together is the InstanceId.
Ok. That's fine and dandy but what about graphing the metrics over the duration of the load test?
Well, that's another SQL query I wrote that is in the alpha stage of development. In the above example I displayed 15 out of 466 metrics that were logged. Let's choose to graph the .NET CLR Memory Gen 2 heap size for w3wp. This particular metric happens to be InstanceId #3 this time around and we can use it in the SQL statement below to extract the data that can eventually be used to graph the results.
1: SELECT LoadTestPerformanceCounterInstance.InstanceName,
2: LoadTestPerformanceCounterSample.ComputedValue
3: FROM LoadTestPerformanceCounterInstance INNER JOIN
4: LoadTestPerformanceCounterSample ON LoadTestPerformanceCounterInstance.LoadTestRunId = LoadTestPerformanceCounterSample.LoadTestRunId AND
5: LoadTestPerformanceCounterInstance.InstanceId = LoadTestPerformanceCounterSample.InstanceId
6: WHERE (LoadTestPerformanceCounterInstance.LoadTestRunId = 83) AND (LoadTestPerformanceCounterInstance.InstanceId = 3)
7: ORDER BY LoadTestPerformanceCounterSample.TestRunIntervalId
This query yields the results of:
1: w3wp 0
2: w3wp 0
3: w3wp 96
4: w3wp 3231264
5: w3wp 2.170597E+07
6: w3wp 2.170597E+07
7: w3wp 2.170597E+07
8: w3wp 2.976022E+07
9: w3wp 2.976022E+07
10: w3wp 2.976022E+07
11: w3wp 2.976022E+07
12: w3wp 4.127677E+07
13: w3wp 4.127677E+07
14: w3wp 4.127677E+07
and so on and so on until all 722 metrics are displayed. We can take those 722 metrics and copypasta into Excel and generate a quick and dirty graph:
Viola! Quick and dirty graph from Excel!
I'll need to change the column output to show more details on the metrics to display all three columns to better identify the perfmon metric and also display the time that the metric was taken.
After I do all that I'll need to write some code to generate graphs on the fly. I'm thinking that this project on SourceForge will fit the bill nicely and there seems to be a bunch of examples out on the 'net how to use ZedGraph to it's fullest.
Oh yeah! Almost forgot. I need to double check the results between the first query and the individual information for InstanceId 3 metric (the .NET CLR Gen 2 heap size for w3wp). The first query output says the average value is 197119053.001387. Take the average of output that I did the copypasta into Excel gonkulates to an average of 197119053.4. The difference between the two? A measly 0.355. I'm thinking that is good 'nuff for government work.
Now I just need to finish up the queries and get them into SPs and integrate them with the other code that I've written that does automagic before/after comparison of transaction response times (more on that at a later date).
Tuesday, September 2, 2008
Using NAnt custom task to wrap SDC Task to create VDir
I got my VDir code working, but there are some caveats that have to be watched, I've found.
A caveat that I've found though is that even though the invoked SDC Task might run into a problem during the .Execute call, .Execute still returns a true so my NAnt task doesn't know if the process has ran into a problem.
In the example below, I'm trying to create a VDir with an AppPool that doesn't exist:
8<-------------------------------------------
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/customtask/default.build
Target framework: Microsoft .NET Framework 2.0
Target(s) specified: test-task
[loadtasks] Scanning assembly "Auswipe.NAntTasks.VDirTasks" for extensions.
[loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions.
test-task:
[echo] Creating VDir
Auswipe.NAntTasks.VDirTasks : The following VDir will be created: 'someVDir'
Creating virtual directory "someVDir".
A task error has occured.
Message = Exception has been thrown by the target of an invocation.
VirtualDirectoryName = someVDir
Path = c:\somewebsite
MachineName = Auswipel-rfs2we
WebSiteName = Default Web Site
WebAppName =
AppPoolId = AppPoolDoesNotExist
AppCreate = True
AnonymousUserName =
AnonymousUserPassword =
UncUserName =
UncPassword =
AuthFlags =
AccessFlags =
at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)
at Microsoft.Sdc.Tasks.Configuration.Web.VirtualDirectory.AppCreate3() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Configuration\Web\VirtualDirectory.cs:line 324
at Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory.InternalExecute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Web\WebSite\CreateVirtualDirectory.cs:line 315
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 66
Element not found. (Exception from HRESULT: 0x80070490)
PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String message, Object[] messageArgs)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 95
Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:
System.ApplicationException: PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace, Boolean showDetail, String file)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 99
at Auswipe.NAntTasks.VDirTasks.CreateVDir.ExecuteTask()
BUILD SUCCEEDED - 2 non-fatal error(s), 0 warning(s)
Total time: 0.2 seconds.
------------------------------------------->8
You would think that .Execute should return a False for the call but it doesn't. Notice the verbage of "2 non-fatal error(s)" so it appears that the SDC Task does not treat the non-existant Applicaiton Pool as a fatal error. Good to know!
Going back and looking at the IIS Manager we see this for the create VDir:
Notice how the Application Pool is set to "<Invalid Application Pool>."
So, the VDir was created, despite the lack of a valid Application Pool. That's something to be aware of when testing out NAnt tasks that wrap SDC Task functionality.
1: using System;
2: using System.Text;
3: using NAnt.Core;
4: using NAnt.Core.Attributes;
5: using Microsoft.Sdc.Tasks;
6: using Microsoft.Build.Utilities;
7: using Microsoft.Build.Framework;
8: namespace Auswipe.NAntTasks.VDirTasks {
9: # region Delete VDir
10: [TaskName("deletevdir")]
11: public class DeleteVDir : NAnt.Core.Task {
12: private string vDirToDelete;
13: private string machineName;
14: private string webSiteName;
15: [TaskAttribute("vdirtodelete", Required = true)]
16: [StringValidator(AllowEmpty = false)]
17: public string VDirToDelete {
18: get { return vDirToDelete; }
19: set { vDirToDelete = value; }
20: }
21: [TaskAttribute("machinename", Required = true)]
22: [StringValidator(AllowEmpty = false)]
23: public string MachineName {
24: get { return machineName; }
25: set { machineName = value; }
26: }
27: [TaskAttribute("websitename", Required = true)]
28: [StringValidator(AllowEmpty = false)]
29: public string WebSiteName {
30: get { return webSiteName; }
31: set { webSiteName = value; }
32: }
33: protected override void ExecuteTask() {
34: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The following AppPool will be deleted: '" + vDirToDelete + "'");
35: try {
36: Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory delVDirObject = new Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory();
37: delVDirObject.MachineName = machineName;
38: delVDirObject.VirtualDirectoryName = vDirToDelete;
39: delVDirObject.WebSiteName = webSiteName;
40: if (delVDirObject.Execute()) {
41: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The VDir '" + vDirToDelete + "' was successful deleted.");
42: } else {
43: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : ERROR: The VDir '" + vDirToDelete + "' was NOT deleted but an exception was not thrown.");
44: };
45: } catch (Exception e) {
46: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : VDir deletion failed with the resulting exception:");
47: Project.Log(Level.Error, e.ToString());
48: };
49: }
50: }
51: #endregion
52: # region Create VDir
53: [TaskName("createvdir")]
54: public class CreateVDir : NAnt.Core.Task {
55: private string vDirName;
56: private string webSiteName;
57: private string appPoolName;
58: private string machineName;
59: private string fullPath;
60: [TaskAttribute("apppoolname", Required = true)]
61: [StringValidator(AllowEmpty = false)]
62: public string AppPoolName {
63: get { return appPoolName; }
64: set { appPoolName = value; }
65: }
66: [TaskAttribute("vdirname", Required = true)]
67: [StringValidator(AllowEmpty = false)]
68: public string VDirName {
69: get { return vDirName; }
70: set { vDirName = value; }
71: }
72: [TaskAttribute("website", Required = true)]
73: [StringValidator(AllowEmpty = false)]
74: public string WebSite {
75: get { return webSiteName; }
76: set { webSiteName = value; }
77: }
78: [TaskAttribute("machinename", Required = true)]
79: [StringValidator(AllowEmpty = false)]
80: public string MachineName {
81: get { return machineName; }
82: set { machineName = value; }
83: }
84: [TaskAttribute("path", Required = true)]
85: [StringValidator(AllowEmpty = false)]
86: public string FullPath {
87: get { return fullPath; }
88: set { fullPath = value; }
89: }
90: protected override void ExecuteTask() {
91: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The following VDir will be created: '" + vDirName + "'");
92: try {
93: Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory createVDirObject = new Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory();
94: createVDirObject.AppCreate = true;
95: createVDirObject.AppPoolId = appPoolName;
96: createVDirObject.MachineName = machineName;
97: createVDirObject.VirtualDirectoryName = vDirName;
98: createVDirObject.WebSiteName = webSiteName;
99: createVDirObject.Path = fullPath;
100: if (createVDirObject.Execute()) {
101: Project.Log(Level.Info, "Auswipe.NAntTasks.VDirTasks : The VDir '" + vDirName + "' was successfuly created.");
102: } else {
103: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : ERROR: The VDir '" + vDirName + "' was NOT created but an exception was not thrown.");
104: };
105: } catch (Exception e) {
106: Project.Log(Level.Error, "Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:");
107: Project.Log(Level.Error, e.ToString());
108: };
109: }
110: }
111: #endregion
112: }
A caveat that I've found though is that even though the invoked SDC Task might run into a problem during the .Execute call, .Execute still returns a true so my NAnt task doesn't know if the process has ran into a problem.
In the example below, I'm trying to create a VDir with an AppPool that doesn't exist:
8<-------------------------------------------
NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)
Copyright (C) 2001-2006 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/customtask/default.build
Target framework: Microsoft .NET Framework 2.0
Target(s) specified: test-task
[loadtasks] Scanning assembly "Auswipe.NAntTasks.VDirTasks" for extensions.
[loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions.
test-task:
[echo] Creating VDir
Auswipe.NAntTasks.VDirTasks : The following VDir will be created: 'someVDir'
Creating virtual directory "someVDir".
A task error has occured.
Message = Exception has been thrown by the target of an invocation.
VirtualDirectoryName = someVDir
Path = c:\somewebsite
MachineName = Auswipel-rfs2we
WebSiteName = Default Web Site
WebAppName =
AppPoolId = AppPoolDoesNotExist
AppCreate = True
AnonymousUserName =
AnonymousUserPassword =
UncUserName =
UncPassword =
AuthFlags =
AccessFlags =
at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)
at Microsoft.Sdc.Tasks.Configuration.Web.VirtualDirectory.AppCreate3() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Configuration\Web\VirtualDirectory.cs:line 324
at Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory.InternalExecute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Web\WebSite\CreateVirtualDirectory.cs:line 315
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 66
Element not found. (Exception from HRESULT: 0x80070490)
PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String message, Object[] messageArgs)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 95
Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:
System.ApplicationException: PseudoEngineException
at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77
at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace, Boolean showDetail, String file)
at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace)
at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 99
at Auswipe.NAntTasks.VDirTasks.CreateVDir.ExecuteTask()
BUILD SUCCEEDED - 2 non-fatal error(s), 0 warning(s)
Total time: 0.2 seconds.
------------------------------------------->8
You would think that .Execute should return a False for the call but it doesn't. Notice the verbage of "2 non-fatal error(s)" so it appears that the SDC Task does not treat the non-existant Applicaiton Pool as a fatal error. Good to know!
Going back and looking at the IIS Manager we see this for the create VDir:
Notice how the Application Pool is set to "<Invalid Application Pool>."
So, the VDir was created, despite the lack of a valid Application Pool. That's something to be aware of when testing out NAnt tasks that wrap SDC Task functionality.
Subscribe to:
Posts (Atom)