Skip to content

Commit

Permalink
Made the -f (force) flag actually do something useful
Browse files Browse the repository at this point in the history
Added 'force' keyword to the config file
Relevent operations now fatally fail if -f is not specified
Rewrote the Makefile to use the $VERSION identifier from the vmm script rather than the git tag
Removed the git tags for version numbers
Added whitespace splitting to CSV readers
BUGFIX: Fatal errors now a little easier to parse
BUGFIX: Fatal errors are now indeed fatal - yes, yes i know, stupid stupid stupid
BUGFIX: Info on mass clones now only displays if we are making >1 clone
BUGFIX: Many minor fixes
BUGFIX: Small documentation fixes
  • Loading branch information
hash-bang committed Apr 25, 2011
1 parent abfdf18 commit f891865
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 35 deletions.
4 changes: 2 additions & 2 deletions Makefile
@@ -1,4 +1,4 @@
VERSION := $(shell git describe --match 'v[0-9].[0-9]' --tags --long | grep -Eo 'v[0-9]+\.[0-9]+-[0-9]+' | tr - . | cut -c 2-)
VERSION := $(shell perl -MExtUtils::MakeMaker -le 'print MM->parse_version(shift)' vmm)
DEBFACTORY := DebFactory

README: vmm
Expand All @@ -7,7 +7,7 @@ README: vmm
git commit -m 'Auto update from POD'

commit: README
git commit -a
-git commit -a

push: commit
git push
Expand Down
1 change: 1 addition & 0 deletions docs/vmmrc.example
Expand Up @@ -4,6 +4,7 @@ verbose = 2
profile = Cluster1
dryrun = 0
human = 1
force = 0
seperator = \t

[Cluster1]
Expand Down
109 changes: 76 additions & 33 deletions vmm
@@ -1,4 +1,5 @@
#!/usr/bin/perl
# POD {{{
=head1 NAME
vmm - Manage VMware virtual machines
Expand Down Expand Up @@ -202,6 +203,8 @@ Datastore name
Specify a data store for operations that require it.
=item B<--force>
=item B<-f>
=over
Expand All @@ -218,8 +221,8 @@ Switch
=back
Force continue if an error occurs.
Normaly if an error occurs vmm will stop processing any further VMs specified on the command line.
If this flag is enabled vmm will instead continue operation as if no error occured.
Normaly if an error occurs vmm will stop processing any operations specified on the command line.
If this flag is enabled vmm will continue operation as if no error occured.
=item B<--human>
Expand Down Expand Up @@ -439,6 +442,11 @@ Take a snapshot of VMs 'DB04' and 'DB05' using the title 'Todays backup'
Turn DB00 and DB01 on waiting 30 seconds between machines.
=item B<vmm state on DB00 DB01 -w 30 -f>
Turn DB* VMs on.
-f ensures that even if any of the machines fail to turn on for any reason the remaining machines will still be sent the 'on' command.
=back
=head1 FILES
Expand Down Expand Up @@ -467,6 +475,7 @@ The layout of the config file spcifies which profiles to use.
profile = Cluster1
dryrun = 0
human = 1
force = 0
seperator = \t
[Cluster1]
Expand Down Expand Up @@ -503,6 +512,10 @@ The authentication information when connecting to the vServer.
Specify a default dry run value. See B<-n> for further information.
=item B<force>
If an error is encounted during a multiple VM operation the default behaviour is to stop execution. If this setting is set to '1' this behaviour will be overridden and operations will continue even if an error is encounted.
=item B<human>
Always output numbers in a human readable format rather than the raw form.
Expand Down Expand Up @@ -559,6 +572,10 @@ Please report to https://github.com/hash-bang/VMM when found.
Matt Carter <m@ttcarter.com>
=cut
# }}} POD

package vmm;
our $VERSION = '0.1.1';

# Header {{{
use Config::IniFiles;
Expand Down Expand Up @@ -601,14 +618,14 @@ sub pause {
}

