PVS Retries – Script

As PVS retries can indeed cause all sorts of degradation to the user experience (i.e. applications freezing or overall slowness) and it is not something that is readily exposed on any of the Citrix monitoring/management consoles (even the PVS console does not show that info, or Director for that matter), I decided to write this little PowerShell script to get that information and show it in a nice graph. This is what it looks like:

PVSGraph

Couple comments:

  • What is considered high/normal/low for retries? I have no idea if anyone ever came up to a number. Also keep in mind the number returned by PVS is since the machine booted up or since someone reset the counter. So 1000 retries over 10 days is not a big deal if you ask me but 1000 in 5 minutes there is indeed something wrong. I would love to hear what others have to say.
  • I could (and should ) calculate and show Retries/min instead of just retries. Simply a matter of retrieving the uptime of the server, converting to minutes and dividing retries by that.
  • I assume you know how to get the MS Chart .NET libraries/PVS stuff registered/working.

So here is the code:

# This function was created by Remko, another Citrix CTP
# and probably the craziest motherfucker I have ever met.
# As the PVS PowerShell sucks, not even returning proper objects
# people like Remko took matters on their own hands.
# You can see his post here.
# http://www.remkoweijnen.nl/blog/2012/02/29/convert-mcli-output-into-powershell-objects/

function ToObject {
    param(
     [Parameter(
          Position=0,
          Mandatory=$false,
          ValueFromPipeline=$true,
          ValueFromPipelineByPropertyName=$true)
    ]
    [Alias('Command')]
    [string]$cmd
    )
 
     $collection = @()
     $item = $null
 
     switch -regex (Invoke-Expression $cmd)
     {
          "^Record\s#\d+$"
          {
                if ($item) {$collection += $item}
                $item = New-Object System.Object
          }
          "^(?<name>\w+):\s(?<value>.*)"
          {
                if ($Matches.Name -ne "Executing")
                {
                     $item | Add-Member -Type NoteProperty -Name $Matches.Name -Value $Matches.Value
                }
          }
     }
     return $collection
}


