Load Testing with Agents running on Windows Azure – part 2


** Important note – Please read before reading further **

The content in this post was first written in 2011, exploring the earlier versions of Windows Azure. Since then, Windows Azure has evolved quite a lot, as well as other services in the Microsoft portfolio.

We now have an official solution to run load tests on Azure - load testing is part of Team Foundation Service and a preview is available. If you haven't tried it already, I'd encourage you to try it.

Find out more at https://aka.ms/loadtfs.

 


If you’ve read the last post, you know by now that we’re building a Load Test rig hosted on Windows Azure using Visual Studio 2010. Let’s take a look at our 2 different components.

Controller

For the Controller machine, you need Visual Studio 2010 and the Visual Studio 2010 agents, so that you can install the Controller agent.

You should create a user account vstestagent that has the same password as the vstestagent user created in the setup.cmd on the Agent machines. This is required because the machines are not domain-joined but must authenticate with each other.

You must also install the Endpoint software for Windows Azure connect. You can do this from the Management Portal by clicking “Install Local Endpoint” on the ribbon.

 

AzureLoadTesting9

 

There’s really not more you need to do or install on this machine.

Agent

My immediate idea was to build a custom VM for this role, but a colleague pointed me to right direction and suggested using worker roles and some startup tasks to deploy the needed software.

I started by creating a Cloud project in Visual Studio and adding a Worker Role. The code for the worker role is rather basic: in fact, the worker role does absolutely nothing because all of the work is done in the startup tasks.

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Diagnostics;
  4: using System.Linq;
  5: using System.Net;
  6: using System.Threading;
  7: using Microsoft.WindowsAzure;
  8: using Microsoft.WindowsAzure.Diagnostics;
  9: using Microsoft.WindowsAzure.ServiceRuntime;
  10: using Microsoft.WindowsAzure.StorageClient;
  11:  
  12: namespace WorkerRole1
  13: {
  14:     public class WorkerRole : RoleEntryPoint
  15:     {
  16:         public override void Run()
  17:         {
  18:             // This is a sample worker implementation. Replace with your logic.
  19:             Trace.WriteLine("WorkerRole1 entry point called", "Information");
  20:  
  21:             while (true)
  22:             {
  23:                 Thread.Sleep(10000);
  24:                 Trace.WriteLine("Working", "Information");
  25:             }
  26:         }
  27:  
  28:         public override bool OnStart()
  29:         {
  30:             // Set the maximum number of concurrent connections 
  31:             ServicePointManager.DefaultConnectionLimit = 12;
  32:  
  33:             // For information on handling configuration changes
  34:             // see the MSDN topic at https://go.microsoft.com/fwlink/?LinkId=166357.
  35:  
  36:             return base.OnStart();
  37:         }
  38:     }
  39: }

The service definition includes the Worker Role, loads required modules, including Connect, and defines the startup tasks that must run during startup.

  1: <?xml version="1.0" encoding="utf-8"?>
  2: <ServiceDefinition name="TestAgent" xmlns="https://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  3:   <WorkerRole name="WorkerRole1" vmsize="Medium">
  4:     <Imports>
  5:       <Import moduleName="Diagnostics" />
  6:       <Import moduleName="Connect" />
  7:       <Import moduleName="RemoteAccess" />
  8:       <Import moduleName="RemoteForwarder" />
  9:     </Imports>
  10:     <Startup>
  11:       <Task commandLine="setup.cmd" executionContext="elevated" taskType="simple" />
  12:       <Task commandLine="setupfw.cmd" executionContext="elevated" taskType="simple" />
  13:     </Startup>
  14:   </WorkerRole>
  15: </ServiceDefinition>

The 2 tasks declared will be responsible for: 1) configuring the software and 2) configure the firewall rules required for the Controller-Agent connection.

To configure an Agent, the following tasks will have to performed:

AzureLoadTesting2_thumb1