sub error {
# FIXME: This function is pretty basic and needs a user-friendlyness upgrade
my $action = shift;
my $subject = shift;
if (ref($@) eq 'SoapFault') {
say(0, "An error occured: " . $@->detail);
say(0, "ERROR: " . $@->detail);
} else {
say(0, "General fault!");
say(0, "ERROR: General fault!");
}
fatal("Stopping execution. Use -f to force continue.") unless $force;
}
# }}} Flow control

Expand Down Expand Up @@ -871,6 +888,7 @@ our $cycleno = 0; # Offset of the object we are operating on
our $dryrun = $cfg->val($profile, 'dryrun', 0);
our $wait = $cfg->val($profile, 'wait', 0);
our $human = $cfg->val($profile, 'human', '0');
our $force = $cfg->val($profile, 'force', '0');
my $priority = $cfg->val($profile, 'priority', 'low');
my $seperator = $cfg->val($profile, 'seperator', "\t");
my $title, $display, $pool, $datastore;
Expand All @@ -886,6 +904,7 @@ Util::connect();
GetOptions(
# Global options
'dryrun|n' => \$dryrun,
'force|f' => \$force,
'verbose|v+' => \$verbose,
'wait|w=i' => \$wait,

Expand Down Expand Up @@ -926,7 +945,7 @@ if ($cmd eq 'clone') { # CLONE {{{
$poolref = $sourceref->resourcePool;
}
if ($datastore) { # User is specifying pattern of datastores
foreach (split(',', $datastore)) {
foreach (split('\s*,\s*', $datastore)) {
say(2, "Loading datastore '$_'");
my $dsref = getview('Datastore', $_) or fatal("Unknown datastore: '$_'");
push @dsrefs, $dsref;
Expand All @@ -935,7 +954,7 @@ if ($cmd eq 'clone') { # CLONE {{{
push @dsrefs, getview('Mor', $sourceref->datastore->[0]);
}

say(1, "Clone source $source (x $count clones, across " . scalar(@dsrefs) ." datastores)");
say(1, "Clone source $source (x $count clones, across " . scalar(@dsrefs) ." datastores)") if $count > 1;

while ($cycleno < $count) {
pause() if $cycleno++;
Expand All @@ -944,7 +963,7 @@ if ($cmd eq 'clone') { # CLONE {{{
$target++;
}

say(1, "Cloning $source -> $target (clone #$cycleno, DS " . $dsrefs[$dsno]->name . ")");
say(1, "Cloning $source -> $target (clone #$cycleno, DS = " . $dsrefs[$dsno]->name . ", Pool = " . $poolref->name . " )");

eval {
$sourceref->CloneVM(
Expand Down Expand Up @@ -974,14 +993,13 @@ if ($cmd eq 'clone') { # CLONE {{{
my $displaytype = 0; # 0 - Plain list, 1 - CSV list, 2 - Perl string eval
if ($display =~ /\$/) { # Contains a '$' - assume Perl eval
$displaytype = 2;
} elsif ($display) {
@displaylist = split(',',$display); # Split 'display' CSV option
print Dumper(@displaylist);
} elsif ($display) { # Treat as CSV
@displaylist = split(',',$display);
$displaytype = 1;
}

if ($title) { # Output a title
$title =~ s/,/$seperator/g;
$title =~ s/\s*,\s*/$seperator/g;
say(0, $title);
}

Expand All @@ -1001,7 +1019,7 @@ if ($cmd eq 'clone') { # CLONE {{{
foreach $type (@displaylist) {
$line .= ($info{$type} ? $info{$type} : 'UNKNOWN') . $seperator;
}
$line = substr($line, 0, 0 - length($seperator)); # Stip last $seperator
$line = substr($line, 0, 0 - length($seperator)); # Strip last $seperator
print "$line\n";
}
} elsif ($displaytype == 2) { # Perl eval
Expand Down Expand Up @@ -1057,22 +1075,23 @@ if ($cmd eq 'clone') { # CLONE {{{
}
# }}} HOST
} elsif ($cmd eq 'migrate') { # MIGRATE {{{
my $poolref, $priorityref;
fatal("Invalid priority: '$priority'. Choose from: low, normal, high") unless $priority =~ /^low|normal|high$/;
my $priorityref = VirtualMachineMovePriority->new($priority);
my $priorityref = VirtualMachineMovePriority->new($priority) or fatal('Invalid internal priority state. Probably an issue with the VMware libraries');

my $targethost = pop;
fatal('No destination host specified') unless $targethost;

$poolref = getview('ResourcePool', $pool) or fatal("Invalid destination pool: $pool") if ($pool);
my $poolref = getview('ResourcePool', $pool) or fatal("Invalid destination pool: $pool") if ($pool);
my $targethostref = getview('HostSystem', $targethost) or fatal("Invalid destination host: $targethost");

my @vms = multiglob([list('VirtualMachine')], \@ARGV);
fatal('No VM\'s match the given pattern') unless scalar(@vms);

say(2, "Migrating to $targethost with $priority priority");
my $success; # Last operation worked
foreach $vm (@vms) {
pause() if $cycleno++;
pause() if $cycleno++ and $success;
$success = 0;
say(1, "Migrate $vm -> $targethost");

my $vmref = getview('VirtualMachine', $vm) or fatal("Invalid VM: $vm");
Expand All @@ -1086,29 +1105,37 @@ if ($cmd eq 'clone') { # CLONE {{{
state => $vm->runtime->powerState->val,
) unless $dryrun;
};
error('migrate', $vm, @_) if @_;
if (@_) { # Had errors
error('migrate', $vm, @_) ;
last unless $force;
} else {
$success = 1;
}
}
# }}} MIGRATE
} elsif ($cmd eq 'move') { # MOVE {{{
my $poolref, $success;
my $targetds = pop;
fatal('No destination datasore specified') unless $targetds;

my $targetdsref = getview('Datastore', $targetds) or fatal("Invalid destination datastore: $targetds");

$poolref = getview('ResourcePool', $pool) or fatal("Invalid destination pool: $pool") if ($pool);
my $poolref;
if ($pool) {
$poolref = getview('ResourcePool', $pool) or fatal("Invalid destination pool: $pool");
}

my @vms = multiglob([list('VirtualMachine')], \@ARGV);
fatal('No VM\'s match the given pattern') unless scalar(@vms);

say(2, "Moving to datastore '$targetds'");
say(2, "Moving " . scalar(@vms) . " VM's to datastore '$targetds'");

my $success; # Last operation worked
foreach $vm (@vms) {
pause() if $cycleno++ and $success;
$success = 0;
say(1, "Move $vm -> $targetds");

my $vmref = getview('VirtualMachine', $vm) or fatal("Invalid VM: $vm");
fatal("Invalid virtual machine: $vm") unless $vmref;
if (getview('Mor', $vmref->datastore->[0])->name eq $targetdsref->name) {
say(0, "$vm is already located in datastore '$targetds'");
next;
Expand All @@ -1124,8 +1151,12 @@ if ($cmd eq 'clone') { # CLONE {{{
pool => $vmpoolref,
)) unless $dryrun;
};
error('move', $vm, @_) if @_;
$success = 1;
if (@_) { # Had errors
error('move', $vm, @_);
last unless $force;
} else {
$success = 1;
}
}
# }}} MOVE
} elsif ($cmd eq 'state') { # STATE {{{
Expand All @@ -1135,13 +1166,16 @@ if ($cmd eq 'clone') { # CLONE {{{

my @vms = multiglob([list('VirtualMachine')], \@ARGV);
fatal('No VM\'s match the given pattern') unless scalar(@vms);
my $success;
foreach $vm (@vms) {
pause() if $cycleno++;
pause() if $cycleno++ and $success;
$success = 0;
my $vmref = getview('VirtualMachine', $vm) or fatal("Invalid virtual machine: $vm");
if ($state eq 'on') {
if ($vmref->runtime->powerState->val eq 'poweredOff') {
say(1, "Powering on $vm");
$vmref->PowerOnVM() unless $dryrun;
$success = 1;
} else {
say(0, "$vm needs to be powered off before it can be turned on (VMware recognises the power state as '" . $vm->runtime->powerState->val . "')!");
}
Expand All @@ -1151,21 +1185,27 @@ if ($cmd eq 'clone') { # CLONE {{{
} elsif ($state eq 'reboot') {
say(1, "Rebooting $vm");
$vmref->ResetVM() unless $dryrun;
$success = 1;
} elsif ($state eq 'suspend') {
say(1, "Suspending $vm");
$vmref->SuspendVM() unless $dryrun;
$success = 1;
} elsif ($state eq 'off') {
say(1, "Powering off $vm");
$vmref->PowerOffVM() unless $dryrun;
$success = 1;
} elsif ($state eq 'standby') {
say(1, "Standby $vm");
$vmref->StandbyGuest() unless $dryrun;
$success = 1;
} elsif ($state eq 'shutdown') {
say(1, "Shutting down $vm");
$vmref->ShutdownGuest() unless $dryrun;
$success = 1;
} elsif ($state eq 'restart') {
say(1, "Restarting $vm");
$vmref->RebootGuest() unless $dryrun;
$success = 1;
}
}
}
Expand All @@ -1178,10 +1218,11 @@ if ($cmd eq 'clone') { # CLONE {{{
fatal('You must specify what item to set. Choose from: cpu, mem, all') unless $item;
fatal('Invalid item selection. Choose from: cpu, mem, all') unless $item =~ /^cpu|mem|all$/;

my @pools = multiglob([list('ResourcePool')], \@ARGV);
fatal('No pools match the given pattern') unless scalar(@pools);
my @pools = multiglob([list('ResourcePool')], \@ARGV) or fatal('No pools match the given pattern');
my $success;
foreach $pool (@pools) {
pause() if $cycleno++;
pause() if $cycleno++ and $success;
$success = 0;
my $poolref = getview('ResourcePool', $pool);
fatal("Invalid resource pool: $pool") unless $poolref;

Expand All @@ -1199,12 +1240,13 @@ if ($cmd eq 'clone') { # CLONE {{{
}
my $config = ResourceConfigSpec->new(cpuAllocation=>$cpualloc, memoryAllocation=>$memalloc);
$poolref->UpdateConfig(config => $config) unless $dryrun;
$success = 1;
}
# }}} SETPOOL
} elsif ($cmd eq 'show') { # SHOW {{{
my $rawtype = $type = shift;
unless ($type = translate($type)) { # First arg is not a recognised type - assume the user is just listing VMs
unshift @ARGV, $rawtype; # Add back to pattern list
unless ($type = translate($type)) { # First arg is not a recognised type - assume the user ommitted the prefix 'vm'
unshift @ARGV, $rawtype; # Add the incorrect type back to pattern list
$type = 'VirtualMachine';
say(1, "No specific type requested. Assuming: 'vm'");
}
Expand All @@ -1217,10 +1259,10 @@ if ($cmd eq 'clone') { # CLONE {{{
}
# }}} SHOW
} elsif ($cmd eq 'snapshot') { # SNAPSHOT {{{
my $success;
my @vms = multiglob([list('VirtualMachine')], \@ARGV);
fatal('No VM\'s match the given pattern') unless scalar(@vms);
my @vms = multiglob([list('VirtualMachine')], \@ARGV) or fatal('No VM\'s match the given pattern');
$title = $title || time;

my $success;
foreach $vm (@vms) {
say(1, "Snapshot '$vm'");
pause() if $cycleno++ and $success;
Expand Down Expand Up @@ -1258,3 +1300,4 @@ __DATA__
[GLOBAL]
verbose = 1
dryrun = 0
force = 0

0 comments on commit f891865

Please sign in to comment.