#!/usr/bin/perl -w

##########################################################################
# Copyright 2005 VMware, Inc.  All rights reserved. -- VMware Confidential
##########################################################################

#
# esxcfg-ugprade --
#
#       This script performs various tasks related to upgrading from
#       ESX Server 2.x to ESX Server 3.0.
#

use lib "/usr/lib/vmware/esx-perl/perl5/site_perl/5.8.0";

use VMware::CmdTool;
use VMware::Log qw(:log);
use VMware::FileSys::FstabInfo qw(UpgradeFstab);
use VMware::FileSys::StandardLocator qw(StdFile);
use VMware::Config::VmacoreConfigObj;

use VMware::PCI::LinuxDeviceManager;
use VMware::PCI::PCIInfo qw(GetPCIDeviceManagerFromConfig);

use VMware::Upgrade::Locator qw(BackUpFiles PreUpgradeFile
                                UnlinkPreUpgradeFiles);
use VMware::Upgrade::LiloConfig;
use VMware::Upgrade::HWConfig;
use VMware::Upgrade::VMwareConfig;
use VMware::Upgrade::VMKConfig;
use VMware::Upgrade::MPath;
use VMware::Upgrade::VMHBADevs;
use VMware::Upgrade::CustomerLog;
use VMware::Upgrade::RCLocal;

use strict;


########################################################################
#
# Usage --
#
#       Returns the usage statement as a string.
#
# Results:
#       The usage statement.
#
# Side effects:
#       None.
#
########################################################################

sub Usage
{
   return <<EOD;
esxcfg-upgrade -h --help
               -g --convert-grub
               -f --convert-fstab
               -r --upgrade-pre-vmkernel
               -o --upgrade-post-vmkernel

The -g option may only be used with the -r option.
EOD

}


########################################################################
#
# CmdConvertFstab --
#
#       Implements the "--convert-fstab" command. Converts /etc/fstab
#       to specify UUIDs for all ext3 or swap entries with UUIDs.
#
# Results:
#       1 on success, 0 otherwise.
#
# Side effects:
#       See UpgradeFstab.
#
########################################################################

sub CmdConvertFstab
{
   LogInfo("Converting /etc/fstab to UUIDs.");
   return UpgradeFstab();
}


########################################################################
#
# CmdUpgradePre --
#
#       Implements the "--upgrade-pre-vmkernel" command.
#       Performs the upgrade steps that can be done before the VMkernel
#       is loaded (and in some cases must be done before).
#
# Results:
#       1 on success, 0 otherwise.
#
# Side effects:
#       Backs up config files, creates new config files, changes
#       networking scripts.
#
########################################################################

sub CmdUpgradePre
{
   my $convertGRUB = shift;   # IN: Whether or not to convert LILO->GRUB.

   LogInfo("Converting hwconfig and other ESX configuration files.");

   unless (BackUpFiles('esx')) {
      LogError("Could not back up configuration files.  Aborting.");
      return 0;
   }
   LogInfo("Configuration files backed up.");

   # XXX: Is this the best place to put this?
   LogInfo("Updating linux device information.");
   unless (VMware::PCI::LinuxDeviceManager::UpdateConfig()) {
      LogError("Could not update linux device information.  Aborting.");
      return 0;
   }
   LogInfo("Linux device information updated.");

   my $esxCfgPath = StdFile('esx_config', 'esx');
   my $esxCfg = new VMware::Config::VmacoreConfigObj();

   my $hwconfig = new VMware::Upgrade::HWConfig();
   unless (defined $hwconfig) {
      LogError("Could not upgrade primary configuration.  Aborting.");
      return 0;
   }
   $esxCfg->SetTree([], $hwconfig->GetConfigData());
   LogInfo("hwconfig and associated files converted.");

   my $vmwconfig = new VMware::Upgrade::VMwareConfig();
   if (defined $vmwconfig) {
      $esxCfg->SetTree(['vmkernel'], $vmwconfig->GetConfigData());
   } else {
      LogWarn("Could not upgrade VMkernel options from '" .
              PreUpgradeFile('config', 'esx') . "', continuing.");
   }
   LogInfo("VMkernel options from '" . PreUpgradeFile('config', 'esx') .
            "' converted.");

   #vmconfig wipes the vmkernel tree but there are things from hwconfig that go
   #into that tree, in this case hyperthreading
   $esxCfg->Set("/vmkernel/hyperthreading", $hwconfig->{hyperthreading});

   my $vmkconfig = new VMware::Upgrade::VMKConfig();
   if (defined $vmkconfig) {
      $esxCfg->SetTree(['adv'], $vmkconfig->GetConfigData());
   } else {
      LogWarn("Could not upgrade advanced VMkernel settings from '" .
              PreUpgradeFile('vmkconfig', 'esx') . "', continuing.");
   }
   LogInfo("Advanced VMkernel settings upgraded.");

   my $rclocal = new VMware::Upgrade::RCLocal();
   unless (defined $rclocal) {
      LogError("Could not upgrade rc.local. Aborting.");
      return 0;
   }

   my $liloconfig = undef;
   if ($convertGRUB) {
      $liloconfig = new VMware::Upgrade::LiloConfig();
      unless (defined $liloconfig) {
         LogError("Could not upgrade bootloader. Aborting.");
         return 0;
      }

      $esxCfg->SetTree(['boot'], $liloconfig->GetConfigData());
      LogInfo("Bootloader upgraded.");
   }

   #
   # Commit to disk
   #

   unless ($esxCfg->WriteFile($esxCfgPath)) {
      LogError("Could not write out '$esxCfgPath'.  Aborting.");
      return 0;
   }
   LogInfo("Primary new ESX configuration file written.");

   unless ($hwconfig->CommitCosNetworking()) {
      LogError("Could not write out Service Console networking changes." .
               "  Aborting.");
      return 0;
   }
   LogInfo("Service Console networking changes committed.");
   
   unless ($rclocal->WriteRemainder()) {
      LogError("Could not write out rc.local changes.  Aborting.");
      return 0;
   }
   LogInfo("rc.local changes committed.");

   if ($convertGRUB) {
      unless ($liloconfig->Commit()) {
         LogError("Could not write out bootloader changes.  Aborting.");
         return 0;
      }
      LogInfo("Bootloader changes committed.");
   }

   return 1;
}


