Lors d’une intervention, on m’a demandé de faire un script de shutdown de toutes les VM d’un site lorsque l’UPS HPE R5000 passe sous un certain seuil de batterie. Avec cet UPS, une façon de faire est de déployer une VM (vMA) sur le cluster à éteindre et d’installer le Power Protector client sur cette petite machine. Enfin, on configure ce client pour lancer un script en PERL de Shutdown afin de s’assurer que l’environnement virtuel soit proprement éteint.
Les infos de l’infrastructure existante :
- Version ESXi : 6.0 U3
- vCenter sur un autre site (bien que non important ici)
- Nom de la vMA : VM-vMA
- Version HPE Power Protector : Linux x64 v1.05.058
- Version de la vMA : 6.0.0.1
Script Perl général Power Protector
La vMA utilise le SDK Perl afin de se connecter à l’ESXi et permet de récupérer des informations (Host, VM…) ou encore de faire des actions sur cet hôte. Comme nous sommes dans un cluster, il n’est pas possible d’utiliser la fonction de démarrage/arrêt automatique fourni par VMware. Il ne suffit pas simplement de Shutdown l’ESXi mais bien toutes les VM. Un script “ Talata Mafara all-shutdown.pl” principal va alors lancer plusieurs scripts PERL pour automatiser l’extinction :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/usr/bin/perl -w ############################################################################### # # Ce Script arrête 2 ESXi dans un cluster avec HPPP et la vMA de VMWARE # ############################################################################### # ESXi01 with VM's shutdown system("perl /home/vi-admin/Shutdown-VM.pl --server ESXi01.lab.local --ups_vm VM-vMA --username root --password ****** >> /tmp/log_shutdown.log"); # ESXi02 with VM's shutdown system("perl /home/vi-admin/Shutdown-VM.pl --server ESXi02.lab.local --ups_vm VM-vMA --username root --password ****** >> /tmp/log_shutdown.log"); sleep(60); # ESXi02 shutdown system("perl /usr/local/HPE/PowerProtector/bin/virt_tools/shutdownESXi.pl --server ESXi02.lab.local --username root --password ***"); # ESXi01 shutdown system("perl /usr/local/HPE/PowerProtector/bin/virt_tools/shutdownESXi.pl --server ESXi01.lab.local --username root --password ***"); |
Quelques remarques :
- un fichier /tmp/log_shutdown.log doit être créé
- on spécifie le nom de la vMA afin de ne pas l’arrêter (sinon tout le script ne se terminera pas)
- le script shutdownESXi.pl est fourni avec le client Power Protector d’HPE
- Il faut également faire un chmod 755 sur tous les scripts all-shutdown.pl, Shudown-VM.pl et shutdownESXi.pl et sur le fichier log_shutdown.log
- Remplacez seulement les étoiles par les mots de passes ESXi respectifs
Ci-dessous, un exemple de configuration du client HPE Power Protector installé sur la vMA. Après avoir renseigné l’adresse de votre UPS et les credentials associés, vous entrez le type de shutdown (mode script) avec le script en argument. PS : Je trouve la gestion de Power Protector étrange car on ne peut pas configurer un seuil de batterie pour le Shutdown :/ donc quoi qu’il arrive, notre environnement vSphere s’éteindra au bout de 4800 secondes si le courant est coupé.
Script Perl Shutdown VM
Le premier script appelé dans celui principal permet d’éteindre toutes les VM de l’ESXi en question sauf la vMA. Les arguments qui ont été spécifié sont obligatoires et il ne fonctionnera pas si vous ne précisez pas le nom de la vMA par exemple. Une fonction de timestamp a été mise car je souhaitais avoir dans le fichier de log le temps précis du démarrage/fin du script. Bien évidement, les VM sont arrêtées avec les VMware Tools si elles sont installées, donc veillez à mettre les tools sur le maximum de machines virtuelles. Nous testons également de savoir si la VM est déjà éteinte afin de ne pas générer d’erreurs.
La partie Patch a été récupérée du script HPE existant et permet de s’assurer de la compatibilité entre la vMa et l’ESXi destination. La fin du script permet juste de revoir l’état des VM avant de sortir du script et de l’inscrire dans le fichier de log. Ceci permet de s’assurer que toutes les VM étaient éteintes lors du shutdown électrique de l’infra.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
#!/usr/bin/perl -w ############################################################################### # ShutDown all VM ############################################################################### #Fonction pour set le temps sub timeStamp { my ($date_format) = @_; my %dttime = (); my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my $my_time; my $time_string; ### begin_: initialize DateTime number formats $dttime{year } = sprintf "%04d",($year + 1900); ## four digits to specify the year $dttime{mon } = sprintf "%02d",($mon + 1); ## zeropad months $dttime{mday } = sprintf "%02d",$mday; ## zeropad day of the month $dttime{wday } = sprintf "%02d",$wday + 1; ## zeropad day of week; sunday = 1; $dttime{yday } = sprintf "%02d",$yday; ## zeropad nth day of the year $dttime{hour } = sprintf "%02d",$hour; ## zeropad hour $dttime{min } = sprintf "%02d",$min; ## zeropad minutes $dttime{sec } = sprintf "%02d",$sec; ## zeropad seconds $dttime{isdst} = $isdst; if($date_format eq 'MDYHMS') { $my_time = "$dttime{mon}-$dttime{mday}-$dttime{year} $dttime{hour}:$dttime{min}:$dttime{sec}"; $time_string = $my_time; } elsif ($date_format eq 'YMD') { $my_time = "$dttime{year}-$dttime{mon}-$dttime{mday}"; $time_string = $my_time; } return $time_string; } printf "\nHPE Shutdown Initiated : ".timeStamp('MDYHMS')."\n"; #---------Start of Patch------------------# my $lwpversion= "$LWP::UserAgent::VERSION"; my ($major, $minor) = split (/\./, $LWP::UserAgent::VERSION); if(($major < 6)) { #Under 6 printf "LWP::UserAgent version is under 6\n"; $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; } elsif( ($major >= 6)&& ($minor <04)) { #Above 6 under 04 printf "LWP::UserAgent version is above 6 an under 6.04\n"; $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; } else { #Above 6 printf "LWP::UserAgent version number above 6.04\n"; printf "Actual version is: $lwpversion\n"; } #----------End of Patch------------# use strict; use warnings; use VMware::VIRuntime; use VMware::VILib; #Récupération des arguments my %opts = ( server => { type => "=s", help => "IP name or adress of the ESX/ESXi server", required => 1, }, username => { type => "=s", help => "Name of the ESX/ESXi administrator (ex: root)", required => 1, }, password => { type => "=s", help => "Password of the administrator", required => 1, }, ups_vm => { type => "=s", help => "UPS Monitoring VM", required => 1, }, ); # Validate options, and connect to the server Opts::add_options(%opts); Opts::parse(); Opts::validate(); Util::connect(); # Obtain all inventory objects of the specified type ########## Recup des infos de l'ESXi my $ups_vm_name = Opts::get_option('ups_vm'); my $esxi = Opts::get_option('server'); ########## Liste de toutes les VM de l'ESXi my $entity_views = Vim::find_entity_view(view_type => 'HostSystem'); my $vm_views = Vim::find_entity_views(view_type => 'VirtualMachine', begin_entity => $entity_views); printf "\nStarting VM Shutdown on ESXi : ".$esxi." \n"; # Process Shutdown all VM foreach my $vm (@$vm_views) { #on recupere le nom de la VM courante my $entity_name = $vm->name; #si ce n'est pas la vMA if ($entity_name ne $ups_vm_name) { #Check si la VM est poweredOn if($vm->runtime->powerState->val eq 'poweredOn') { #Check si Vmware Tools if(defined($vm->guest) && ($vm->guest->toolsStatus->val eq 'toolsOld' || $vm->guest->toolsStatus->val eq 'toolsOk') ) { #Si tools printf "\tinfo : Shutting down $entity_name via VMware Tools...\n"; $vm->ShutdownGuest(); } else { #Si pas de Tools printf "\tinfo : Hard Powering off $entity_name, no VMware Tools found ...\n"; $vm->PowerOffVM(); } }else{ #Si déjà Off printf "\tinfo : VM $entity_name already Powered Off.\n"; } }else{ #Si vMA printf "\tinfo : VM $entity_name controls scripts, no actions...\n"; } } #Attente pour shutdown sleep(60); printf "\n\nSleeping for 60seconds...\n"; # Recheck Status des VM printf "\n\nChecking VM State on ESXi : ".$esxi." \n"; ########## Get a view of the Virtual Machines on the current host $entity_views = Vim::find_entity_view(view_type => 'HostSystem'); $vm_views = Vim::find_entity_views(view_type => 'VirtualMachine', begin_entity => $entity_views); foreach my $vm (@$vm_views) { my $entity_name = $vm->name; my $entity_state = $vm->runtime->powerState->val; printf "\t".$entity_name." : ".$entity_state.".\n"; } # Disconnect to the server printf "\nCompleted at ".timeStamp('MDYHMS')." : Disconnecting ESXi...\n"; Util::disconnect(); |
Voici un petit exemple du fichier de log qui peut être généré. Chaque nouvelle occurence du script ajoute des lignes au fichier sans l’écraser. On peut voir les différents scénarios (vMA, déjà éteinte, tools/notools).
Script Perl HPE Shutdown ESXi
Enfin le script d’extinction fournit par HPE qui reste simple car il fait simplement un shutdown de l’ESXi sans se préoccuper de savoir si des VM vont être correctement éteintes ou non.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#!/usr/bin/perl -w ############################################################################### # (c) 2010 - Eaton Power Quality # # This scripts allows users to perform a Shutdown on an VMware ESXi Server # with the vMA for HPPP # example: # ./shutdownESXi.pl --server $server --username $username --password $password ############################################################################### #---------Start of Patch------------------# #Issue Known => http://www.vmware.com/support/developer/vcli/vcli501/vsp5_501_vcli_relnotes.html #Search "version unavailable" my $lwpversion= "$LWP::UserAgent::VERSION"; my ($major, $minor) = split (/\./, $LWP::UserAgent::VERSION); if(($major < 6)) { #Under 6 printf "LWP::UserAgent version is under 6\n"; $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; } elsif( ($major >= 6)&& ($minor <04)) { #Above 6 under 04 printf "LWP::UserAgent version is above 6 an under 6.04\n"; $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; } else { #Above 6 printf "LWP::UserAgent version number above 6.04\n"; printf "Actual version is: $lwpversion\n"; } #----------End of Patch------------# use strict; use warnings; use VMware::VIRuntime; use VMware::VILib; my %opts = ( server => { type => "=s", help => "IP name or adress of the ESX/ESXi server", required => 1, }, username => { type => "=s", help => "Name of the ESX/ESXi administrator (ex: root)", required => 1, }, password => { type => "=s", help => "Password of the administrator", required => 1, } ); # Validate options, and connect to the server Opts::add_options(%opts); Opts::parse(); Opts::validate(); Util::connect(); # Obtain all inventory objects of the specified type my $entity_type = "HostSystem"; my $entity_views = Vim::find_entity_views(view_type => $entity_type); # Process the findings foreach my $entity_view (@$entity_views) { my $entity_name = $entity_view->name; $entity_view->ShutdownHost(force => 1); #if force => 0 the Host must be in maintenance mode otherwise the operation is not allowed } # Disconnect to the server Util::disconnect(); |
Conclusion
Voici une solution pour arrêter vos ESXi proprement et qui fonctionne avec un UPS HPE (et son network module). Le script est forcément à améliorer car je l’ai fait en une après-midi, je n’ai pas pensé à tous les uses cases et les améliorations possibles. Le problème de la prestation, jamais le temps de s’attarder sur les choses 😀