Balance your VMs across ESXi hosts in a vSphere cluster with local storage

The Citrix guys in my company has a bad habbit of using local storage, probably because they have had some bad performance experiences in the past. Local storage is not good for the vSphere admin, as vMotion is set out of play, and I need to move things around manually when doing maintenance. Furthermore the Citrix provisioning tool is not very good at launching/deploying VM’s on hosts that has a lot of free resources, so pretty often we end up with clusters where 50% of the hosts is utilizing 90% of the resources (mainly memory) and 50 % is doing nothing 🙁

To mitigate this I have written a script to “balance” the VM’s equally across the clusters. The script takes a parameter with the cluster name, and you are able to exclude specific hosts by editing the file.

# Script to balance VMs across ESXi hosts in cluster using local storage.
# Created by kasper@nordal-lund.dk
# Execute with cluster name as parameter
param(
[string]$cluster
)

#Make sure the vmware modules are loaded
Get-Module -name vmware* -ListAvailable | Import-Module

#Connect to the viserver
connect-viserver hostname.vcenter -alllinked -Credential (Get-Credential)

#$cluster = "xxx" # Manually define the cluster value. For testing purposes.

# Exclude hosts from the operation, use * as wildcard and seperate with |
$excluded = "" 

#Check if the cluster parameter is set.
if ($cluster -eq "") {
    write-host "You forgot to specify a cluster, please try again..."
    exit
}

# Fire up the main loop
while ($true) {

# Pull out the target hosts
$hosttargetsRaw =  get-cluster $cluster | get-vmhost | where {($_.connectionstate -eq "Connected")} | select name,@{N="VMCount";E={(get-vm -location $_.name).count}} | Sort-Object VMCount -Descending

# Trim the list for exclusions 
$hosttargetstrimmed = $hosttargetsRaw -notmatch $excluded

# Calculate the difference between the most and least populated hosts
$diff = ($hosttargetstrimmed | select -First 1).VMCount - ($hosttargetstrimmed | select -Last 1).VMCount

if ($diff -gt 4) {

# Define the source and destination hosts
$sourcehosts = $hosttargetstrimmed | select -First 2
$desthosts = $hosttargetstrimmed | select -last 2

# Check if we have only one hosts doing nothing
$bottomdiff = ($desthosts | select -first 1).VMcount - ($desthosts | select -Last 1).VMCount

$i = 0

foreach ($sourcehost in $sourcehosts.name){
	if ($bottomdiff -gt 4) {
        $curdesthost = $desthosts[1].name
    }else {
        $curdesthost = $desthosts[$i].name
	    $i++
    }
# Get the destination datastore		
$destds = (get-datastore -vmhost $curdesthost local*).name

# get the VM's we want to move
$targets = get-vmhost $sourcehost | get-vm | select -first 2

# Move the VM's 2 from each hosts = 4 VM's pr. loop
foreach ($target in $targets) {
    echo "move-vm -vm $target -Destination $curdesthost -Datastore $destds -RunAsync"
   }
}
# Wait for the VM's to be moved before looping again.  
sleep -Seconds 90
}else {
    write-host "Balancing of cluster $cluster is finished, difference between most and least populated host is $diff VMs..."
exit
}
}

I hope you are able to use this, at least as inspiration for getting on with your own.
And remember, this script can of course be combined with my powershell menu script found here: https://www.nordal-lund.dk/?p=574

Enjoy…

Powershell menu script

Do you ever need to give some input to a script? Maybe its a long filename or some other long and complex string, or maybe you’re just lazy like me? I wrote a script for handling the input, it’s a bit like the curses based menus you see in some network equipment, and in Linux systems. The eaxmple below is for getting the firmware baseline for a HP OneView system, but the menu is usable almost everywhere you need to specify something.

Lets stop talking, and take a look at the actual script:

param(
[string]$selection
)

if ((get-module hpone*).count -lt 1){
Get-Module -name hpone* -ListAvailable | Import-Module -WarningAction SilentlyContinue
}

if ($Global:connectedSessions){
    echo "Already connected..."
    } else {
    Connect-HPOVMgmt -Appliance ADDRESS -Credential (Get-Credential)
}