########################################################################
#
# CmdUpgradePost --
#
#       Implements the "--upgrade-post-vmkernel" command.
#       Performs the upgrade steps that must be done after
#       the vmkernel has been loaded.
#       Also performs other final-stage-upgrade cleanup.
#
# Results:
#       1 on success, 0 otherwise.
#
# Side effects:
#       Unlinks old config files (assumes they were backed up earlier).
#       Modifies new config files with additional information.
#
########################################################################

sub CmdUpgradePost
{
   my $config = VMware::CmdTool::LoadConfig(1);

   LogInfo("Updating multipathing information.");
   my $mpath = new VMware::Upgrade::MPath();
   unless (defined $mpath) {
      LogError("Could not update multipathing information.  Aborting.");
      return 0;
   }
   my $mpathCfg = $mpath->GetConfigData();
   LogInfo("Multipathing information updated.");

   if (keys %$mpathCfg) {
      $config->SetTree(['storage'], $mpathCfg);
   }

   LogInfo("Building storage upgrade map for legacy VMs.");
   my $devMgr = GetPCIDeviceManagerFromConfig($config);
   my $vmhbadevs = new VMware::Upgrade::VMHBADevs($devMgr, $mpath);
   my $vmhbaCfg = $vmhbadevs->GetConfigData();
   LogInfo("Storage upgrade map constructed.");
   if (keys %$vmhbaCfg) {
      $config->SetTree(['upgrade', 'storage'], $vmhbaCfg);
   }

   unless ($config->WriteAndUnlockFile()) {
      LogError("Could not commit changes back to esx.conf.");
      return 0;
   }

   LogInfo("Cleaning up temporary files.  All old config files are backed " .
           "up in /etc/vmware/upgrade-backup");
   return UnlinkPreUpgradeFiles('esx');
}


########################################################################
#
# main --
#
# Results:
#       Exit code 0 on success, non-zero otherwise.
#
# Side effects:
#       Everything.
#
########################################################################

sub main
{
   my $cmdHash = {
      convertFstab => 0,      # Convert fstab entries to use UUIDs
      convertGRUB => 0,       # Convert bootloader from lilo to grub
      preVMK => 0,            # Run pre-vmkernel upgrade
      postVMK => 0            # Run post-vmkernel upgrade
   };
   my $opts = {
      "convert-fstab|f" => \$cmdHash->{convertFstab},
      "convert-grub|g" => \$cmdHash->{convertGRUB},
      "upgrade-pre-vmkernel|r" => \$cmdHash->{preVMK},
      "upgrade-post-vmkernel|o" => \$cmdHash->{postVMK},
   };
   unless (VMware::CmdTool::InitTool('esxcfg_upgrade_cfg',
                                     'esxcfg_upgrade_log',
                                     $opts, Usage())) {
      exit -1;
   }

   unless (VMware::Upgrade::CustomerLog::Init()) {
      VMware::CmdTool::Finish(0, 1);
   }

   unless ($cmdHash->{convertFstab} or
           $cmdHash->{preVMK} or
           $cmdHash->{postVMK}) {
      die Usage();
   }

   if ($cmdHash->{convertGRUB} and not $cmdHash->{preVMK}) {
      die Usage();
   }

   my $success = 1;
   if ($success && $cmdHash->{preVMK}) {
      $success = CmdUpgradePre($cmdHash->{convertGRUB});
   }
   if ($success && $cmdHash->{convertFstab}) {  
      $success = CmdConvertFstab();
   }
   if ($success && $cmdHash->{postVMK}) {
      $success = CmdUpgradePost();
   }

   VMware::CmdTool::Finish($success, 1);
}

main();