# Loads the appropriate assemblies
[void][Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
[void][Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms.DataVisualization”)
Add-PSSnapin –Name McliPSSnapIn -ErrorAction SilentlyContinue
Mcli-Run SetupConnection -p server="ENTER YOUR PVS SERVER FQDN HERE (i.e. PVS01.Company.com)"
$XAServers = 'Mcli-Get DeviceInfo -p siteName="YOUR PVS SITE NAME",collectionName="DEVICE COLLECTION YOUR VMs ARE IN"' | ToObject

# Creates chart object
 $Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
 $Chart.Width = 1000
 $Chart.Height = 600
 $Chart.Left = 10
 $Chart.Top = 10

# Creates a chartarea to draw on and add to chart
 $ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
 $ChartArea.AxisX.Interval = 1
 $ChartArea.AxisX.Title = “Servers”
 $ChartArea.AxisY.Interval = 50
 $ChartArea.AxisY.Title = “PVS Retries”
 $Chart.ChartAreas.Add($ChartArea)
 [void]$Chart.Series.Add(“Data”)
 $Chart.Series["Data"]["DrawingStyle"] = "Cylinder"

# Adds a data point for each server
 foreach ($server in $XAServers)
 {
 
 $dp1 = new-object System.Windows.Forms.DataVisualization.Charting.DataPoint(0, $server.status)
 
 # For my particular needs I assumed the retries as this:
 # Good, under 100. Attention, between 100 and 300. Bad, over 300.
 # Am I right? No clue. Please comment/contribute with your findings.
 
 If ([int]$server.status -lt 101) 
    {
     $dp1.Color = [System.Drawing.Color]::Green
    }
   Else
    {
     If ([int]$server.status -gt 100 -and [int]$server.status -lt 301)
        {
         $dp1.Color = [System.Drawing.Color]::Yellow
        }
       Else
        {
         $dp1.Color = [System.Drawing.Color]::Red
        }
    }

 $xlabel = $server.deviceName
 $dp1.AxisLabel = $xlabel
 $Chart.Series[“Data”].Points.Add($dp1)
 }
 # Sets the title to the date and time
 $title = new-object System.Windows.Forms.DataVisualization.Charting.Title
 $Chart.Titles.Add( $title )
 $Chart.Titles[0].Text = date

# Saves the chart to a file on the server where the script runs.
# Could be anywhere, even UNC path.
 $Chart.SaveImage(“C:\Graph\FarmRetries.png“,”png”)

It can be certainly improved and I will work on that. For now, give it a try and let me know what you think.

CR

6,548 total views, 2 views today

StoreFront 1.2 Install and how to avoid SQL issues.

As I promised a couple days ago on Twitter, here you have the scripts I used to install StoreFront. It is pretty annoying to realize if you install it using the installer provided by Citrix and let it deal with the database you will get screwed at one point. You will see stupid errors on the StoreFront Web Site, errors on the event log and so on. Resuming: A PITA.

Now if you use the scripts, well then everything gets fixed magically. That leads us to the question why the installer does not clearly state you MUST use the damn scripts to create the database? Or why the installer cannot use the scripts by itself? As I always joke we put people on the moon but we still fail to have installers that can actually install things properly. Amazing.

The first script deals with installing the pre-requisites. I usually use one like this:
powershell -nologo -executionpolicy bypass “& “c:\Installs\SF-PreReq.ps1”

So basically I have a folder on the C: drive called Installs and on it a script called SF-PreReq.ps1 (PowerShell), setting the required execution policy (basically, leave me alone). The contents of SF-PreReq.ps1 are:

Import-Module ServerManager
Add-WindowsFeature as-net-framework
Add-WindowsFeature Web-Server
Add-WindowsFeature Web-Asp-Net
Add-WindowsFeature Web-Windows-Auth
Add-WindowsFeature Web-Metabase
Add-WindowsFeature Web-Http-Redirect
Add-WindowsFeature Web-App-Dev
Add-WindowsFeature Web-Basic-Auth
Add-WindowsFeature Web-Digest-Auth
Add-WindowsFeature Web-Client-Auth
Add-WindowsFeature Web-Cert-Auth
Add-WindowsFeature Web-Url-Auth
Add-WindowsFeature Web-IP-Security
Add-WindowsFeature Web-Dyn-Compression
Add-WindowsFeature Web-Scripting-Tools
Add-WindowsFeature Web-Mgmt-Service
Add-WindowsFeature Web-Mgmt-Compat

I can tell you it works as I did install my production StoreFront 1.2 using it.
Once the pre-requisites are done, then you connect to your SQL box (RDP for example) and using the SQL Management Studio with the proper credentials you will run four scripts in sequence. Make sure to adjust the database name, paths, etc to match whatever you have/decide to use.

First Script – Creates the database

USE [master]

CREATE DATABASE [CitrixStoreFront] ON PRIMARY
( NAME = N’MyApps’, FILENAME = N’C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\CitrixStoreFront.mdf’ , SIZE = 4096KB ,
MAXSIZE = UNLIMITED, FILEGROWTH = 10% )
LOG ON
( NAME = N’MyApps_log’, FILENAME = N’C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\CitrixStoreFront_log.ldf’ , SIZE = 560KB ,
MAXSIZE = 2048GB , FILEGROWTH = 10% )
COLLATE latin1_general_CI_AS_KS

IF (1 = FULLTEXTSERVICEPROPERTY(‘IsFullTextInstalled’))
begin
EXEC [CitrixStoreFront].[dbo].[sp_fulltext_database] @action = ‘enable’
end

ALTER DATABASE [CitrixStoreFront] SET ANSI_NULL_DEFAULT OFF
ALTER DATABASE [CitrixStoreFront] SET ANSI_NULLS OFF
ALTER DATABASE [CitrixStoreFront] SET ANSI_PADDING OFF
ALTER DATABASE [CitrixStoreFront] SET ANSI_WARNINGS OFF
ALTER DATABASE [CitrixStoreFront] SET ARITHABORT OFF
ALTER DATABASE [CitrixStoreFront] SET AUTO_CLOSE OFF
ALTER DATABASE [CitrixStoreFront] SET AUTO_CREATE_STATISTICS ON
ALTER DATABASE [CitrixStoreFront] SET AUTO_SHRINK OFF
ALTER DATABASE [CitrixStoreFront] SET AUTO_UPDATE_STATISTICS ON
ALTER DATABASE [CitrixStoreFront] SET CURSOR_CLOSE_ON_COMMIT OFF
ALTER DATABASE [CitrixStoreFront] SET CURSOR_DEFAULT GLOBAL
ALTER DATABASE [CitrixStoreFront] SET CONCAT_NULL_YIELDS_NULL OFF
ALTER DATABASE [CitrixStoreFront] SET NUMERIC_ROUNDABORT OFF
ALTER DATABASE [CitrixStoreFront] SET QUOTED_IDENTIFIER OFF
ALTER DATABASE [CitrixStoreFront] SET RECURSIVE_TRIGGERS OFF
ALTER DATABASE [CitrixStoreFront] SET DISABLE_BROKER
ALTER DATABASE [CitrixStoreFront] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
ALTER DATABASE [CitrixStoreFront] SET DATE_CORRELATION_OPTIMIZATION OFF
ALTER DATABASE [CitrixStoreFront] SET TRUSTWORTHY OFF
ALTER DATABASE [CitrixStoreFront] SET ALLOW_SNAPSHOT_ISOLATION OFF
ALTER DATABASE [CitrixStoreFront] SET PARAMETERIZATION SIMPLE
ALTER DATABASE [CitrixStoreFront] SET READ_COMMITTED_SNAPSHOT OFF
ALTER DATABASE [CitrixStoreFront] SET HONOR_BROKER_PRIORITY OFF
ALTER DATABASE [CitrixStoreFront] SET READ_WRITE
ALTER DATABASE [CitrixStoreFront] SET RECOVERY FULL
ALTER DATABASE [CitrixStoreFront] SET MULTI_USER
ALTER DATABASE [CitrixStoreFront] SET PAGE_VERIFY NONE
ALTER DATABASE [CitrixStoreFront] SET DB_CHAINING OFF

Second Script – Creates the tables

USE [CitrixStoreFront]

/****** Object: Table [dbo].[User] ******/
SET ANSI_NULLS ON

SET QUOTED_IDENTIFIER ON

CREATE TABLE [dbo].[User](
[id] [int] IDENTITY(1,1) NOT NULL,
[username] [nvarchar](100) COLLATE latin1_general_CS_AS_KS NOT NULL,
CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PRIMARY]
) ON [PRIMARY]