$global:MenuOptions = get-hpovbaseline

if ($Selection -eq ""){

    Clear-Host
    Write-Host "================Select Firmware Version================"
    
    $MenuNumber = 1
    foreach ($Option in $MenuOptions.version){

    Write-Host "$MenuNumber : $Option"
    $MenuNumber++
    }
    Write-Host -ForegroundColor red "Quit: Press 'ctrl-c' to quit."

    [int]$Selection = Read-Host "Please select firmware by number"
    
}

$baseline = $MenuOptions[$Selection -1]

The above code has some checks in the beginning to see if we have the right modules loaded, and if we are connected to the OneView server. This is not needed for the menu, but maybe it can be useful for you anyway.
In this example I’m listing the firmware included in the baselines from an HP OneView system, and the $baseline variable will have the selection I made.
This is usable for all kinds of interactions with other systems, e.g. its also very usable for vmware vCenter.
The selection parameter is there if you run this task often and already know what to select, then you can add it as an attribute to the script.

Please enjoy using the script.

Get rid of the facebook login box

Do you also hate the big facebook login box nagging you when you’re not signed in? Even sites that should be public available is ruined by this, luckily there’s a solution 🙂

Maybe you already have an add-blocker, if not I can recommend ublock origin, but anyone will probably do.

Create a custom filter and add the following line:

www.facebook.com###pagelet_growth_expanding_cta

This will make the annoying prompt go away – at least for now…

Add new virtualportgroup to vSwitch on multiple VMHosts with Powercli

If you dont have dvSwitches (Distributed vSwitches) in your vSphere cluster, or dont even have a cluster, you may have to add new portgroups manually, depending on the number of VMHosts this can be a pretty cumbersome task.

Luckily, we can use powercli to automate the task. In the following example i will use the -location parameter to define my tagets in the variable $hosts

Lets go ahead and define the VMhosts we want to target:

$hosts=get-vmhost -location “location”

Then, we create the new portgroup for every VMhost in the location by using a foreach loop:

foreach ($vmhost in $hosts) {Get-VMHost $vmhost | Get-VirtualSwitch -name “vSwitch0” | New-VirtualPortGroup -VLanId “10” -Name “Name”}

This will create a new VirtualPortGroup with VLAN ID 10 named “Name” on vSwitch0 on all the VMHosts in the specified location.

Remove the port again by doing this:

foreach ($vmhost in $hosts) {Get-VMHost $vmhost | Get-VirtualSwitch -name “vSwitch0” | Get-VirtualPortGroup -Name “Name” | Remove-VirtualPortGroup -Confirm:$false}

Remember that -Confirm:$false will remove the port without confirmation! Make sure you have the right targets in the $hosts variable!!!

Attachments i roundcube webmail.

Jeg har gennem længere tid brugt roundcube webmail. Den er lavet med AJAX så den har nogle fede “eye candy” effekter og den er dejlig nem og hurtig at bruge. Forleden skulle jeg for første gang prøve at sende en mail med en vedhæftet fil via roundcube, det gik ikke så godt. Der var ingen problemer med at vedhæfte filen, men da den skulle sendes kom den aldrig videre end til at vise en fin animeret cirkel, der ligesom indikerer at den arbejder, men videre kom den altså aldrig.

Så måtte jeg jo ud og spørge google hvad det var for noget, der var mange forslag til at ændre grænsen for hvor meget man må uploade og hvor meget hukommelse PHP må ligge beslag på, men alt så faktisk fint ud.

I php.ini filen kan man angive maximum værdier for 3 parametre der kan have indflydelse:

  1. memory_limit som stod til 32 MB.
  2. post_max_size som stod til 16 MB.
  3. upload_max_filesize som også stod til 16 MB.

Udover php.ini er der en .htaccess fil i roden af roundcube mappen og her kan man også sætte nogle grænser, men de stod alle til 10 MB. og jeg forsøgte kun at sende 5 MB.

Jeg tjekkede at jeg kunne sende en attachment på 5 MB med mutt, og der var ingen problemer. Jeg har heller aldrig oplevet problemer med andre mail klienter, så det måtte altså være roundcube den var gal med.

