Scripting a PVS Capacity on Demand Model with XenApp 6.5 Part 2

See… I wasn’t lying when I said it was coming 🙂  For this post (and the next couple) I’m letting my friend and former colleague Shane O’Neill provide most of the content.  He did quite a bit of the heavy lifting to make my vision a reality and hopefully we will have a chance to present a lot of this at Synergy 2015 as well.  So without further ado, here’s Part II.

Hi everyone! My name is Shane O’Neill and I am a Senior Engineer on a Citrix Engineering team for a major insurance company. I came to the world of virtualization with a background in software and immediately began to focus on opportunities to use automation to improve processes and increase reporting capabilities. I’d like to thank Paul for the invitation to guest here. I hope that those of you reading this find it useful.

I’ll be breaking this topic into 3 different parts; analysing PVS usage, adding a server to a collection and removing a server from a collection. I code mainly in C#, running any required PowerShell from within that. Using C# allows for easier use of multithreading when working with large numbers of objects, plus it also prevents anyone from “accidently” changing the code. All of the examples that I will be posting can be done via PowerShell alone. So with that said, let’s dive right in to our first section, analysing the PVS usage.

The decision on whether to add or remove servers from a collection is going to be based on what your usage is for the XenApp servers that are running in the collection.  So we need to set a threshold to help us decide on a course of action. To do this, we don’t actually even need to talk to the PVS console, we can get all the information we need from XenApp. Let’s assume that we have organized our XenApp servers in a folder structure in AppCenter that corresponds with our gold disks. This will allow us to query the usage of each of the servers that are using that gold disk.

So I start with creating a list of the folders in AppCenter that I am going to query:

static  ArrayList listOfServerPaths = new ArrayList();

With that in place, we can then use PowerShell to query each of these server paths to gather the session information that we need to work out what our current usage ratio is. The PowerShell class in C# is in the System.Management.Automation DLL. You won’t find this under the usual .NET section of the Add Reference dialog, instead you will need to browse C:\Windows\assembly to find it.

This code requires that the XenApp PowerShell SDK is installed. The easiest solution is to use an admin XenApp server in your farm. In the following code, we are cycling through the list of folders that contain each of the servers using an image. The PowerShell object is created using PowerShell.Create(). A string is then built which contains the PowerShell commands to be executed. The breakdown of the script is as follows:

  • Firstly we load the PoSH snapin for XenApp.
  • Next, we get a list of the servers in the current folder that we are checking using the Get-XAServer cmdlet and using the –FolderPath parameter.
  • Set our session count variable to zero.
  • We now enter another loop to cycle through each of the servers that we have just received. We get the session count using the Get-XASession cmdlet and we then filter it so that we are only getting sessions that are active and using ICA. The session count for the server is then added to our overall session count.
  • Finally, I am outputting both of out count variables for servers and sessions. The reason for this is that the PS.Invoke() method returns a collection of PowerShell objects. But that is not what we are returning here. So by assigning the return value of the invoke method to a var type called results, we end up with an array for each line that the script outputs. So in this case, the first two items in the array are those variables being output.
  • Now that the string containing the PowerShell is built, we add it to the PowerShell object, invoke it setting the return value to a var type and then read and parse the two results.


Here is the C# code:
string command;
foreach (string path in listOfServerPaths)
PowerShell PS = PowerShell.Create();
command = “Add-PSSnapin Citrix.Xenapp.Commands” + Environment.NewLine +
@”$servers = Get-XAServer -FolderPath “”” + path + @”””” + Environment.NewLine +
“$sessionCount = 0” + Environment.NewLine +
“foreach($server in $servers)” + Environment.NewLine +
“{$sessions = Get-XASession -ServerName $server | Where-Object {$_.State -match ‘Active’ -and $_.Protocol -match ‘ICA’}” + Environment.NewLine +
“$sessionCount = $sessionCount + $sessions.count}” + Environment.NewLine + “$servers.count” + Environment.NewLine +
var results = PS.Invoke();
int servers = int.Parse(results[0].ToString());
int sessions = int.Parse(results[1].ToString());
And here is what the PowerShell script would look like:
Add-PSSnapin Citrix.Xenapp.Commands
$servers = Get-XAServer -FolderPath $folderPath
$sessionCount = 0
foreach($server in $servers)
{$sessions = Get-XASession -ServerName $server | Where-Object {$_.State -match ‘Active’ -and $_.Protocol -match ‘ICA’}
$sessionCount = $sessionCount + $sessions.count}

Depending on how many images you are dealing with, you may need to create a collection to store your results so that you can cycle through them on the next step, deciding on an action. In my case, I create a struct variable which contains the name of the folder path\image, the number of servers and the number of sessions. Example:

struct loadResult
private string folderName;
private int serverCount;
private int sessionCount;

In order to decide if we want to add or remove servers from a collection, we need to set what our ratio for usage should be and then determine if we are over or under that ratio. For the below example, I have set the ratio to 20 sessions on a server. This could be higher or lower depending on your server spec. I have also set a minimum server threshold of 2. This means that we would always assume that a minimum of 2 servers should be running in the collection and we do not count them in our calculations.

  • Having set our threshold and ratio values, we then start to cycle through the numbers for each image.
  • If the number of servers exceeds the minimum threshold, we will then analyse the usage ratio on it.
  • The ratio is calculated by dividing the number of sessions by the number of servers, minus the minimum threshold from the server count.
  • If the ratio is less then what we have set, we can then workout how many servers should be removed to get the optimum ratio. The optimum number of servers is the number of sessions divided by the session ratio. The number of servers to remove is then the total number of servers minus the optimum number and the minimum threshold.
  • On the flip side of that, if the ratio is greater than what we have set, we can again work out the optimum number of servers. The number of servers to add is then the optimum number minus the current number of servers.

int minServerThreshold = 2;
int sessionRatio = 20;
foreach (loadResult lr in listOfResults)
if (lr.servers > minServerThreshold )
double ratio = lr.sessions / (lr.servers – minServerThreshold);
if (ratio < sessionRatio)
double optimumNumber = Math.Ceiling(lr.sessions / sessionRatio);
int serversToRemove = lr.servers – optimumNumber – minServerThreshold;
double optimumNumber = Math.Ceiling(lr.sessions / sessionRatio);
int serversToAdd = (optimumNumber – lr.servers);

So after all that, we have a course of action, either add or remove servers, for each of our images and the number of servers required for each course of action. The above code is by no means complete, I’ve tried to keep it as concise as possible so that you can get a good understanding of the logic behind the process. In following posts, I will go in to the process of adding and removing the servers.

Questions or comments, feel free to contact me. Email: sasponto(AT)gmail(DOT)com Twitter: @SasPonto

, , ,

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">