1#!/usr/bin/perl -w 2 3# A perl object to provide a simple, unified method of handling some 4# VMware Server VM management functions using the perl and VIX API's. 5# Copyright Brad Henry <brad@samba.org> 2006 6# Released under the GNU GPL version 3 or later. 7 8# VMware Perl API 9use VMware::VmPerl; 10use VMware::VmPerl::VM; 11use VMware::VmPerl::ConnectParams; 12 13# VMware C bindings 14use VMware::Vix::Simple; 15use VMware::Vix::API::Constants; 16 17# Create a class to abstract from the Vix and VMPerl APIs. 18{ package VMHost; 19 my $perl_vm = VMware::VmPerl::VM::new(); 20 my $perl_vm_credentials; 21 my $vix_vm; 22 my $vix_vm_host; 23 24 my $err_code = 0; 25 my $err_str = ""; 26 27 my $hostname; 28 my $port; 29 my $username; 30 my $password; 31 my $vm_cfg_path; 32 my $guest_admin_username; 33 my $guest_admin_password; 34 35 sub error { 36 my $old_err_code = $err_code; 37 my $old_err_str = $err_str; 38 $err_code = 0; 39 $err_str = ""; 40 return ($old_err_code, $old_err_str); 41 } 42 43 # Power on the guest if it isn't already running. 44 # Returns 0 when the guest is already running, and 45 # if not, it waits until it is started. 46 sub start_guest { 47 my $vm_power_state = $perl_vm->get_execution_state(); 48 if (!defined($vm_power_state)) { 49 ($err_code, $err_str) = $perl_vm->get_last_error(); 50 return ($err_code); 51 } 52 if ($vm_power_state == VMware::VmPerl::VM_EXECUTION_STATE_OFF 53 || $vm_power_state == 54 VMware::VmPerl::VM_EXECUTION_STATE_SUSPENDED) 55 { 56 if (!$perl_vm->start()) { 57 ($err_code, $err_str) = 58 $perl_vm->get_last_error(); 59 return ($err_code); 60 } 61 while ($perl_vm->get_tools_last_active() == 0) { 62 sleep(60); 63 } 64 } 65 return ($err_code); 66 } 67 68 sub host_connect { 69 # When called as a method, the first parameter passed is the 70 # name of the method. Called locally, this function will lose 71 # the first parameter. 72 shift @_; 73 ($hostname, $port, $username, $password, $vm_cfg_path, 74 $guest_admin_username, $guest_admin_password) = @_; 75 76 # Connect to host using vmperl api. 77 $perl_vm_credentials = 78 VMware::VmPerl::ConnectParams::new($hostname, $port, 79 $username, $password); 80 if (!$perl_vm->connect($perl_vm_credentials, $vm_cfg_path)) { 81 ($err_code, $err_str) = $perl_vm->get_last_error(); 82 undef $perl_vm; 83 return ($err_code); 84 } 85 86 # Connect to host using vix api. 87 ($err_code, $vix_vm_host) = 88 VMware::Vix::Simple::HostConnect( 89 VMware::Vix::Simple::VIX_API_VERSION, 90 VMware::Vix::Simple::VIX_SERVICEPROVIDER_VMWARE_SERVER, 91 $hostname, $port, $username, $password, 92 0, VMware::Vix::Simple::VIX_INVALID_HANDLE); 93 if ($err_code != VMware::Vix::Simple::VIX_OK) { 94 $err_str = 95 VMware::Vix::Simple::GetErrorText($err_code); 96 undef $perl_vm; 97 undef $vix_vm; 98 undef $vix_vm_host; 99 return ($err_code); 100 } 101 102 # Power on our guest os if it isn't already running. 103 $err_code = start_guest(); 104 if ($err_code != 0) { 105 my $old_err_str = $err_str; 106 $err_str = "Starting guest power after connect " . 107 "failed: " . $old_err_str; 108 undef $perl_vm; 109 undef $vix_vm; 110 undef $vix_vm_host; 111 return ($err_code); 112 } 113 114 # Open VM. 115 ($err_code, $vix_vm) = 116 VMware::Vix::Simple::VMOpen($vix_vm_host, $vm_cfg_path); 117 if ($err_code != VMware::Vix::Simple::VIX_OK) { 118 $err_str = 119 VMware::Vix::Simple::GetErrorText($err_code); 120 undef $perl_vm; 121 undef $vix_vm; 122 undef $vix_vm_host; 123 return ($err_code); 124 } 125 126 # Login to $vix_vm guest OS. 127 $err_code = VMware::Vix::Simple::VMLoginInGuest($vix_vm, 128 $guest_admin_username, $guest_admin_password, 129 0); 130 if ($err_code != VMware::Vix::Simple::VIX_OK) { 131 $err_str = 132 VMware::Vix::Simple::GetErrorText($err_code); 133 undef $perl_vm; 134 undef $vix_vm; 135 undef $vix_vm_host; 136 return ($err_code); 137 } 138 return ($err_code); 139 } 140 141 sub host_disconnect { 142 undef $perl_vm; 143 144 $perl_vm = VMware::VmPerl::VM::new(); 145 if (!$perl_vm) { 146 $err_code = 1; 147 $err_str = "Error creating new VmPerl object"; 148 } 149 150 undef $vix_vm; 151 VMware::Vix::Simple::HostDisconnect($vix_vm_host); 152 VMware::Vix::Simple::ReleaseHandle($vix_vm_host); 153 return ($err_code); 154 } 155 156 sub host_reconnect { 157 $err_code = host_disconnect(); 158 if ($err_code != 0) { 159 my $old_err_str = $err_str; 160 $err_str = "Disconnecting from host failed: " . 161 $old_err_str; 162 return ($err_code); 163 } 164 165 $err_code = host_connect(NULL, $hostname, $port, $username, 166 $password, $vm_cfg_path, $guest_admin_username, 167 $guest_admin_password); 168 if ($err_code != 0) { 169 my $old_err_str = $err_str; 170 $err_str = "Re-connecting to host failed: " . 171 $old_err_str; 172 return ($err_code); 173 } 174 return ($err_code); 175 } 176 177 sub create_snapshot { 178 my $snapshot; 179 180 ($err_code, $snapshot) = 181 VMware::Vix::Simple::VMCreateSnapshot($vix_vm, 182 "Snapshot", "Created by vm_setup.pl", 0, 183 VMware::Vix::Simple::VIX_INVALID_HANDLE); 184 185 VMware::Vix::Simple::ReleaseHandle($snapshot); 186 187 if ($err_code != VMware::Vix::Simple::VIX_OK) { 188 $err_str = 189 VMware::Vix::Simple::GetErrorText($err_code); 190 return $err_code; 191 } 192 193 $err_code = host_reconnect(); 194 if ($err_code != 0) { 195 my $old_err_str = $err_str; 196 $err_str = "Reconnecting to host after creating " . 197 "snapshot: " . $old_err_str; 198 return ($err_code); 199 } 200 return ($err_code); 201 } 202 203 sub revert_snapshot { 204 # Because of problems with VMRevertToSnapshot(), we have to 205 # rely on the guest having set 'Revert to Snapshot' following 206 # a power-off event. 207 $err_code = VMware::Vix::Simple::VMPowerOff($vix_vm, 0); 208 if ($err_code != VMware::Vix::Simple::VIX_OK) { 209 $err_str = 210 VMware::Vix::Simple::GetErrorText($err_code); 211 return $err_code; 212 } 213 214 # host_reconnect() will power-on a guest in a non-running state. 215 $err_code = host_reconnect(); 216 if ($err_code != 0) { 217 my $old_err_str = $err_str; 218 $err_str = "Reconnecting to host after reverting " . 219 "snapshot: " . $old_err_str; 220 return ($err_code); 221 } 222 return ($err_code); 223 } 224 225 # $dest_path must exist. It doesn't get created. 226 sub copy_files_to_guest { 227 shift @_; 228 my (%files) = @_; 229 230 my $src_file; 231 my $dest_file; 232 233 foreach $src_file (keys(%files)) { 234 $dest_file = $files{$src_file}; 235 $err_code = 236 VMware::Vix::Simple::VMCopyFileFromHostToGuest( 237 $vix_vm, $src_file, $dest_file, 0, 238 VMware::Vix::Simple::VIX_INVALID_HANDLE); 239 if ($err_code != VMware::Vix::Simple::VIX_OK) { 240 $err_str = "Copying $src_file: " . 241 VMware::Vix::Simple::GetErrorText( 242 $err_code); 243 return $err_code; 244 } 245 } 246 return $err_code; 247 } 248 249 sub copy_to_guest { 250 # Read parameters $src_path, $dest_path. 251 shift @_; 252 my ($src_path, $dest_dir) = @_; 253 254 my $len = length($dest_dir); 255 my $idx = rindex($dest_dir, '\\'); 256 if ($idx != ($len - 1)) { 257 $err_code = -1; 258 $err_str = "Destination $dest_dir must be a " . 259 "directory path"; 260 return ($err_code); 261 } 262 263 # Create the directory $dest_path on the guest VM filesystem. 264 my $cmd = "cmd.exe "; 265 my $cmd_args = "/C MKDIR " . $dest_dir; 266 $err_code = run_on_guest(NULL, $cmd, $cmd_args); 267 if ( $err_code != 0) { 268 my $old_err_str = $err_str; 269 $err_str = "Creating directory $dest_dir on host: " . 270 $old_err_str; 271 return ($err_code); 272 } 273 274 # If $src_filepath specifies a file, create it in $dest_path 275 # and keep the same name. 276 # If $src_path is a directory, create the files it contains in 277 # $dest_path, keeping the same names. 278 $len = length($src_path); 279 my %files; 280 $idx = rindex($src_path, '/'); 281 if ($idx == ($len - 1)) { 282 # $src_path is a directory. 283 if (!opendir (DIR_HANDLE, $src_path)) { 284 $err_code = -1; 285 $err_str = "Error opening directory $src_path"; 286 return $err_code; 287 } 288 289 foreach $file (readdir DIR_HANDLE) { 290 my $src_file = $src_path . $file; 291 292 if (!opendir(DIR_HANDLE2, $src_file)) { 293 # We aren't interested in subdirs. 294 my $dest_path = $dest_dir . $file; 295 $files{$src_file} = $dest_path; 296 } else { 297 closedir(DIR_HANDLE2); 298 } 299 } 300 } else { 301 # Strip if preceeding path from $src_path. 302 my $src_file = substr($src_path, ($idx + 1), $len); 303 my $dest_path = $dest_dir . $src_file; 304 305 # Add $src_path => $dest_path to %files. 306 $files{$src_path} = $dest_path; 307 } 308 309 $err_code = copy_files_to_guest(NULL, %files); 310 if ($err_code != 0) { 311 my $old_err_str = $err_str; 312 $err_str = "Copying files to host after " . 313 "populating %files: " . $old_err_str; 314 return ($err_code); 315 } 316 return ($err_code); 317 } 318 319 sub run_on_guest { 320 # Read parameters $cmd, $cmd_args. 321 shift @_; 322 my ($cmd, $cmd_args) = @_; 323 324 $err_code = VMware::Vix::Simple::VMRunProgramInGuest($vix_vm, 325 $cmd, $cmd_args, 0, 326 VMware::Vix::Simple::VIX_INVALID_HANDLE); 327 if ($err_code != VMware::Vix::Simple::VIX_OK) { 328 $err_str = VMware::Vix::Simple::GetErrorText( 329 $err_code); 330 return ($err_code); 331 } 332 333 return ($err_code); 334 } 335 336 sub get_guest_ip { 337 my $guest_ip = $perl_vm->get_guest_info('ip'); 338 339 if (!defined($guest_ip)) { 340 ($err_code, $err_str) = $perl_vm->get_last_error(); 341 return NULL; 342 } 343 344 if (!($guest_ip)) { 345 $err_code = 1; 346 $err_str = "Guest did not set the 'ip' variable"; 347 return NULL; 348 } 349 return $guest_ip; 350 } 351 352 sub DESTROY { 353 host_disconnect(); 354 undef $perl_vm; 355 undef $vix_vm_host; 356 } 357} 358 359return TRUE; 360