You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@vcl.apache.org by Jeffrey Wisman <je...@csueastbay.edu> on 2010/03/10 06:54:14 UTC
Re: Yet Another VMware ESX Provisioning Module
Sean,
We're very interested in using this module. Can you provide a step-by-step
instruction on how to make this available as a provisioning module? We're
running ESX 4.0 on our host server. Do we place the module into
/usr/local/vcl/lib/VCL/Module/Provisioning ? What is the next step?
Getting into the database somehow? Please let me know.
Thanks,
Jeff
On Wed, Feb 24, 2010 at 1:16 PM, Sean Dilda <se...@duke.edu> wrote:
> I've written a provisioning module for VCL that we use here at Duke that
> I'd like to share for possible inclusion in VCL. The module is based off
> VCL 2.1.
>
> This module is designed to work against VirtualCenter/vCenter, but should
> be able to work directly with an ESX host as well. It works a little
> differently from the existing ESX modules in that it uses the VMware perl
> API directly instead of calling the helper apps. In addition, it also
> greatly reduces the amount of disk space and deployment time necessary.
>
> Instead of cloning the VM for deployment, this module uses ESX to create a
> snapshot of the golden image disk, and uses that for the deployed VM. This
> means the only disk space used for the deployed VM is the differences
> between it and the master. Likewise, no time is needed to copy all the
> bits, so creating the VM takes seconds instead of minutes.
>
>
> Internally we call the module 'esxduke' to distinguish it from the existing
> ESX module. Going forward is there a better name for this module? Also,
> what should be done from here to have this included in VCL?
>
>
> Thanks,
>
>
> Sean
>
> #!/usr/bin/perl -w
>
> ###############################################################################
> # Copyright 2010 Duke University and Apache Software Foundation
> #
> # Licensed to the Apache Software Foundation (ASF) under one or more
> # contributor license agreements. See the NOTICE file distributed with
> # this work for additional information regarding copyright ownership.
> # The ASF licenses this file to You under the Apache License, Version 2.0
> # (the "License"); you may not use this file except in compliance with
> # the License. You may obtain a copy of the License at
> #
> # http://www.apache.org/licenses/LICENSE-2.0
> #
> # Unless required by applicable law or agreed to in writing, software
> # distributed under the License is distributed on an "AS IS" BASIS,
> # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> # See the License for the specific language governing permissions and
> # limitations under the License.
>
> ###############################################################################
>
> package VCL::Module::Provisioning::esxduke;
>
> # Configure inheritance
> use base qw(VCL::Module::Provisioning);
>
> # Specify the version of this module
> our $VERSION = '1.00';
>
> # Specify the version of Perl to use
> use 5.008000;
>
> use strict;
> use warnings;
> use diagnostics;
>
> use VCL::utils;
> use Fcntl qw(:DEFAULT :flock);
>
> # Use VMware's perl libraries
> use VMware::VIRuntime;
> use VMware::VILib;
> use VMware::VIExt;
>
>
>
>
> ##############################################################################
>
> =head1 CLASS ATTRIBUTES
>
> =cut
>
> =head2 %VMWARE_CONFIG
>
> Data type : hash
> Description : %VMWARE_CONFIG is a hash containing the general VMWARE
> configuration
> for the management node this code is running on. Since the
> data is
> the same for every instance of the
> VMWARE class, a class attribute
> is used and the hash is shared among
> all instances. This also
> means that the data only needs to be
> retrieved from the database
> once.
>
> =cut
>
> #my %VMWARE_CONFIG;
>
>
> ##############################################################################
>
> =head1 OBJECT METHODS
>
> =cut
>
>
> #/////////////////////////////////////////////////////////////////////////////
>
> =head2 initialize
>
> Parameters :
> Returns :
> Description :
>
> =cut
>
> # A provisioning object is created for each request. So this initialize is
> done once per request.
> sub initialize {
> my $self = shift;
> if (!defined ($self) ) {
> return 0;
> }
> notify($ERRORS{'DEBUG'}, 0, "Duke vmware ESX module initialized");
> return 1;
> } ## end sub initialize
>
> sub _checkConnection{
> my $self = shift;
> if (!$VMware::VICommon::is_connected) {
> # Login to the ESX/VC server
> my $retval;
> eval {
> local $SIG{__DIE__}; # We don't want vcld's die
> handler getting this
> $retval = Util::connect('https://' .
> $self->data->get_vmhost_hostname . "/sdk/webService",
> $self->data->get_vmhost_profile_username(),
> $self->data->get_vmhost_profile_password());
> };
> if ($@ || !$retval) {
> notify($ERRORS{'CRITICAL'}, 0, "Could not login to "
> . $self->data->get_vmhost_hostname . ": " . $@);
> return 0;
> }
> }
> return 1;
> }
>
>
> #/////////////////////////////////////////////////////////////////////////////
>
> =head2 provision
>
> Parameters : hash
> Returns : 1(success) or 0(failure)
> Description : loads virtual machine with requested image
>
> =cut
>
> sub load {
> my $self = shift;
>
> #check to make sure this call is for the esx module
> if (ref($self) !~ /esxduke/i) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
> function, it must be called as a class method");
> return 0;
> }
>
> my $request_data = shift;
> my ($package, $filename, $line, $sub) = caller(0);
> notify($ERRORS{'DEBUG'}, 0,
> "****************************************************");
>
> # get various useful vars from the database
> my $request_id = $self->data->get_request_id;
> my $reservation_id = $self->data->get_reservation_id;
> my $vmhost_hostname = $self->data->get_vmhost_hostname;
> my $image_name = $self->data->get_image_name;
> my $computer_shortname = $self->data->get_computer_short_name;
> my $vmclient_computerid = $self->data->get_computer_id;
> my $vmclient_imageminram = $self->data->get_image_minram;
> my $image_os_name = $self->data->get_image_os_name;
> my $image_os_type = $self->data->get_image_os_type;
> my $image_identity = $self->data->get_image_identity;
> my $user_name = $self->data->get_user_login_id . "@" .
> $self->data->get_user_affiliation_name;
>
> my $virtualswitch0 =
> $self->data->get_vmhost_profile_virtualswitch0;
> my $virtualswitch1 =
> $self->data->get_vmhost_profile_virtualswitch1;
> my $vmclient_eth0MAC = $self->data->get_computer_eth0_mac_address;
> my $vmclient_eth1MAC = $self->data->get_computer_eth1_mac_address;
> my $vmclient_OSname = $self->data->get_image_os_name;
>
> my $vmhost_username = $self->data->get_vmhost_profile_username();
> my $vmhost_password = $self->data->get_vmhost_profile_password();
>
> $vmhost_hostname =~ /([-_a-zA-Z0-9]*)(\.?)/;
> my $vmhost_shortname = $1;
>
> $self->_checkConnection();
>
> notify($ERRORS{'OK'}, 0, "Entered ESX Duke module, loading
> $image_name on $computer_shortname (on $vmhost_hostname) for $user_name
> (reservation $reservation_id)");
>
> my $newVm;
> my $newVmPath;
> my $task;
> my $vmView = $self->_getvmView(0);
> my $imageDatastore;
> my $datacenter;
>
> my $fileManager = VIExt::get_file_manager();
>
> # These should be somehow configurable in the future
> my $resourcePoolName = "VCL-TEST";
> my $folderName = "VCL-TEST";
> my $datastoreName = "VCL-TEST-01";
>
> if(open(CONF, "/etc/vcl/vcld.conf")){
> my @conf=<CONF>;
> close(CONF);
> foreach $line (@conf) {
> #resourcePool
> if($line =~ /^RESOURCEPOOLNAME=([\S]*)/){
> chomp($line);
> $resourcePoolName=$1;
> }
> #folderName
> if($line =~ /^FOLDERNAME=([\S]*)/){
> chomp($line);
> $folderName=$1;
> }
> #datastoreName
> if($line =~ /^DATASTORENAME=([\S]*)/){
> chomp($line);
> $datastoreName=$1;
> }
> }
> }
>
> # Get some info on our base image
> my $imageView = $self->_getimageView();
> if (! $imageView) {
> return 0;
> }
>
> my $parentView = Vim::get_view(mo_ref => $imageView->{parent});
> while (ref($parentView) ne 'Datacenter') {
> $parentView = Vim::get_view(mo_ref =>
> $parentView->{parent});
> }
> $datacenter = $parentView;
>
> $imageDatastore = Vim::get_view(mo_ref =>
> @{$imageView->{datastore}}[0]);
>
> my $resourcePool = Vim::find_entity_view(view_type =>
> 'ResourcePool', filter => {'name' => $resourcePoolName}, begin_entity =>
> $datacenter);
> if (! defined($resourcePool)) {
> notify($ERRORS{'WARNING'}, 0, "ERROR: esxduke->load could
> not find resource pool $resourcePoolName");
> return 0;
> }
> my $folder = Vim::find_entity_view(view_type => 'Folder', filter =>
> {'name' => $folderName}, begin_entity => $datacenter);
> if (! defined($folder)) {
> notify($ERRORS{'WARNING'}, 0, "ERROR: esxduke->load could
> not find folder $folderName");
> return 0;
> }
>
> if ($vmView) {
> notify($ERRORS{'DEBUG'}, 0, "Removing old copy of
> $computer_shortname");
> if (!$self->_removeOldVM()) {
> return 0;
> }
> }
>
> # Copy file down
> my $service = Vim::get_vim_service();
> my ($mode, $datacenter_foo, $datastore, $filepath) =
> VIExt::parse_remote_path($imageView->{config}->{files}->{vmPathName});
> #my ($mode, $datacenter_foo, $datastore, $filepath) =
> VIExt::parse_remote_path($imageView->{config}->{files}->{vmPathName});
> $filepath =~ m/(.*)\/.*/;
> my $imageDir = $1;
> my $resp = VIExt::http_get_file($mode, $filepath, $datastore,
> $datacenter->{name});
> if (!defined($resp) || !$resp->is_success) {
> notify($ERRORS{'WARNING'}, 0, "ERROR: esxduke->load could
> not get config for $image_name");
> return 0;
> }
> # Edit file
> my $newvmx = $resp->content;
> $newvmx =~ s/^displayName = .*$/displayName =
> \"$computer_shortname\"/gm;
> $newvmx =~ s/^extendedConfigFile = .*$/extendedConfigFile =
> \"$computer_shortname.vmxf\"/gm;
> $newvmx =~ s/^scsi0:0.filename = .*$/scsi0:0.filename = \"FOO\"/gm;
> $newvmx =~ s/^nvram = .*$/nvram = \"$computer_shortname.nvram\"/gm;
>
> $newvmx =~ s/^uuid.location = .*$//gm;
> $newvmx =~ s/^uuid.bios = .*$//gm;
> $newvmx =~ s/^sched.swap.derivedName = .*$//gm;
> $newvmx =~ s/^sched.mem.max = .*$//gm;
> $newvmx =~ s/^annotation = .*$//gm;
> my $timeStr = localtime;
> $newvmx .= "annotation = \"$image_name created for $user_name at
> $timeStr\"\n";
>
> $newvmx =~ s/^ethernet0.networkName = .*$/ethernet0.networkName =
> \"$virtualswitch0\"/gm;
> $newvmx =~ s/^ethernet1.networkName = .*$/ethernet1.networkName =
> \"$virtualswitch1\"/gm;
>
> if (!$VMWARE_MAC_ETH0_GENERATED && defined($vmclient_eth0MAC)) {
> # Make sure the address type is set right and we have a
> .address line
> if ($newvmx !~ /^ethernet0.addressType = \"static\"$/m) {
> $newvmx .= "ethernet0.addressType = \"static\"\n";
> $newvmx .= "ethernet0.address = \"XXX\"\n";
> }
> $newvmx =~ s/^ethernet0.address = .*$/ethernet0.address =
> \"$vmclient_eth0MAC\"/gm;
> } else {
> $newvmx =~ s/^ethernet0.addressType =
> \"static\"$/ethernet0.addressType = \"generated\"/gm;
> }
> if (!$VMWARE_MAC_ETH1_GENERATED && defined($vmclient_eth1MAC)) {
> # Make sure the address type is set right and we have a
> .address line
> if ($newvmx !~ /^ethernet1.addressType = \"static\"$/m) {
> $newvmx .= "ethernet1.addressType = \"static\"\n";
> $newvmx .= "ethernet1.address = \"XXX\"\n";
> }
> $newvmx =~ s/^ethernet1.address = .*$/ethernet1.address =
> \"$vmclient_eth1MAC\"/gm;
> } else {
> $newvmx =~ s/^ethernet1.addressType =
> \"static\"$/ethernet1.addressType = \"generated\"/gm;
> }
>
> my $imageDSUUID;
> if (defined($imageDatastore->{vmfs}->{uuid})) {
> $imageDSUUID = $imageDatastore->{vmfs}->{uuid};
> } else {
> # Use the name if its NFS, not preferred, but I don't know
> how to get the UUID for an NFS datastore
> $imageDSUUID = $imageDatastore->info->name;
> }
>
> # This is ugly, but I don't know a better way to make sure I catch
> all the disks. If anyone has a better way, please let me know
> my @vmxlines = split(/\n/, $newvmx);
> for $line (@vmxlines) {
> # Make sure the scsi filename isn't an absolute path before
> we make it an absolute path
> if ($line !~ /scsi\d:\d{1,2}.fileName = "\/.*"/) {
> $line =~ s/scsi(\d:\d{1,2}).fileName =
> "(.*)"/scsi$1.fileName = "\/vmfs\/volumes\/$imageDSUUID\/$imageDir\/$2"/;
> }
> }
> $newvmx = join("\n", @vmxlines) . "\n";
>
> # Upload
> # Need to make sure this directory doesn't already exist...
> _deleteDirectory($datastoreName, $computer_shortname, $datacenter);
> eval {
> $fileManager->MakeDirectory(name => "[$datastoreName] " .
> $computer_shortname, datacenter => $datacenter);
> };
> if ($@) {
> notify($ERRORS{'WARNING'}, 0, "ERROR: Unable to create
> directory [$datastoreName] " . $computer_shortname . ": " .
> ($@->fault_string));
> return 0;
> }
>
> # Based on VIExt::http_put_file
> my $serviceURI = URI::URL->new($service->{vim_soap}->{url});
> my $userAgent = $service->{vim_soap}->{user_agent};
>
> $newVmPath = $computer_shortname . "/" . $computer_shortname .
> ".vmx";
> my $attempt = 1;
> while ($attempt <= 3) {
> my $req = VIExt::build_http_request("PUT", "folder",
> $serviceURI, $newVmPath, $datastoreName, $datacenter->{name});
> $req->header('Content-Type', 'application/octet-stream');
> $req->header('Content-Length', length($newvmx));
> $req->content($newvmx);
> $resp = $userAgent->request($req);
> if (!$resp || !$resp->is_success) {
> notify($ERRORS{'WARNING'}, 0, "ERROR: Unable to
> upload [$datastoreName] $newVmPath: " . $resp->message . ": " .
> $resp->content);
> $attempt += 1;
> sleep 3;
> next;
> } else {
> last;
> }
> }
>
> if (!$resp || !$resp->is_success) {
> return 0;
> }
> if ($attempt > 1) {
> notify($ERRORS{'OK'}, 0, "Uploaded new vmx file on attempt
> number $attempt");
> }
>
> $newVmPath = "[$datastoreName]" . " $newVmPath";
>
> $attempt = 1;
> my $result;
> while ($attempt <= 3) {
> $task = $folder->RegisterVM_Task(path => $newVmPath, name =>
> $computer_shortname, asTemplate => "false", pool => $resourcePool);
> $result = _checkTask($task, "registering
> $computer_shortname");
> if (!$result) {
> $attempt += 1;
> sleep 3;
> next;
> } else {
> last;
> }
> }
>
> if (!$resp) {
> return 0;
> }
> if ($attempt > 1) {
> notify($ERRORS{'OK'}, 0, "Registered VM on attempt number
> $attempt");
> }
>
> $vmView = $self->_getvmView();
>
> $task = $vmView->CreateSnapshot_Task(name => 'linked clone from
> vcl-image-' . $self->data->get_image_name, memory => "false", quiesce =>
> "true");
> if (!_checkTask($task, "creating snapshot of $computer_shortname"))
> {
> return 0;
> }
>
> if ($vmclient_OSname =~ /winxp/) {
> # I am hard coding this for now... I am sure we can pass it
> in or read it from the db
> _customizeVm('vcl-xp', $computer_shortname , $vmView);
> }
>
> $task = $vmView->PowerOnVM_Task();
> if (!_checkTask($task, "powering on $computer_shortname")) {
> return 0;
> }
>
>
> #Set some variable
> my $wait_loops = 0;
> my $arpstatus = 0;
> my $client_ip;
>
> if ($VMWARE_MAC_ETH0_GENERATED) {
> # allowing vmware to generate the MAC address
> # find out what MAC got assigned
> # find out what IP address is assigned to this MAC
> my $devices = $vmView->config->hardware->device;
> my $mac_addr;
> foreach my $dev (@$devices) {
> next unless ($dev->isa("VirtualEthernetCard"));
> notify($ERRORS{'DEBUG'}, 0, "deviceinfo->summary:
> $dev->deviceinfo->summary");
> notify($ERRORS{'DEBUG'}, 0, "virtualswitch0:
> $virtualswitch0");
> if ($dev->deviceInfo->summary eq $virtualswitch0) {
> $mac_addr = $dev->macAddress;
> last;
> }
> }
> if (!$mac_addr) {
> notify($ERRORS{'WARNING'}, 0, "Failed to find MAC
> address");
> return 0;
> }
> notify($ERRORS{'DEBUG'}, 0, "Queried MAC address is
> $mac_addr");
>
> # Query ARP table for $mac_addr to find the IP (waiting for
> machine to come up if necessary)
> # The DHCP negotiation should add the appropriate ARP entry
> for us
> while (!$arpstatus) {
> my $arpoutput = `arp -n`;
> if ($arpoutput =~
> /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*?$mac_addr/mi) {
> $client_ip = $1;
> $arpstatus = 1;
> notify($ERRORS{'OK'}, 0,
> "$computer_shortname now has ip $client_ip");
> }
> else {
> if ($wait_loops > 24) {
> notify($ERRORS{'WARNING'}, 0,
> "waited acceptable amount of time for dhcp, please check $computer_shortname
> on $vmhost_shortname");
> return 0;
> }
> else {
> $wait_loops++;
> notify($ERRORS{'OK'}, 0, "going to
> sleep 10 seconds, waiting for computer to DHCP. Try $wait_loops");
> sleep 10;
> }
> } ## end else [ if ($arpoutput =~
> /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*?$mac_addr/mi)
> } ## end while (!$arpstatus)
>
>
>
> notify($ERRORS{'OK'}, 0, "Found IP address $client_ip");
>
> # Delete existing entry for $computer_shortname in
> /etc/hosts (if any)
> notify($ERRORS{'OK'}, 0, "Removing old hosts entry");
> my $sedoutput = `sed -i "/.*\\b$computer_shortname\$/d"
> /etc/hosts`;
> notify($ERRORS{'DEBUG'}, 0, $sedoutput);
>
> # Add new entry to /etc/hosts for $computer_shortname
> `echo -e "$client_ip\t$computer_shortname" >> /etc/hosts`;
> } ## end if ($VMWARE_MAC_ETH0_GENERATED)
> else {
> notify($ERRORS{'DEBUG'}, 0, "IP is known for
> $computer_shortname");
> }
> # Start waiting for SSH to come up
> my $sshdstatus = 0;
> $wait_loops = 0;
> my $sshd_status = "off";
> notify($ERRORS{'DEBUG'}, 0, "Waiting for ssh to come up on
> $computer_shortname");
> while (!$sshdstatus) {
> $sshd_status = _sshd_status($computer_shortname,
> $image_name, $image_os_type);
> if ($sshd_status eq "on") {
> $sshdstatus = 1;
> notify($ERRORS{'OK'}, 0, "$computer_shortname now
> has active sshd running");
> }
> else {
> #either sshd is off or N/A, we wait
> if ($wait_loops > 150) {
> notify($ERRORS{'WARNING'}, 0, "waited
> acceptable amount of time for sshd to become active, please check
> $computer_shortname on $vmhost_shortname");
> #need to check power, maybe reboot it. for
> now fail it
> return 0;
> }
> else {
> $wait_loops++;
> # to give post config a chance
> notify($ERRORS{'DEBUG'}, 0, "going to sleep
> 10 seconds, waiting for computer to start SSH. Try $wait_loops");
> sleep 10;
> }
> } # else
> } #while
>
> # Set IP info
> if ($IPCONFIGURATION ne "manualDHCP") {
> #not default setting
> if ($IPCONFIGURATION eq "dynamicDHCP") {
> insertloadlog($reservation_id, $vmclient_computerid,
> "dynamicDHCPaddress", "collecting dynamic IP address for node");
> notify($ERRORS{'DEBUG'}, 0, "Attempting to query
> vmclient for its public IP...");
> #### Sean
> ### Why call getdynamicaddress? It doesn't work. Should we instead just
> write a local method
> #### to get the ip from another means?
> #### Liz
> my $assignedIPaddress =
> getdynamicaddress($computer_shortname, $vmclient_OSname, $image_os_type);
> if ($assignedIPaddress) {
> #update computer table
> notify($ERRORS{'DEBUG'}, 0, " Got dynamic
> address from vmclient, attempting to update database");
> if
> (update_computer_address($vmclient_computerid, $assignedIPaddress)) {
> notify($ERRORS{'DEBUG'}, 0, "
> succesfully updated IPaddress of node $computer_shortname");
> }
> else {
> notify($ERRORS{'CRITICAL'}, 0,
> "could not update dynamic address $assignedIPaddress for $computer_shortname
> $image_name");
> return 0;
> }
> } ## end if ($assignedIPaddress)
> else {
> notify($ERRORS{'CRITICAL'}, 0, "could not
> fetch dynamic address from $computer_shortname $image_name");
> insertloadlog($reservation_id,
> $vmclient_computerid, "failed", "could not collect dynamic IP address for
> node");
> return 0;
> }
> } ## end if ($IPCONFIGURATION eq "dynamicDHCP")
> elsif ($IPCONFIGURATION eq "static") {
> notify($ERRORS{'CRITICAL'}, 0, "STATIC ASSIGNMENT
> NOT SUPPORTED. See vcld.conf");
> return 0;
> #insertloadlog($reservation_id,
> $vmclient_computerid, "staticIPaddress", "setting static IP address for
> node");
> #if (setstaticaddress($computer_shortname,
> $vmclient_OSname, $vmclient_publicIPaddress)) {
> # # good set static address
> #}
> }
> } ## end if ($IPCONFIGURATION ne "manualDHCP")
>
> # Perform post load tasks
> return 1;
> } ## end sub load
>
> #/////////////////////////////////////////////////////////////////////////
>
> =head2 node_status
>
> Parameters : $nodename, $log
> Returns : array of related status checks
> Description : checks on sshd, currentimage
>
> =cut
>
> sub node_status {
> my $self = shift;
>
> my ($package, $filename, $line, $sub) = caller(0);
>
> my $vmpath = 0;
> my $datastorepath = 0;
> my $requestedimagename = 0;
> my $vmhost_type = 0;
> my $vmhost_hostname = 0;
> my $vmhost_imagename = 0;
> my $image_os_type = 0;
> my $vmclient_shortname = 0;
> my $request_forimaging = 0;
> my $identity_keys = 0;
> my $log = 0;
> my $computer_node_name = 0;
>
>
> # Check if subroutine was called as a class method
> if (ref($self) !~ /esxduke/i) {
> notify($ERRORS{'OK'}, 0, "subroutine was called as a
> function");
> if (ref($self) eq 'HASH') {
> $log = $self->{logfile};
> #notify($ERRORS{'DEBUG'}, $log, "self is a hash
> reference");
>
> $vmpath =
> $self->{vmhost}->{vmprofile}->{vmpath};
> $datastorepath =
> $self->{vmhost}->{vmprofile}->{datastorepath};
> $requestedimagename =
> $self->{imagerevision}->{imagename};
> $vmhost_type =
> $self->{vmhost}->{vmprofile}->{vmtype}->{name};
> $vmhost_hostname = $self->{vmhost}->{hostname};
> $vmhost_imagename = $self->{vmhost}->{imagename};
> $image_os_type = $self->{image}->{OS}->{type};
> $computer_node_name = $self->{computer}->{hostname};
> $identity_keys =
> $self->{managementnode}->{keys};
>
> } ## end if (ref($self) eq 'HASH')
> # Check if node_status returned an array ref
> elsif (ref($self) eq 'ARRAY') {
> notify($ERRORS{'DEBUG'}, $log, "self is a array
> reference");
> }
>
> $vmclient_shortname = $1 if ($computer_node_name =~
> /([-_a-zA-Z0-9]*)(\.?)/);
> } ## end if (ref($self) !~ /esx/i)
> else {
>
> # try to contact vm
> # $self->data->get_request_data;
> # get state of vm
> $vmpath =
> $self->data->get_vmhost_profile_vmpath;
> $datastorepath =
> $self->data->get_vmhost_profile_datastore_path;
> $requestedimagename = $self->data->get_image_name;
> $vmhost_type = $self->data->get_vmhost_type;
> $vmhost_hostname = $self->data->get_vmhost_hostname;
> $vmhost_imagename = $self->data->get_vmhost_image_name;
> $image_os_type = $self->data->get_image_os_type;
> $vmclient_shortname = $self->data->get_computer_short_name;
> $request_forimaging = $self->data->get_request_forimaging();
> } ## end else [ if (ref($self) !~ /esx/i)
>
> notify($ERRORS{'OK'}, 0, "Entering node_status, checking status
> of $vmclient_shortname");
> notify($ERRORS{'DEBUG'}, 0, "request_for_imaging:
> $request_forimaging");
> notify($ERRORS{'DEBUG'}, 0, "requeseted image name:
> $requestedimagename");
>
> my ($hostnode, $identity);
>
> # Create a hash to store status components
> my %status;
>
> # Initialize all hash keys here to make sure they're defined
> $status{status} = 0;
> $status{currentimage} = 0;
> $status{ping} = 0;
> $status{ssh} = 0;
> $status{vmstate} = 0; #on or off
> $status{image_match} = 0;
>
> if ($vmhost_type eq "blade") {
> $hostnode = $1 if ($vmhost_hostname =~
> /([-_a-zA-Z0-9]*)(\.?)/);
> $identity = $IDENTITY_bladerhel;
> #if($vm{vmhost}{imagename} =~ /^(rhel|rh3image|rh4image|fc|rhfc)/);
> }
> else {
> #using FQHN
> $hostnode = $vmhost_hostname;
> $identity = $IDENTITY_linux_lab if ($vmhost_imagename =~
> /^(realmrhel)/);
> }
>
> if (!$identity) {
> notify($ERRORS{'CRITICAL'}, 0, "could not set ssh identity
> variable for image $vmhost_imagename type= $vmhost_type host=
> $vmhost_hostname");
> }
>
> # Check if node is pingable
> notify($ERRORS{'DEBUG'}, 0, "checking if $vmclient_shortname is
> pingable");
> if (_pingnode($vmclient_shortname)) {
> $status{ping} = 1;
> notify($ERRORS{'OK'}, 0, "$vmclient_shortname is pingable
> ($status{ping})");
> }
> else {
> notify($ERRORS{'OK'}, 0, "$vmclient_shortname is not
> pingable ($status{ping})");
> $status{status} = 'RELOAD';
> return $status{status};
> }
>
> #
> #my $vmx_directory = "$requestedimagename$vmclient_shortname";
> #my $myvmx =
> "$vmpath/$requestedimagename$vmclient_shortname/$requestedimagename$vmclient_shortname.vmx";
> #my $mybasedirname = $requestedimagename;
> #my $myimagename = $requestedimagename;
>
> notify($ERRORS{'DEBUG'}, 0, "Trying to ssh...");
>
> #can I ssh into it
> my $sshd = _sshd_status($vmclient_shortname, $requestedimagename,
> $image_os_type);
>
>
> #is it running the requested image
> if ($sshd eq "on") {
>
> notify($ERRORS{'DEBUG'}, 0, "SSH good, trying to query image
> name");
>
> $status{ssh} = 1;
> $identity = $IDENTITY_bladerhel;
> my @sshcmd = run_ssh_command($vmclient_shortname, $identity,
> "cat currentimage.txt");
> $status{currentimage} = $sshcmd[1][0];
>
> notify($ERRORS{'DEBUG'}, 0, "Image name:
> $status{currentimage}");
>
> if ($status{currentimage}) {
> chomp($status{currentimage});
> if ($status{currentimage} =~ /$requestedimagename/)
> {
> $status{image_match} = 1;
> notify($ERRORS{'OK'}, 0,
> "$vmclient_shortname is loaded with requestedimagename
> $requestedimagename");
> }
> else {
> notify($ERRORS{'OK'}, 0,
> "$vmclient_shortname reports current image is currentimage=
> $status{currentimage} requestedimagename= $requestedimagename");
> }
> } ## end if ($status{currentimage})
> } ## end if ($sshd eq "on")
>
> # Determine the overall machine status based on the individual
> status results
> if ($status{ssh} && $status{image_match}) {
> $status{status} = 'READY';
> }
> else {
> $status{status} = 'RELOAD';
> }
>
> notify($ERRORS{'DEBUG'}, 0, "status set to $status{status}");
>
>
> if ($request_forimaging) {
> $status{status} = 'RELOAD';
> notify($ERRORS{'OK'}, 0, "request_forimaging set, setting
> status to RELOAD");
> }
>
> notify($ERRORS{'DEBUG'}, 0, "returning node status hash reference
> (\$node_status->{status}=$status{status})");
> return \%status;
>
> } ## end sub node_status
>
> sub does_image_exist {
> my $self = shift;
> if (ref($self) !~ /esxduke/i) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
> function, it must be called as a class method");
> return 0;
> }
>
> my $image_view = $self->_getimageView(0);
> if (! $image_view) {
> return 0;
> }
> return 1;
>
> } ## end sub does_image_exist
>
>
> #/////////////////////////////////////////////////////////////////////////////
>
> =head2 getimagesize
>
> Parameters : imagename
> Returns : 0 failure or size of image
> Description : in size of Kilobytes
>
> =cut
>
> # Need to implement
> sub get_image_size {
> my $self = shift;
> if (ref($self) !~ /esxduke/i) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
> function, it must be called as a class method");
> return 0;
> }
>
> # Just a placeholder - not sure if this value even matters
> return 1;
> } ## end sub get_image_size
>
> sub power_off {
> my $self = shift;
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
>
> my $task;
> my $computer_shortname = $self->data->get_computer_short_name;
> my $vmView = $self->_getvmView();
> if (!$vmView) {
> return 0;
> }
> if ($vmView->{runtime}->{powerState}->val eq "poweredOff") {
> # VM is turned off, so just return happily
> return 1;
> }
> $task = $vmView->PowerOffVM_Task();
> if (!_checkTask($task, "powering off " . $computer_shortname)) {
> return 0;
> }
> return 1;
> }
>
> sub power_on {
> my $self = shift;
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
>
> my $task;
> my $computer_shortname = $self->data->get_computer_short_name;
> my $vmView = $self->_getvmView();
> if (!$vmView) {
> return 0;
> }
> if ($vmView->{runtime}->{powerState}->val eq "poweredOn") {
> # VM is turned on, so just return happily
> return 1;
> }
> $task = $vmView->PowerOnVM_Task();
> if (!_checkTask($task, "powering on " . $computer_shortname)) {
> return 0;
> }
> return 1;
> }
>
> sub power_reset {
> my $self = shift;
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
>
> my $task;
> my $computer_shortname = $self->data->get_computer_short_name;
> my $vmView = $self->_getvmView();
> if (!$vmView) {
> return 0;
> }
> $task = $vmView->ResetVM_Task();
> if (!_checkTask($task, "reseting on " . $computer_shortname)) {
> return 0;
> }
> return 1;
> }
>
> sub power_status {
> my $self = shift;
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
>
> my $task;
> my $computer_shortname = $self->data->get_computer_short_name;
> my $vmView = $self->_getvmView();
> if (!$vmView) {
> return;
> }
> $vmView->update_view_data();
> if ($vmView->runtime->powerState->val eq 'poweredOn') {
> return "on";
> } elsif ($vmView->runtime->powerState->val eq 'poweredOff') {
> return "off";
> } else {
> notify($ERRORS{'WARNING'}, 0, "Unable to determin power
> status. VMware reports powerState: " . $vmView->runtime->powerState->val);
> return;
> }
> }
>
>
> #/////////////////////////////////////////////////////////////////////////////
>
> =head2 capture
>
> Parameters : $request_data_hash_reference
> Returns : 1 if sucessful, 0 if failed
> Description : Creates a new vmware image.
>
> =cut
>
> sub capture {
> my $self = shift;
> if (ref($self) !~ /esxduke/i) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
> function, it must be called as a class method");
> return 0;
> }
>
> my ($package, $filename, $line, $sub) = caller(0);
>
> # Store some hash variables into local variables
> # to pass to write_current_image routine
> my $request_data = $self->data->get_request_data;
>
> if (!$request_data) {
> notify($ERRORS{'WARNING'}, 0, "unable to retrieve request
> data hash");
> return 0;
> }
>
> # Store some hash variables into local variables
> my $request_id = $self->data->get_request_id;
> my $reservation_id = $self->data->get_reservation_id;
>
> my $image_id = $self->data->get_image_id;
> my $image_os_name = $self->data->get_image_os_name;
> my $image_identity = $self->data->get_image_identity;
> my $image_os_type = $self->data->get_image_os_type;
> my $image_name = $self->data->get_image_name();
>
> my $computer_id = $self->data->get_computer_id;
> my $computer_shortname = $self->data->get_computer_short_name;
> my $computer_nodename = $computer_shortname;
> my $computer_hostname = $self->data->get_computer_hostname;
> my $computer_type = $self->data->get_computer_type;
>
> my $vmtype_name = $self->data->get_vmhost_type_name;
> my $vmhost_vmpath =
> $self->data->get_vmhost_profile_vmpath;
> my $vmprofile_vmdisk =
> $self->data->get_vmhost_profile_vmdisk;
> my $vmprofile_datastorepath =
> $self->data->get_vmhost_profile_datastore_path;
> my $vmhost_hostname = $self->data->get_vmhost_hostname;
> my $host_type = $self->data->get_vmhost_type;
> my $vmhost_imagename = $self->data->get_vmhost_image_name;
>
> my ($hostIdentity, $hostnodename);
> if ($host_type eq "blade") {
> $hostnodename = $1 if ($vmhost_hostname =~
> /([-_a-zA-Z0-9]*)(\.?)/);
> $hostIdentity = $IDENTITY_bladerhel;
> }
> else {
> #using FQHN
> $hostnodename = $vmhost_hostname;
> $hostIdentity = $IDENTITY_linux_lab if ($vmhost_imagename =~
> /^(realmrhel)/);
> }
> # Assemble a consistent prefix for notify messages
> my $notify_prefix = "req=$request_id, res=$reservation_id:";
>
>
> # Print some preliminary information
> notify($ERRORS{'OK'}, 0, "$notify_prefix new name: $image_name");
> notify($ERRORS{'OK'}, 0, "$notify_prefix computer_name:
> $computer_shortname");
> notify($ERRORS{'OK'}, 0, "$notify_prefix vmhost_hostname:
> $vmhost_hostname");
> notify($ERRORS{'OK'}, 0, "$notify_prefix vmtype_name:
> $vmtype_name");
>
> my $vmView = $self->_getvmView();
> if (!$vmView) {
> return 0;
> }
>
> my $imageFolderName = "VCL-TEST";
> my $imageDatastoreName = "VCL-IMAGES-TEST-01";
>
> if(open(CONF, "/etc/vcl/vcld.conf")){
> my @conf=<CONF>;
> close(CONF);
> foreach $line (@conf) {
> #folderName
> if($line =~ /^IMAGEFOLDERNAME=([\S]*)/){
> chomp($line);
> $imageFolderName=$1;
> }
> #datastoreName
> if($line =~ /^IMAGEDATASTORENAME=([\S]*)/){
> chomp($line);
> $imageDatastoreName=$1;
> }
> }
> }
>
> my $parentView = Vim::get_view(mo_ref => $vmView->{parent});
> while (ref($parentView) ne 'Datacenter') {
> $parentView = Vim::get_view(mo_ref =>
> $parentView->{parent});
> }
> my $datacenter = $parentView;
> my $datastoreView;
> my $datastores = Vim::get_views(mo_ref_array =>
> $datacenter->{datastore});
> foreach (@$datastores) {
> if ($_->{info}->{name} eq $imageDatastoreName) {
> $datastoreView = $_;
> last;
> }
> }
> if (!defined($datastoreView)) {
> notify($ERRORS{'CRITICAL'}, 0, "Unable to find datastore
> $imageDatastoreName");
> return 0;
> }
> my $folder = Vim::find_entity_view(view_type => 'Folder', filter =>
> {'name' => $imageFolderName}, begin_entity => $datacenter);
> if (! defined($folder)) {
> notify($ERRORS{'CRITICAL'}, 0, "ERROR: esxduke->capture
> could not find folder $imageFolderName");
> return 0;
> }
>
> # Modify currentimage.txt
> if (write_currentimage_txt($self->data)) {
> notify($ERRORS{'OK'}, 0, "$notify_prefix currentimage.txt
> updated on $computer_shortname");
> }
> else {
> notify($ERRORS{'WARNING'}, 0, "$notify_prefix unable to
> update currentimage.txt on $computer_shortname");
> return 0;
> }
>
> # Set some vm paths and names
> my $vmx_directory = "$reservation_id$computer_shortname";
> my $vmx_image_name = "$reservation_id$computer_shortname";
> my $vmx_path =
> "$vmhost_vmpath/$vmx_directory/$vmx_image_name.vmx";
>
> my @sshcmd;
>
> # Check if pre_capture() subroutine has been implemented by the OS
> module
> if ($self->os->can("pre_capture")) {
> # Call OS pre_capture() - it should perform all OS steps
> necessary to capture an image
> # pre_capture() should shut down the computer when it is
> done
> notify($ERRORS{'OK'}, 0, "calling OS module's pre_capture()
> subroutine");
>
> if (!$self->os->pre_capture({end_state => 'off'})) {
> notify($ERRORS{'WARNING'}, 0, "OS module
> pre_capture() failed");
> return 0;
> }
>
> }
> # Get the power status, make sure computer is off
> my $power_status = $self->power_status();
> notify($ERRORS{'DEBUG'}, 0, "retrieved power status:
> $power_status");
> if ($power_status eq 'off') {
> notify($ERRORS{'OK'}, 0, "verified $computer_nodename power
> is off");
> }
> elsif ($power_status eq 'on') {
> notify($ERRORS{'WARNING'}, 0, "$computer_nodename power is
> still on, turning computer off");
>
> # Attempt to power off computer
> if ($self->power_off()) {
> notify($ERRORS{'OK'}, 0, "$computer_nodename was
> powered off");
> }
> else {
> notify($ERRORS{'WARNING'}, 0, "failed to power off
> $computer_nodename");
> return 0;
> }
> }
> else {
> notify($ERRORS{'WARNING'}, 0, "failed to determine power
> status of $computer_nodename");
> return 0;
> }
>
> # Clone with new image name: $image_name
>
> my $relocateSpec = VirtualMachineRelocateSpec->new(datastore =>
> $datastoreView, transform =>
> VirtualMachineRelocateTransformation->new('sparse'));
> my $cloneSpec = VirtualMachineCloneSpec->new(powerOn => 0, template
> => 1, location => $relocateSpec);
>
> notify($ERRORS{'OK'}, 0, "Cloning $computer_shortname to
> vcl-image-$image_name");
>
> my $task = $vmView->CloneVM_Task(folder => $folder, name =>
> "vcl-image-" . $image_name, spec => $cloneSpec);
> return _checkTask($task, "Cloning $computer_shortname");
>
> } ## end sub capture
>
>
> sub _getvmView {
> my $self = shift;
> my $is_warning = shift;
> my $notify_level = $ERRORS{'WARNING'};
>
> if (defined($is_warning) && !$is_warning) {
> $notify_level = $ERRORS{'DEBUG'};
> }
>
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
> if (defined($self->{_vmView})) {
> return $self->{_vmView};
> }
>
> $self->_checkConnection();
> my $computer_shortname = $self->data->get_computer_short_name;
> my $vmView = Vim::find_entity_view(view_type => 'VirtualMachine',
> filter => {'name' => $computer_shortname });
> if (!$vmView) {
> notify($notify_level, 0, "Could not find VM
> $computer_shortname");
> return 0;
> }
> $self->{_vmView} = $vmView;
>
> return $vmView;
> }
>
> sub _getimageView {
> my $self = shift;
> my $is_crit = shift;
> my $notify_level = $ERRORS{'CRITICAL'};
>
> if (defined($is_crit) && !$is_crit) {
> $notify_level = $ERRORS{'DEBUG'};
> }
>
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
> if (defined($self->{_imageView})) {
> return $self->{_imageView};
> }
>
> $self->_checkConnection();
> my $image_name = $self->data->get_image_name;
> my $imageView = Vim::find_entity_view(view_type => 'VirtualMachine',
> filter => {'name' => "vcl-image-$image_name"});
> if (!$imageView) {
> notify($notify_level, 0, "Could not find image
> vcl-image-$image_name");
> return 0;
> }
> if ($imageView->{snapshot}) {
> # This is always crit. If we're called from does_image_exist
> we want to log the weird state of the image existing but being invalid
> notify($ERRORS{'CRITICAL'}, 0, "ERROR: vcl-image-$image_name
> is an invalid image, it has a snapshot");
> return 0;
> }
> $self->{_imageView} = $imageView;
>
> return $imageView;
> }
>
> sub _removeOldVM {
>
> my $self = shift;
> unless (ref($self) && $self->isa('VCL::Module')) {
> notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be
> called as a VCL::Module module object method");
> return 0;
> }
>
> my $task;
> my (@deviceSpecs, $vmDevices, $vmConfigSpec);
>
> my $vmView = $self->_getvmView();
>
> if ($vmView->{runtime}->{powerState}->val ne "poweredOff") {
> notify($ERRORS{'DEBUG'}, 0, "Powering off " .
> $vmView->{name});
> $task = $vmView->PowerOffVM_Task();
> if (!_checkTask($task, "powering off " . $vmView->{name})) {
> return 0;
> }
> }
>
> # We simply unregister the VM. The _deleteDirectory function will
> # clean up the files. If we were to Destroy the VM, VMware would
> # wipe out the disk of the image, which we need to avoid.
> $vmView->UnregisterVM();
>
> $self->{_vmView} = undef;
> return 1;
>
> }
>
> sub _checkTask {
> my ($task, $taskName) = @_;
>
> my $taskView = Vim::get_view(mo_ref => $task);
> while (1) {
> # Sometimes FileManager->DeleteDatastoreFile() gives us an
> odd task, so this is to give us a shot of making it a normal task
> if (!defined $taskView->{info}) {
> my $attempt = 1;
> while ($attempt <= 3) {
> $taskView->update_view_data();
> if (defined $taskView->{info}) {
> last;
> } else {
> $attempt += 1;
> sleep 1;
> }
> }
> if (!defined $taskView->{info}) {
> notify($ERRORS{'WARNING'}, 0, "ERROR: Could
> not get valid task for $taskName");
> return 0;
> }
> }
>
> if ($taskView->info->state->val eq "success") {
> return 1;
> } elsif ($taskView->info->state->val eq "error") {
> notify($ERRORS{'WARNING'}, 0, "ERROR: $taskName: " .
> $taskView->info->error->localizedMessage);
> return 0;
> }
> sleep 1;
> $taskView->update_view_data();
> }
> }
>
> sub _customizeVm {
> my ($customSpec, $nodeName, $view ) = @_;
>
> my $customView = Vim::get_view(mo_ref =>
> Vim::get_service_content()->customizationSpecManager);
> my $customizationSpec = $customView->GetCustomizationSpec ('name' =>
> $customSpec)->spec;
>
> # We need to set the machine name in this spec
> _setMachineName($nodeName, $customizationSpec);
> my $task = $view->CustomizeVM_Task(spec => $customizationSpec);
> if (_checkTask($task, "customizing $nodeName")){
> notify($ERRORS{'OK'}, 0, "success setting custom specs");
> }
> }
>
> sub _setMachineName {
> my ($vmName, $customizationSpec) = @_;
>
> my @machineName = split(/\./,$vmName);
> my $cust_name = CustomizationFixedName->new (name =>
> $machineName[0]);
> $customizationSpec->identity->userData->computerName ($cust_name);
> return;
> }
>
> sub _deleteDirectory {
> my ($dsName, $dirname, $datacenter) = @_;
> my $ds;
>
> foreach my $ds2 (@{$datacenter->{'datastore'}}) {
> my $ds_view = Vim::get_view(mo_ref => $ds2);
> if ($ds_view->info->name eq $dsName) {
> $ds = $ds_view;
> last;
> }
> }
>
> if (!defined($ds)) {
> notify($ERRORS{'WARNING'}, 0, "Could not find datastore
> $dsName");
> return 0;
> }
>
> my $ds_browser = Vim::get_view(mo_ref => $ds->browser);
>
> my $search_spec = HostDatastoreBrowserSearchSpec->new(matchPattern
> => [$dirname]);
> my $browse_result = $ds_browser->SearchDatastore(datastorePath =>
> "[$dsName]", searchSpec => $search_spec);
> # Only do the delete if the file exists
> if (defined $browse_result->file) {
> eval {
> my $fileManager = Vim::get_view(mo_ref =>
> Vim::get_service_content()->fileManager);
> my $task =
> $fileManager->DeleteDatastoreFile_Task(name => "[$dsName] $dirname",
> datacenter => $datacenter);
> if (!_checkTask($task, "deleting [$dsName]
> $dirname")) {
> return 0;
> }
> };
> if ($@) {
> notify($ERRORS{'WARNING'}, 0, "Error trying to
> delete [$dsName] $dirname: " . ($@->fault_string));
> }
> }
> }
>
>
>
> initialize();
>
> END {
> Util::disconnect();
> }
>
>
> #/////////////////////////////////////////////////////////////////////////////
>
> 1;
> __END__
>
> =head1 SEE ALSO
>
> L<http://cwiki.apache.org/VCL/>
>
> =cut
>
>