CREATE UNIQUE NONCLUSTERED INDEX [username_idx] ON [dbo].[User]
(
[username] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PRIMARY]

/****** Object: Table [dbo].[Subscription] ******/
SET ANSI_NULLS ON

SET QUOTED_IDENTIFIER ON

CREATE TABLE [dbo].[Subscription](
[id] [int] IDENTITY(1,1) NOT NULL,
[subscription_ref] [varchar](32) COLLATE latin1_general_CS_AS_KS NOT NULL,
[resource_id] [nvarchar](400) COLLATE latin1_general_CS_AS_KS NOT NULL,
[user_id] [int] NOT NULL,
[status] [int] NOT NULL,
[metadata] [nvarchar](max) NULL,
[secure_metadata] [nvarchar](max) NULL,
CONSTRAINT [PK_subscriptions] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PRIMARY]
) ON [PRIMARY]

CREATE UNIQUE NONCLUSTERED INDEX [subscription_ref_idx] ON
[dbo].[Subscription]
(
[subscription_ref] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PRIMARY]

CREATE NONCLUSTERED INDEX [user_resource_idx] ON [dbo].[Subscription]
(
[user_id] ASC,
[resource_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF,
ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF)
ON [PRIMARY]

/****** Object: Default [DF_subscriptions_status] ******/
ALTER TABLE [dbo].[Subscription]
ADD CONSTRAINT [DF_subscriptions_status]
DEFAULT ((0)) FOR [status]

/****** Object: ForeignKey [FK_subscriptions_user_id] ******/
ALTER TABLE [dbo].[Subscription]
WITH CHECK ADD CONSTRAINT [FK_subscriptions_user_id]
FOREIGN KEY([user_id])
REFERENCES [dbo].[User] ([id])

ALTER TABLE [dbo].[Subscription]
CHECK CONSTRAINT [FK_subscriptions_user_id]

CREATE TABLE [dbo].[SchemaDetails](
[major_version] [int] NOT NULL,
[minor_version] [int] NOT NULL,
[details] [nvarchar](max) NULL
) ON [PRIMARY]

INSERT INTO [dbo].[SchemaDetails] ([major_version], [minor_version])
VALUES (1, 0)

Third Script – Assigns the correct login to the database. This one you need to create on the SQL server a local group with whatever name and add the StoreFront servers to it. In my case I used StoreFrontServers as the local group on the SQL Server.

A local group on the SQL Server must be created.
A local group on the SQL Server must be created.

USE [master]
CREATE LOGIN [YOUR_SQL_SERVER\StoreFrontServers] FROM WINDOWS;
ALTER LOGIN [YOUR_SQL_SERVER\StoreFrontServers]
WITH DEFAULT_DATABASE = [CitrixStoreFront];

Fourth Script – Fixes permissions on the database

USE [CitrixStoreFront]
CREATE USER [CitrixSubscriptionDBUsers] FOR LOGIN [YOUR_SQL_SERVER\StoreFrontServers];

EXEC sp_addrolemember N’db_datawriter’, N’CitrixSubscriptionDBUsers’;
EXEC sp_addrolemember N’db_datareader’, N’CitrixSubscriptionDBUsers’;

That is it. Once you have all the scripts done (in sequence) on the SQL Server you can then fire up the StoreFront installation. All the pre-requisites will be already in place and the database created. You simply follow the wizard and you are all set.
Make sure you do have a certificate installed on the StoreFront server before you fire up the install (do the whole certificate thing right AFTER you run the pre-requisites script and make sure HTTPS is bound to IIS) .

That is all. StoreFront should now install properly.

CR

10,449 total views, no views today