Postfix log filerne sagde intet, faktisk kunne jeg se at den aldrig nåede til at begynde at sende, da der slet ikke var nogle entries som der normalt er når man sender en mail.

Så fandt jeg nogle logfiler fra roundcube som den gemmer i kataloget logs/ i sin egen mappe. Her fandt jeg nogle fejl i logs/errors som ledte mig på rette vej. Her er et eksempel:

PHP Fatal error:  Allowed memory size of 33554432 bytes exhausted (tried to allocate 4696737 bytes) in /usr/share/roundcubemail/program/lib/Net/SMTP.php on line 831

OK, så det måtte altså være PHP der blokerede for at sende denne mail. Jeg google lidt igen på den ovennævnte log entry og jeg fandt en løsning.

Det viser sig at memory_limit værdien i php.ini som stod til 32 MB var for lidt. Hvorfor ved jeg ikke, men jeg har heller ikke gjort noget for at undersøge det. Jeg ændrede den til 64 MB og nu kunne jeg sende mails med “store” vedhæftninger.

Citrix XenApp klient til Linux på Ubuntu 9.04

Følgende gælder på ubuntu 9.04, men muligvis også andre linux distributioner.

Den, i skrivende stund, nyeste XenApp klient fra citrix er version 11. Den kræver ligesom de tidligere versioner Openmotif, dette klares på ubuntu ved at installere libmotif3:

sudo apt-get install libmotif3

Muligvis er det også nødvendigt at installere pakken libmotif-dev det klares på samme måde som ovenstående bare med libmotif-dev istedet for libmotif3.

når ovenstående er klaret installerer man citrix klienten som beskrevet i installations guiden og man får et fint ikon i sin start menu der hedder “Citrix Receiver” (det nyeste navn for citrix klienten til linux). Problemet er nu at porgrammet ikke starter op når man trykker på ikonet. En manuel start af programmet fra en konsol afslører at der mangler et library kaldet libXm.so.4.

Hvis man så ser efter i /usr/lib/ vil man kunne se at der ligger libXm.so og at der er nogle symlinks til libXm.so.1 libXm.so.2 og libXm.so.3. Nu laver man så bare endnu et symlink til libXm.so.4, dette gøres således:

sudo ln -s /usr/lib/libXm.so /usr/lib/libXm.so.4

Nu starter klienten op og virker som forventet.

sed

sed er en forkortelse for stream editor, jeg vil her give en kort introduktion til hvad man bl.a. kan bruge det til.

sed er et stækt værktøj når man hurtigt skal have ændret indholdet i tekst filer – se eksemplet herunder:

sed -i ‘s/hurtig/langsom/’ /home/kasper/dokuments/skildpadden.txt

Lad os gennemgå kommandoen ovenover:

-i fortæller sed at den skal lave ændringerne “in place”, dvs. at ændringerne sker direkte i filen. Hvis vi havde udeladt attributten -i havde vi fået resultatet ud til std. output (skærmen) og der vil altså ikke være ændret i filens indhold. s står for substitute og skråstregerne adskiller vores søgemønster med vores erstatnings tekst. Pingerne er ikke nødvendige i ovenstående eksempel, men skal bruges når der optræder visse karakterer, så jeg har gjort det til en vane bare eat bruge dem altid.

Ovenstående kommando søger altså efter ordet hurtig og skifter det ud med langsom i teksten skildpadden.txt der ligger mappen /home/kasper/documents. Dog vil denne kommando kun finde den første forekomst af ordet og skifte det ud, hvis vi vil skifte alle forekomster skal vi tilføje et g som står for global efter den sisdte skråstreg i søgningen, kommandoen vil komme til at se såldedes ud:

sed -i ‘s/hurtig/langsom/g’ /home/kasper/dokuments/skildpadden.txt

Nu vil alle forekomster af ordet hurtig i filen skildpadden.txt blive skiftet ud med langsom.

Dette var en lille hurtig introduktion, måske skriver jeg noget mere på senere. Indtil da kan i læse mere her: http://www.grymoire.com/Unix/Sed.html#uh-0