So, what do the scripts look like? Let’s start by looking at setup.cmd, which is more or less self explaining:

  1: powershell -command "set-executionpolicy Unrestricted"
  2: powershell -command ".\setup.ps1" -NonInteractive >; out.txt
  3:  
  4: unzip -q -o "c:\Resources\temp\VSAgent\VSAgent.zip" -d "c:\Resources\temp\vsagentsetup"
  5: c:\Resources\temp\vsagentsetup\testagent\setup.exe /q /f /norestart /unattendfile e:\approot\setupagent.ini
  6:  
  7: net user vstestagent yourpasswordgoeshere /add
  8: net localgroup Administrators vstestagent /add
  9:  
  10: REM Create a task that will run with full network privileges.
  11: net start Schedule
  12:  
  13: schtasks /CREATE /TN "Configure Test Agent Service" /SC ONCE /SD 01/01/2020 /ST 00:00:00 /RL HIGHEST /RU admin /RP youradminpasswordgoeshere /TR e:\approot\configagent.cmd /F
  14:  
  15: schtasks /RUN /TN "Configure Test Agent Service"

The only tricky bit in the script above is the fact that you need to configure a scheduled task that runs immediately to actually run a command line using credentials other than the system account.

The setupfw.cmd script is nothing more than a set of commands to open the common RPC/WMI/etc ports, plus the Agent Service default port on port 6901. Note that I tested this configuration only and I was not concerned about creating a least privilege or closed setup. This may need some tuning to be more secure.

  1: netsh advfirewall firewall add rule name="RPC" dir=in action=allow service=any enable=yes profile=any localport=rpc protocol=tcp
  2: netsh advfirewall firewall add rule name="RPC-EP" dir=in action=allow service=any enable=yes profile=any localport=rpc-epmap protocol=tcp
  3:  
  4: netsh advfirewall firewall add rule name="Port 135 TCP" dir=in action=allow service=any enable=yes profile=any localport=135 protocol=tcp
  5: netsh advfirewall firewall add rule name="Port 135 UDP" dir=in action=allow service=any enable=yes profile=any localport=135 protocol=udp
  6: netsh advfirewall firewall add rule name="Port 136 TCP" dir=in action=allow service=any enable=yes profile=any localport=136 protocol=tcp
  7: netsh advfirewall firewall add rule name="Port 136 UDP" dir=in action=allow service=any enable=yes profile=any localport=136 protocol=udp
  8: netsh advfirewall firewall add rule name="Port 137 TCP" dir=in action=allow service=any enable=yes profile=any localport=137 protocol=tcp
  9: netsh advfirewall firewall add rule name="Port 137 UDP" dir=in action=allow service=any enable=yes profile=any localport=137 protocol=udp
  10: netsh advfirewall firewall add rule name="Port 138 TCP" dir=in action=allow service=any enable=yes profile=any localport=138 protocol=tcp
  11: netsh advfirewall firewall add rule name="Port 138 UDP" dir=in action=allow service=any enable=yes profile=any localport=138 protocol=udp
  12: netsh advfirewall firewall add rule name="Port 139 TCP" dir=in action=allow service=any enable=yes profile=any localport=139 protocol=tcp
  13: netsh advfirewall firewall add rule name="Port 139 UDP" dir=in action=allow service=any enable=yes profile=any localport=139 protocol=udp
  14:  
  15: netsh advfirewall firewall add rule name="Port 445 TCP" dir=in action=allow service=any enable=yes profile=any localport=445 protocol=tcp
  16: netsh advfirewall firewall add rule name="Port 445 UDP" dir=in action=allow service=any enable=yes profile=any localport=445 protocol=udp
  17:  
  18: netsh advfirewall firewall add rule name="Port 6901 TCP" dir=in action=allow service=any enable=yes profile=any localport=6901 protocol=tcp
  19: netsh advfirewall firewall add rule name="Port 6901 UDP" dir=in action=allow service=any enable=yes profile=any localport=6901 protocol=udp
  20:  
  21: netsh advfirewall firewall add rule name="Port 6910 TCP" dir=in action=allow service=any enable=yes profile=any localport=6910 protocol=tcp
  22: netsh advfirewall firewall add rule name="Port 6910 UDP" dir=in action=allow service=any enable=yes profile=any localport=6910 protocol=udp
  23:  

Now that the scripts are ready, how is this deployed to Windows Azure? Let’s talk about that in the next part of the post.