host/AzureRecoveryLib/linux/RecoveryHelpers.cpp (1,190 lines of code) (raw):
/*
+------------------------------------------------------------------------------------+
Copyright(c) Microsoft Corp. 2015
+------------------------------------------------------------------------------------+
File : RecoveryHelpers.cpp
Description : Linux Recovery helper function declarations
History : 1-6-2015 (Venu Sivanadham) - Created
+------------------------------------------------------------------------------------+
*/
#include <ace/Init_ACE.h>
#include <boost/foreach.hpp>
#include <boost/assert.hpp>
#include <sstream>
#include "RecoveryHelpers.h"
#include "LinuxUtils.h"
#include "../AzureRecovery.h"
#include "../common/utils.h"
#include "../common/Trace.h"
#include "../resthelper/HttpClient.h"
#include "../config/HostInfoDefs.h"
#include "../config/HostInfoConfig.h"
#include "../config/RecoveryConfig.h"
/*
Method : GlobalInit
Description : Global initialization of the library. This function should be called
before using any of the helper functions in this library.
Parameters : None
Return : None
*/
int GlobalInit()
{
if (-1 == ACE::init())
{
std::cerr << "ACE Init failed" << std::endl;
return 1;
}
AzureStorageRest::HttpClient::GlobalInitialize();
return 0;
}
/*
Method : GlobalUnInit
Description : Global un-initialization. No helper function should be called after this function call.
Parameters : None
Return : None
*/
void GlobalUnInit()
{
AzureStorageRest::HttpClient::GlobalUnInitialize();
}
/*
Method : StartRecovery
Description : Entry point function for PreRecovery steps execution.
Parameters : None
Return : 0 on success, otherwise recovery error code will be returned.
*/
int StartRecovery()
{
using namespace AzureRecovery;
TRACE_FUNC_BEGIN;
int retcode = 0;
std::stringstream errStream;
std::string errorMessage;
std::string curTaskDesc;
do
{
std::map<std::string, std::string> mapSrcTgtDevices;
std::map<std::string, MountPointInfo> srcSysMountInfo;
curTaskDesc = TASK_DESCRIPTIONS::PREPARE_DISKS;
if(!VerifyDisks(mapSrcTgtDevices , errorMessage))
{
retcode = E_RECOVERY_DISK_NOT_FOUND;
errStream << "Error verifying disks. ( " << errorMessage << " )";
TRACE_ERROR("%s\n", errStream.str().c_str());
break;
}
curTaskDesc = TASK_DESCRIPTIONS::MOUNT_SYSTEM_PARTITIONS;
if( !PrepareSourceSysPartitions(mapSrcTgtDevices, srcSysMountInfo, errorMessage) )
{
retcode = E_RECOVERY_VOL_NOT_FOUND;
errStream << "Could not prepare Source System partitions on Hydration VM.";
TRACE_ERROR("%s\n", errStream.str().c_str());
break;
}
curTaskDesc = TASK_DESCRIPTIONS::CHANGE_BOOT_CONFIG;
if(!PerformPreRecoveryChanges(mapSrcTgtDevices,srcSysMountInfo, errorMessage))
{
retcode = E_RECOVERY_BOOT_CONFIG;
errStream << "Could not modify source system configuration.";
TRACE_ERROR("%s\n", errStream.str().c_str());
break;
}
curTaskDesc = TASK_DESCRIPTIONS::UNMOUNT_SYSTEM_PARTITIONS;
if(!CleanupMountPoints(srcSysMountInfo, errorMessage))
{
retcode = E_RECOVERY_CLEANUP;
errStream << "Could not clean-up source system partitions mount points.";
TRACE_ERROR("%s\n", errStream.str().c_str());
break;
}
if(!FlushDevicesAndRevertLvmConfig(mapSrcTgtDevices, errorMessage))
{
retcode = E_RECOVERY_CLEANUP;
errStream << "Could not flush the block devices.";
TRACE_ERROR("%s\n", errStream.str().c_str());
break;
}
curTaskDesc = TASK_DESCRIPTIONS::UPLOAD_LOG;
} while(false);
// Update status info.
RecoveryStatus::Instance().UpdateErrorDetails(
retcode,
errStream.str());
RecoveryStatus::Instance().UpdateProgress(100,
retcode == 0 ?
Recovery_Status::ExecutionSuccess :
Recovery_Status::ExecutionFailed,
curTaskDesc);
return retcode;
}
/*
Method : StartMigration
Description : Entry point function for Migration steps execution.
Parameters : None
Return : 0 on success, otherwise recovery error code will be returned.
*/
int StartMigration()
{
TRACE_FUNC_BEGIN;
int retcode = 0;
namespace AR = AzureRecovery;
std::stringstream errStream;
std::string curTaskDesc;
do
{
curTaskDesc = TASK_DESCRIPTIONS::DISCOVER_OS_VOLS;
if (!AR::DiscoverSourceSystemPartitions())
{
errStream << "Error discovering source system partitions. "
<< RecoveryStatus::Instance().GetLastErrorMessge();
retcode = E_RECOVERY_VOL_NOT_FOUND;
break;
}
curTaskDesc = TASK_DESCRIPTIONS::MOUNT_SYSTEM_PARTITIONS;
if (!AR::MountSourceSystemPartitions())
{
errStream << "Error mounting source system partitions. "
<< RecoveryStatus::Instance().GetLastErrorMessge();
retcode = E_RECOVERY_COULD_NOT_MOUNT_SYS_VOL;
break;
}
curTaskDesc = TASK_DESCRIPTIONS::CHANGE_BOOT_NW_CONFIG;
if (!AR::PrepareSourceOSForAzure())
{
errStream << "Error preparing source OS for Azure. "
<< RecoveryStatus::Instance().GetLastErrorMessge();
retcode = E_RECOVERY_INTERNAL;
break;
}
curTaskDesc = TASK_DESCRIPTIONS::CHANGE_FSTAB;
if (!AR::FixSourceFstabEntries())
{
errStream << "Error fixing source etc/fstab entries. "
<< RecoveryStatus::Instance().GetLastErrorMessge();
retcode = E_RECOVERY_COULD_NOT_FIX_FSTAB;
break;
}
curTaskDesc = TASK_DESCRIPTIONS::UPLOAD_LOG;
} while (false);
// Failure from below cleanup steps can be ignored.
AR::UnmountSourceSystemPartitions();
AR::DeactivateAllVgs();
AR::FlushAllBlockDevices();
// Set error details. And this retcode will be ignored
// if low level functions had already set error code.
// Also it is resposibility of low level functions
// to set the error data specific to the failure.
RecoveryStatus::Instance().UpdateErrorDetails(
retcode,
errStream.str());
RecoveryStatus::Instance().UpdateProgress(100,
retcode == 0 ? Recovery_Status::ExecutionSuccess :
Recovery_Status::ExecutionFailed,
curTaskDesc);
return retcode;
}
/*
Method : StartGenConversion
Description : Entry point function for gen conversion steps execution.
Parameters : None
Return : 0 on success, otherwise gen conversion error.
*/
int StartGenConversion()
{
TRACE_FUNC_BEGIN;
int retcode = 0;
std::stringstream errStream;
std::string curTaskDesc;
// TODO: Linux gen conversion steps.
errStream << "Not Implemented";
curTaskDesc = TASK_DESCRIPTIONS::UPLOAD_LOG;
RecoveryStatus::Instance().UpdateErrorDetails(
retcode,
errStream.str());
// Update progress.
RecoveryStatus::Instance().UpdateProgress(100,
retcode == 0 ?
Recovery_Status::ExecutionSuccess :
Recovery_Status::ExecutionFailed,
curTaskDesc);
TRACE_FUNC_END;
return retcode;
}
namespace AzureRecovery
{
/*
Method : VerifyDisks
Description : Verifies that that all the disks of source machine are discoverable and then
create the mapping of source to target device names.
Parameters : [out] mapSrcTgtDevices : Filled with source -> target device name pairs
[out] errorMsg : Filled with error message on failure.
Return : true on success, otherwise false.
*/
bool VerifyDisks( std::map<std::string, std::string>& mapSrcTgtDevices,
std::string& errorMsg)
{
int nMaxRetry = 5;
bool bSuccess = true;
bool bAllDisksDiscovered = false;
TRACE_FUNC_BEGIN;
std::stringstream errorStream;
disk_lun_map mapSrcDiskTgtLun;
AzureRecoveryConfig::Instance().GetDiskMap(mapSrcDiskTgtLun);
do
{
std::map<UINT, std::string> mapLunTgtDevice;
DWORD dwRet = GetLunDeviceIdMappingOfDataDisks( mapLunTgtDevice );
if(0 != dwRet)
{
errorStream << "Could not get LUN -> Device mapping on hydration VM. "
<< GetLastErrorMsg();
bSuccess = false;
break;
}
//
// Verify that all the data disks are discovered.
//
bAllDisksDiscovered = true;
disk_lun_cons_iter iterSrcDiskLun = mapSrcDiskTgtLun.begin();
for(; iterSrcDiskLun != mapSrcDiskTgtLun.end(); iterSrcDiskLun++)
{
//
// Get the original disk name for sanitized-name by looking at host info.
//
std::string srcDiskName;
TRACE_INFO("Looking for the disk device %s\n", iterSrcDiskLun->first.c_str());
HostInfoConfig::Instance().FindDiskName(iterSrcDiskLun->first, srcDiskName);
std::map<UINT, std::string>::const_iterator iterLunTgtDevice =
mapLunTgtDevice.find(iterSrcDiskLun->second);
if(iterLunTgtDevice != mapLunTgtDevice.end())
{
if( !srcDiskName.empty() )
mapSrcTgtDevices[srcDiskName] = iterLunTgtDevice->second;
else
TRACE_ERROR ("Host info does not have disk entry related to %s\n",
iterSrcDiskLun->first.c_str()
);
// Ignoring the error at this stage as the work-flow will fail at later
// point if its a source boot disk and it's not available in mapSrcTgtDevices map.
// If its a data disk then this failure does not affect the workflow.
}
else
{
if( --nMaxRetry > 0)
{
TRACE_WARNING("Disk at Lun %d is not visible to system yet. Retrying the disk discovery\n",
iterSrcDiskLun->second);
ACE_OS::sleep(30);
bAllDisksDiscovered = false;
}
else
{ // Error in discovering all the disks.
errorStream << "Device at LUN " << iterSrcDiskLun->second
<< " is not available." ;
bSuccess = false;
}
break;
}
}
} while (!bAllDisksDiscovered);
if(!bSuccess)
{
errorMsg = errorStream.str();
TRACE_ERROR("%s\n", errorMsg.c_str());
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : MountFstab
Description : Mounts subvolumes of btrfs (for now) mounted at mount-path.
Parameters : [in] srcRootMountPath : Root Mount path
[in] devName : File system type
Return : 0 -> On Success
mount return code -> On failure
*/
DWORD MountFstab(const std::string& srcRootMountPath,
const std::string& devName)
{
DWORD dwRet = 0;
std::string mntCmd = GetWorkingDir();
mntCmd += ACE_DIRECTORY_SEPARATOR_STR;
mntCmd += MOUNT_FSTAB;
std::string args = srcRootMountPath + " " + devName;
std::stringstream OutStream;
dwRet = RunCommand(mntCmd, args, OutStream);
if( 0 != dwRet )
TRACE_ERROR("subvolume mount error %d for %s. Retrying ...\n",
dwRet,
srcRootMountPath.c_str());
TRACE_INFO("SubVolume Log:\n%s\n",
OutStream.str().c_str());
return dwRet;
}
/*
Method : MountSrcSystemPartition
Description : Identifies the partition number of system partition and calculates its name on target device
and mounts it to a specified location. If partition is an LV then activates its VG and mounts
the LV to a specified location.
Parameters : [in] mountPath: full path of the mount-points
[in] sysPartitionDetails: system partition details
[in] mapSrcTgtDevices: Source to target device name mapping
Return : true on success, otherwise false.
*/
bool MountSrcSystemPartition( const std::string& rootMountPath,
const std::string& mountPath,
const partition_details& sysPartitionDetails,
const std::map<std::string, std::string>& mapSrcTgtDevices
)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
do
{
//
// Identify and mount the source partition
//
DWORD dwRet = 0;
std::string sysPartition;
if(sysPartitionDetails.VolType == PartitionLayout::LVM)
{
sysPartition = sysPartitionDetails.LvName;
dwRet = ActivateVG(sysPartitionDetails.VgName);
if(0 != dwRet)
{
TRACE_ERROR("Could not activate vg %s on Hydration vm.\n",
sysPartitionDetails.VgName.c_str() );
bSuccess = false;
break;
}
}
else if(sysPartitionDetails.VolType == PartitionLayout::FS)
{
//
// Get the device name corresponds to source boot device
//
std::string srcBootDeviceName;
if( mapSrcTgtDevices.find(sysPartitionDetails.DiskName) == mapSrcTgtDevices.end())
{
TRACE_ERROR("Source boot disk not found\n");
bSuccess = false;
break;
}
else
{
srcBootDeviceName = (mapSrcTgtDevices.find(sysPartitionDetails.DiskName))->second;
TRACE_INFO("Device corresponds to source system device %s is %s\n",
sysPartitionDetails.DiskName.c_str(),
srcBootDeviceName.c_str()
);
}
//
// Get the partition corresponds to source sys partition.
//
dwRet = GetTargetStandardOrMapperDevicePartition( sysPartitionDetails.Partition,
sysPartitionDetails.DiskName,
srcBootDeviceName,
sysPartition
);
if(0 != dwRet)
{
TRACE_ERROR("Could not get partition corresponds to source %s partition\n",
sysPartitionDetails.MountPoint.c_str() );
bSuccess = false;
break;
}
}
else
{
//ASSERT
TRACE_ERROR("Unknown volume type\n");
bSuccess = false;
break;
}
if ((sysPartitionDetails.FsType.compare("btrfs") != 0) ||
(sysPartitionDetails.MountPoint.compare("/") == 0))
{
//
// Mount the source system partition
//
dwRet = MountPartition( sysPartition,
mountPath,
sysPartitionDetails.FsType
);
if(0 != dwRet)
{
TRACE_ERROR("Could not mount the system partition %s at %s.\n",
sysPartition.c_str(),
mountPath.c_str() );
//
// Set error details.
//
SetRecoveryErrorCode(E_RECOVERY_COULD_NOT_MOUNT_SYS_VOL);
SetLastErrorMsg("Mount failed with error %d.",
sysPartition.c_str(),
dwRet);
bSuccess = false;
}
}
if (sysPartitionDetails.FsType.compare("btrfs") == 0)
{
dwRet = MountFstab(rootMountPath, sysPartition);
if(0 != dwRet)
{
TRACE_ERROR("Could not mount the btrfs subvolumes of %s at %s.\n",
sysPartition.c_str(),
mountPath.c_str() );
//
// Set error details.
//
SetRecoveryErrorCode(E_RECOVERY_COULD_NOT_MOUNT_SYS_VOL);
SetLastErrorMsg("Mount failed with error %d.",
sysPartition.c_str(),
dwRet);
bSuccess = false;
}
}
} while (false);
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : PrepareSourceSysPartitions
Description : Identifies the corresponding source system partitions/LVs on hydration-VM
and mounts those partitions to system the OS configuration.
Parameters : [in] mapSrcTgtDevices: source -> target device name mapping.
[out] srcSysMountInfo: Filled with system partition type(root, boot, etc) to its mount details pairs.
[out] errorMsg : Filled with error details in failure.
Return : true on success, otherwise false.
*/
bool PrepareSourceSysPartitions( const std::map<std::string, std::string>& mapSrcTgtDevices,
std::map<std::string, MountPointInfo>& srcSysMountInfo,
std::string& errorMsg)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
std::stringstream errorStream;
do
{
//
// Scan all source block devices and VGs for partition information.
//
DWORD dwRet = ScanDevices(mapSrcTgtDevices);
if(0 != dwRet)
{
errorStream << "Error scanning devices. "
<< GetLastErrorMsg() ;
bSuccess = false;
break;
}
//
// Get root partition details from host configure
//
partition_details rootPartitionDetails;
const HostInfoConfig& hConf = HostInfoConfig::Instance();
bSuccess = hConf.GetPartitionInfoFromSystemDevice( ROOT_MOUNT_POINT,
rootPartitionDetails
);
if(!bSuccess)
{
errorStream << "Could not find root partition details from host info.";
break;
}
//
// Generate the mount directory for source root partition.
//
std::string srcRootMountPath;
dwRet = GenerateUniqueMntDirectory(srcRootMountPath);
if(0 != dwRet)
{
errorStream << "Could not generate unique mount point for root.";
bSuccess = false;
break;
}
TRACE_INFO("Source root mount path: %s\n", srcRootMountPath.c_str());
bSuccess = MountSrcSystemPartition(srcRootMountPath,
srcRootMountPath,
rootPartitionDetails,
mapSrcTgtDevices);
if(!bSuccess)
{
errorStream << "Could not prepare source root partition. "
<< GetLastErrorMsg() ;
break;
}
TRACE_INFO("Source root partition %s successfully mounted at %s\n",
rootPartitionDetails.LvName.empty() ?
rootPartitionDetails.Partition.c_str() : rootPartitionDetails.LvName.c_str(),
srcRootMountPath.c_str());
//
// Prepare Mount information for root partition and add to mount information map.
// This information will be used in entire work-flow.
//
MountPointInfo rootMountInfo(rootPartitionDetails.DiskName, srcRootMountPath);
if(rootPartitionDetails.VolType == PartitionLayout::LVM)
{
rootMountInfo.VgName = rootPartitionDetails.VgName;
rootMountInfo.IsLv = true;
}
srcSysMountInfo.insert(std::make_pair(ROOT_MOUNT_POINT,rootMountInfo));
//
// Mount other system volumes under root mount-point if they have dedicated partitions.
// Trying to mount all possible system volumes to build actual root file system hierarchy.
//
std::vector<std::string> nonRootSysPartitions;
nonRootSysPartitions.push_back(BOOT_MOUNT_POINT);
nonRootSysPartitions.push_back(ETC_MOUNT_POINT);
nonRootSysPartitions.push_back(USR_MOUNT_POINT);
nonRootSysPartitions.push_back(VAR_MOUNT_POINT);
nonRootSysPartitions.push_back(OPT_MOUNT_POINT);
nonRootSysPartitions.push_back(USR_LOCAL_MOUNT_POINT);
nonRootSysPartitions.push_back(HOME_MOUNT_POINT);
nonRootSysPartitions.push_back(EFI_MOUNT_POINT);
std::vector<std::string>::const_iterator iterSysPartition = nonRootSysPartitions.begin();
for(; iterSysPartition != nonRootSysPartitions.end(); iterSysPartition++)
{
partition_details sysPartitionDetails;
if( hConf.GetPartitionInfoFromSystemDevice( *iterSysPartition, sysPartitionDetails) )
{
std::string srcSysPartMountPoint = srcRootMountPath + *iterSysPartition;
bSuccess = MountSrcSystemPartition( srcRootMountPath,
srcSysPartMountPoint,
sysPartitionDetails,
mapSrcTgtDevices);
if(!bSuccess)
{
errorStream << "Could not prepare source system partition: "
<< *iterSysPartition
<< " . " << GetLastErrorMsg() ;
break;
}
//
// Prepare Mount information for non root system partition.
//
MountPointInfo mountInfo(sysPartitionDetails.DiskName, srcSysPartMountPoint);
if(sysPartitionDetails.VolType == PartitionLayout::LVM)
{
mountInfo.VgName = sysPartitionDetails.VgName;
mountInfo.IsLv = true;
}
srcSysMountInfo.insert(std::make_pair(*iterSysPartition, mountInfo));
}
else
{
TRACE_INFO("Source's %s might not be a separate partition\n",
iterSysPartition->c_str() );
}
}
// Set OS Version in RecoveryStatus metadata.
VerifyOSVersion(false, srcRootMountPath);
} while (false);
if(!bSuccess)
{
errorMsg = errorStream.str();
TRACE_ERROR("%s\n", errorMsg.c_str());
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : CreateProtectedDisksMapFile
Description : Creates a disks map file which will be used by Azure recovery scripts as input.
Parameters : [in] mapSrcTgtDevices: source -> target device name mapping.
[in] srcSysMountInfo: System partition type(root, boot, etc) to its mount details pairs.
Return : true on success, otherwise false.
*/
bool CreateProtectedDisksMapFile( const std::map<std::string, std::string>& mapSrcTgtDevices,
const std::map<std::string, MountPointInfo>& srcSysMountInfo)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
do
{
//
// Retrieved all source devices from host configuration in same order
// they have appeared in host info configuration.
//
const HostInfoConfig& hConf = HostInfoConfig::Instance();
std::vector<std::string> protectedSrcDevices;
hConf.GetDiskNames(protectedSrcDevices);
//
// Populate sourceDevice list in SourceHostId.PROTECTED_DISKS_LIST file.
// This file should have source non-os disks in the same order they have available in hostinfo
// configure file, and the OS disks will be inserted at first.
//
std::map<std::string, MountPointInfo>::const_iterator iterSysPartMountInfo =
srcSysMountInfo.find(BOOT_MOUNT_POINT);
if(srcSysMountInfo.end() == iterSysPartMountInfo)
{
iterSysPartMountInfo = srcSysMountInfo.find(ROOT_MOUNT_POINT);
if(srcSysMountInfo.end() == iterSysPartMountInfo)
{
TRACE_ERROR("Internal error: boot and root partition information is missing.\n");
bSuccess = false;
break;
}
}
//
// Make sure the disk with boot/root partition comes first in order.
//
std::vector<std::string>::iterator iterSrcDisk = protectedSrcDevices.begin();
for( ; iterSrcDisk != protectedSrcDevices.end(); iterSrcDisk++)
{
if(iterSrcDisk->compare(iterSysPartMountInfo->second.SrcDisk) == 0)
{
protectedSrcDevices.erase(iterSrcDisk);
protectedSrcDevices.insert( protectedSrcDevices.begin(),
iterSysPartMountInfo->second.SrcDisk);
break;
}
}
//
// Write these devices with their target device names to PROTECTED_DISKS_LIST file
//
std::string diskListFile = GetWorkingDir() +
ACE_DIRECTORY_SEPARATOR_STR +
hConf.HostId() +
AZURE_REC_SRC_TGT_MAP_CONF_EXT;
std::ofstream protDisksOut(diskListFile.c_str());
if(!protDisksOut)
{
TRACE_ERROR("Could not open file for writing protected disks list. File : %s\n",
diskListFile.c_str() );
bSuccess = false;
break;
}
TRACE_INFO("Writing source device names to protected disks list file\n");
iterSrcDisk = protectedSrcDevices.begin();
for( ; iterSrcDisk != protectedSrcDevices.end(); iterSrcDisk++)
{
//
// Verify weather the source device is protected or not by searching for it
// in Source<=>Target device mapping. If the device is not protected then
// skip writing that device name in protected disks list file.
//
std::map<std::string, std::string>::const_iterator
iterSrcTgtDisks = mapSrcTgtDevices.find(*iterSrcDisk);
if(mapSrcTgtDevices.end() != iterSrcTgtDisks)
{
protDisksOut << *iterSrcDisk << std::endl;
TRACE_INFO("%s\n", iterSrcDisk->c_str() );
}
else
{
TRACE_ERROR("Source device %s not found in source to target device mapping. This device might not be protected\n",
iterSrcDisk->c_str() );
}
}
protDisksOut.close();
} while (false);
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : ModifyConfig
Description : Modifies the system configuration for a given system partition type.
Parameters : [in] sysPartitionType: System partition type (boot, etc or all)
[in] mountPoint: Mount point of the system partition.
Return : true on success, otherwise false.
*/
bool ModifyConfig(const std::string& sysPartitionType, const std::string& mountPoint)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
do
{
std::string workingDir = GetWorkingDir();
std::string srcHostId = HostInfoConfig::Instance().HostId();
std::string ptConfFile = workingDir +
ACE_DIRECTORY_SEPARATOR_STR +
srcHostId +
AZURE_REC_PREP_TGT_CONF_EXT;
std::string ptCmd = workingDir;
ptCmd += ACE_DIRECTORY_SEPARATOR_STR;
ptCmd += AZURE_RECOVRY_PREPARE_TGT;
std::string newHostId = AzureRecoveryConfig::Instance().GetNewHostId();
if(newHostId.empty())
{
TRACE_ERROR("Empty Host-Id received from recovery config.\n" );
bSuccess = false;
break;
}
std::ofstream OutPtConfFile(ptConfFile.c_str(), std::ios::out | std::ios::trunc );
if(!OutPtConfFile)
{
TRACE_ERROR("Could not open the configure file: %s\n",
ptConfFile.c_str() );
bSuccess = false;
break;
}
// TODO: Add new parameters to take log file path, hostinfo xml file paths
OutPtConfFile << "_MACHINE_UUID_ " << srcHostId << std::endl;
OutPtConfFile << "_TASK_ " << "PREPARETARGET" << std::endl;
OutPtConfFile << "_PLAN_NAME_ " << std::endl;
OutPtConfFile << "_VX_INSTALL_PATH_ " << std::endl;
OutPtConfFile << "_MOUNT_PATH_ " << mountPoint << std::endl;
OutPtConfFile << "_MOUNT_PATH_FOR_ " << sysPartitionType << std::endl;
OutPtConfFile << "_INSTALL_VMWARE_TOOLS_ 0" << std::endl;
OutPtConfFile << "_UNINSTALL_VMWARE_TOOLS_ 1" << std::endl;
OutPtConfFile << "_NEW_GUID_ " << newHostId << std::endl;
OutPtConfFile << "_WORKING_DIRECTORY_ " << workingDir << std::endl;
OutPtConfFile.close();
//
// Run the Prepare Target script by providing this configure file as input.
//
std::stringstream ptOutStream;
DWORD dwRet = RunCommand(ptCmd, ptConfFile, ptOutStream);
if(0 != dwRet)
{
TRACE_ERROR("Prepare Target script failed with error %d\n",
dwRet );
bSuccess = false;
}
TRACE_INFO("Prepare Target Script Log:\n%s\n",
ptOutStream.str().c_str() );
} while (false);
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : PerformPreRecoveryChanges
Description : This is the entry function for performing actual changes on system configuration.
And this routine will be called after mounting the system partitions.
Parameters : [in] mapSrcTgtDevices: source -> target device name mapping.
[in] srcSysMountInfo: System partition type(root, boot, etc) to its mount details pairs.
Return : true on success, otherwise false.
*/
bool PerformPreRecoveryChanges( const std::map<std::string, std::string>& mapSrcTgtDevices,
const std::map<std::string, MountPointInfo>& srcSysMountInfo,
std::string& errorMsg)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
std::stringstream errorStream;
do
{
//
// Create protected disks list file in current directory where the script runs.
// This file will be picked by PrepareTarget.sh file for data massaging on /boot, /etc.
//
bSuccess = CreateProtectedDisksMapFile( mapSrcTgtDevices, srcSysMountInfo);
if(!bSuccess)
{
errorStream << "Could not create protected disks map input file. "
<< GetLastErrorMsg() ;
break;
}
//
// Perform data massaging on existing system configuration such as enabling dhcp,
// updating fstab entries, updating boot image etc.
//
BOOST_ASSERT(!srcSysMountInfo.empty());
std::map<std::string, MountPointInfo>::const_iterator
iterRoot = srcSysMountInfo.find(ROOT_MOUNT_POINT);
if(iterRoot == srcSysMountInfo.end())
{
errorStream << "Internal error: Root mount information missing.";
bSuccess = false;
break;
}
bSuccess = ModifyConfig("all", iterRoot->second.MountPoint);
if(!bSuccess)
{
errorStream << "Could not modify configuration in system partitions";
break;
}
//
// If its a test failover then disable the services on recovering vm.
//
if (AzureRecoveryConfig::Instance().IsTestFailover())
{
if (DisableService(iterRoot->second.MountPoint,MobilityAgents::VXAgent) ||
DisableService(iterRoot->second.MountPoint,MobilityAgents::UARespawn))
{
errorStream << "Could not disable mobility services";
bSuccess = false;
break;
}
//
// Disable FXAgen.
// If FX Agent is not installed on source then this call may fail,
// hence ignoring return code.
//
DisableService(iterRoot->second.MountPoint, MobilityAgents::FXAgent);
}
//
// Run pre recovery script to install the WALinuxAgent and other drivers on source system partitions.
// It also configures Network changes for recovering VM.
//
std::string rcvrScriptCmd = GetWorkingDir();
rcvrScriptCmd += ACE_DIRECTORY_SEPARATOR_STR;
rcvrScriptCmd += AZURE_PRE_RECOVERY_SCRIPT;
std::string rcvrScriptCmdArgs = iterRoot->second.MountPoint;
rcvrScriptCmdArgs += " " + GetWorkingDir();
rcvrScriptCmdArgs += " " + GetHydrationConfigSettings();
std::stringstream OutStream;
DWORD dwRet = RunCommand(rcvrScriptCmd, rcvrScriptCmdArgs, OutStream);
if( 0 != dwRet )
{
errorStream << "Pre Recovery script failed with error " << dwRet;
bSuccess = false;
//break; //Don't break here, log Command outstream on failure as well.
}
std::string err_line, telemetry_data;
while (std::getline(OutStream, err_line))
{
boost::trim(err_line);
if (boost::istarts_with(err_line,
PrepareForAzureScript::TELEMETRY_DATA_LINE_BEGIN))
{
telemetry_data = boost::erase_first_copy(
err_line,
PrepareForAzureScript::TELEMETRY_DATA_LINE_BEGIN);
break;
}
}
RecoveryStatus::Instance().SetTelemetryData(
telemetry_data);
TRACE_INFO("Script Console Log:\n%s\n", OutStream.str().c_str());
} while (false);
if(!bSuccess)
{
errorMsg = errorStream.str();
TRACE_ERROR("%s\n", errorMsg.c_str());
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : CleanupMountPoints
Description : Un-mount the mounted system partitions, and delete the randomly generated root
partition mount folder.
Parameters : [in] mapSrcTgtDevices: source -> target device name mapping.
[out] errorMsg: filled with error details on failure.
Return : true on success, otherwise false.
*/
bool CleanupMountPoints( const std::map<std::string, MountPointInfo>& srcSysMountInfo,
std::string& errorMsg )
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
BOOST_ASSERT(!srcSysMountInfo.empty());
std::stringstream errorStream;
do
{
//
// All the mounted mount-points are inserted into srcSysMountInfo map, and the map sorts them based on mount-point name
// so the longest mount-point path will be bellow to its parent if exist. So iterate the map in reverse direction to
// get the child mount points first before any of its parent.
// For Example: Insertion => /, /etc, /boot, /usr, /var, /usr/local, /opt
// Forward iteration => / ,/boot ,/etc ,/opt ,/usr, /usr/local ,/var
// Reverse iteration => /var, /usr/local ,/usr ,/opt ,/etc ,/boot ,/
//
std::set<std::string> VGSet;
std::map<std::string, MountPointInfo>::const_reverse_iterator iterSrcSysMountInfo = srcSysMountInfo.rbegin();
for(; iterSrcSysMountInfo != srcSysMountInfo.rend(); iterSrcSysMountInfo++ )
{
TRACE_INFO("Un-mounting %s\n", iterSrcSysMountInfo->second.MountPoint.c_str());
if(iterSrcSysMountInfo->second.IsLv)
{
BOOST_ASSERT(!iterSrcSysMountInfo->second.VgName.empty());
TRACE_INFO("Un-mounting volume is an LV, and its VG is : %s\n", iterSrcSysMountInfo->second.VgName.c_str());
VGSet.insert(iterSrcSysMountInfo->second.VgName);
}
DWORD dwRet = UnMountPartition(iterSrcSysMountInfo->second.MountPoint, true, true);
if(0 != dwRet)
{
errorStream << "Unable to un-mount the partition " << iterSrcSysMountInfo->second.MountPoint;
bSuccess = false;
break;
}
}
//
// Deactivate the VGs
//
std::set<std::string>::const_iterator iterVG = VGSet.begin();
for( ; iterVG != VGSet.end(); iterVG++)
{
DWORD dwRet = DeactivateVG(*iterVG);
if(0 != dwRet)
{
errorStream << "VG " << *iterVG << " de-activation failed with error " << dwRet;
bSuccess = false;
break;
}
else
{
TRACE_INFO("VS %s de-activated successfully\n", iterVG->c_str());
}
}
if(!bSuccess)
{
TRACE_INFO("One of the source system partition VG de-activation failed.\n");
TRACE_INFO("Skipping root partition mount directory deletion\n");
break;
}
//
// Delete the root partition mount directory.
//
std::map<std::string, MountPointInfo>::const_iterator iterRootMountInfo= srcSysMountInfo.find(ROOT_MOUNT_POINT);
if(iterRootMountInfo == srcSysMountInfo.end() ||
0 != ACE_OS::rmdir(iterRootMountInfo->second.MountPoint.c_str()))
{
errorStream << "Could not delete Source Root Partition mount point on hydration VM. Error "
<< ACE_OS::last_error() ;
//
// Not failing the operation on rmdir failure as its not a critical error.
//
}
} while (false);
if(!bSuccess)
{
errorMsg = errorStream.str();
TRACE_ERROR("%s\n", errorMsg.c_str());
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : FlushDevicesAndRevertLvmConfig
Description : Flush the all disk devices to clear any intermediate buffers.
Parameters : [in] mapSrcTgtDevices: source -> target device name mapping.
Return : true on success, otherwise false.
*/
bool FlushDevicesAndRevertLvmConfig( const std::map<std::string, std::string>& mapSrcTgtDevices,
std::string& errorMsg)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
BOOST_ASSERT(!mapSrcTgtDevices.empty());
std::stringstream errorStream;
do
{
//
// Flush all the source devices
//
if(0 != FlushBlockDevices(mapSrcTgtDevices) )
{
errorStream << "Some of the devices might not flushed successfully. "
<< GetLastErrorMsg() ;
bSuccess = false;
}
//
// Un-Register LVs from Hydration VM. Again the return code is not
// considering as its related to Hydration-VM LVs configuration.
//
std::set<std::string> srcLvs;
HostInfoConfig::Instance().GetAllLvNames(srcLvs);
UnRegisterLVs(srcLvs);
//
// Note: In Caspian V1, there is additional clean-up w.r.t scsi LUN slots and
// multipath mapper device name. It is not required here as the Hydration-VM
// does not use multipath mapper device names and the scsi LUN slots are not
// going to re-use.
//
} while (false);
if(!bSuccess)
{
errorMsg = errorStream.str();
TRACE_ERROR("%s\n", errorMsg.c_str());
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : DiscoverSourceSystemPartitions
Description : Discovers the source root('/') and other system partitions.
On success, the root partition will mounted at SMS_AZURE_CHROOT
and rest of the system partition details are discovered and
updated in the SourceFilesystemTree object.
Parameters :
Return : true on success, otherwise false.
*/
bool DiscoverSourceSystemPartitions()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
TRACE_INFO("Fetching the volumes/partitions visible to the system.\n");
std::vector<volume_details> volumes;
BlockDeviceDetails::Instance().GetVolumeDetails(volumes, true);
std::vector<fstab_entry> fs_entries;
SourceFilesystemTree &sft = SourceFilesystemTree::Instance();
bool bIsRootFound = false;
BOOST_FOREACH(const volume_details &volume, volumes)
{
// Ignore filesystem type if swap or lvm or empty.
// TODO: Improve this logic, and have a list of supported fs types.
if (volume.fstype.empty() ||
boost::equals(volume.fstype, "swap") ||
boost::istarts_with(volume.fstype, "lvm"))
{
TRACE_INFO("Volume %s filesystem is [%s], skipping it.\n",
volume.name.c_str(),
volume.fstype.c_str());
continue;
}
std::string mountpoint = bIsRootFound ? SMS_AZURE_TMP_MNT : SMS_AZURE_CHROOT;
if (!CreateDirIfNotExist(mountpoint))
{
TRACE_ERROR("Could not create directory for mounting source partitions.\n");
bSuccess = false;
break;
}
TRACE_INFO("Checking volume: %s.\n", volume.ToString().c_str());
std::string partition_dev = "UUID=" + volume.uuid;
DWORD dwRet = MountPartition(partition_dev,
mountpoint,
volume.fstype);
if (0 == dwRet)
{
std::string src_partition;
if (sft.DetectPartition(mountpoint, src_partition, volume) &&
boost::equals(src_partition, "/"))
{
bIsRootFound = true;
// If detected partition is a root(/) then verify OS version.
// If its not in supported OS list then stop the hydration.
if (!VerifyOSVersion(true, mountpoint))
{
bSuccess = false;
TRACE_ERROR("Unsupported OS detected.\n");
// VerifyOSVersion sets the error code on unsupported os,
// no need to set here again.
// Unmount and break the detection loop.
UnMountPartition(mountpoint);
break;
}
// Its a supported OS, now read fstab file and update
// details in SourceFilesystemTree obj. Also, skip unmounting
// this partition as it will be the chroot and rest of the
// system partiton will be munted under this chroot mount point.
ReadFstab(SMS_AZURE_CHROOT_FSTAB, fs_entries);
sft.UpdateSrcFstabDetails(fs_entries);
}
else
{
dwRet = UnMountPartition(mountpoint);
if (0 != dwRet)
{
TRACE_ERROR("Could not un-mount this device %s.\n",
partition_dev.c_str());
// Check if the mountpoint is still valid, otherwise
// ignore the failure and continue the execution.
if (0 == RunCommand(CmdLineTools::FindMnt, mountpoint))
{
// Mount point is still valid, its real unmount issue.
// Return failure.
SetRecoveryErrorCode(E_RECOVERY_INTERNAL);
SetLastErrorMsg("Unmount failed with error %d", dwRet);
bSuccess = false;
break;
}
else
{
TRACE_INFO("Mountpoint is no more valid, ignoring the umount failure.\n");
}
}
}
}
else
{
TRACE_WARNING("Cound not mount this device %s. Moving to next.\n",
partition_dev.c_str());
}
if (!sft.ShouldContinueDetection())
{
TRACE_INFO("Required partitions are discovered. Stop mounting...\n");
break;
}
}
// Apply stanadard device name heuristics
// to detect undiscovered standard partitons.
sft.DetectUndiscoveredStandardPartitions();
// Update btrfs subvolumes if defined in fstab. And
// if any subvolume is found then it will be mounted
// along with source system partitions.
sft.UpdateFstabSubvolDetails(fs_entries);
// Check if the required system partitions are discovered,
// if not then set the error data and return failure. If any
// of the required source system partitions is not discovered
// then this function should return failure.
if (bSuccess && !sft.IsDiscoveryComplete())
{
bSuccess = false;
std::string mntpoints;
std::vector<std::string> undiscovered_mountpoints;
sft.GetUndiscoveredSrcSysMountpoints(undiscovered_mountpoints);
BOOST_FOREACH(const std::string &mnt, undiscovered_mountpoints)
mntpoints += mnt + ";";
RecoveryStatus::Instance().SetStatusErrorCode(
E_RECOVERY_VOL_NOT_FOUND,
mntpoints);
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : MountSourceSystemPartitions
Description : Root should have already mounted while detecting system
partitions, it mounts rest of the source system partitions
if there is any.
Parameters :
Return : true on success, otherwise false.
*/
bool MountSourceSystemPartitions()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
// Fetch sft mountpoint entries which are really seperate
// partitions on source.
SourceFilesystemTree &sft = SourceFilesystemTree::Instance();
std::vector<fs_tree_entry> sft_entries;
sft.GetRealSystemPartitions(sft_entries);
BOOST_ASSERT(!sft_entries.empty());
BOOST_ASSERT(FileExists(SMS_AZURE_CHROOT_FSTAB));
BOOST_FOREACH(const fs_tree_entry& sft_entry, sft_entries)
{
// root (/) is already mounted, ignore it
// and continue with rest.
if (boost::equals(sft_entry.mountpoint, "/"))
{
// If its btrfs volume then mount subvolumes.
if (sft_entry.src_fstab_entry.IsBtrfsVolume())
{
TRACE_INFO("%s is btrfs volume, mounting its subvolumes too.\n",
sft_entry.mountpoint.c_str());
sft_entry.MountSubvolumes();
}
continue;
}
std::stringstream mnt;
mnt << SMS_AZURE_CHROOT
<< ACE_DIRECTORY_SEPARATOR_STR
<< boost::trim_left_copy_if(sft_entry.mountpoint,
boost::is_any_of(ACE_DIRECTORY_SEPARATOR_STR));
BOOST_ASSERT(!sft_entry.is_discovered);
std::string partition_dev =
sft_entry.src_fstab_entry.IsStandardDeviceName() ?
"UUID=" + sft_entry.vol_info.uuid:
sft_entry.src_fstab_entry.device;
TRACE_INFO("Mount %s at %s.\n",
partition_dev.c_str(),
mnt.str().c_str());
BOOST_ASSERT(!partition_dev.empty());
DWORD dwRet = MountPartition(partition_dev,
mnt.str(),
sft_entry.src_fstab_entry.fstype);
if (0 != dwRet)
{
TRACE_ERROR("Could not mount partition on hydration VM for: %s.\n",
sft_entry.src_fstab_entry.ToString().c_str());
RecoveryStatus::Instance().SetStatusErrorCode(
E_RECOVERY_COULD_NOT_MOUNT_SYS_VOL,
sft_entry.src_fstab_entry.mountpoint);
bSuccess = false;
break;
}
// If its btrfs volume then mount subvolumes too.
if (sft_entry.src_fstab_entry.IsBtrfsVolume())
sft_entry.MountSubvolumes();
TRACE_ERROR("Successfully mounted the partition on hydration: %s.\n",
sft_entry.src_fstab_entry.ToString().c_str());
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : VerifyOSVersion
Description : Verify is the OS is supported, it also collect the OS information.
Parameters : [in] setError: If set to false, the function only logs the OS Version
in telemetry without failing for UnsupportedOSVersion
[in] mntPath: The mount path where the root partition of source VM is present.
Return : true on success, otherwise false.
*/
bool VerifyOSVersion(bool setError, std::string mntPath)
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
do
{
std::stringstream os_details_script;
os_details_script << GetWorkingDir()
<< ACE_DIRECTORY_SEPARATOR_STR
<< AZURE_OS_DETAILS_TGT;
std::stringstream script_args;
script_args << mntPath
<< " --formated-output";
std::stringstream osDetailsScriptOut;
DWORD dwRet = RunCommand(os_details_script.str(),
script_args.str(),
osDetailsScriptOut);
TRACE_INFO("\n%s\n", osDetailsScriptOut.str().c_str());
if (dwRet != 0)
{
TRACE_ERROR("Could not detect source OS. Script failed with error %d\n",
dwRet);
bSuccess = false;
break;
}
std::string os_version, os_details;
GetOSDetailsFromScriptOutput(osDetailsScriptOut,
os_version,
os_details);
// Set os_version or os_details string as metadata to status blob.
// This will be used for telemetry.
boost::trim(os_details);
if (os_details.empty())
{
os_details = "Unknown";
RecoveryStatus::Instance().SetSourceOSDetails(os_version);
}
else
{
RecoveryStatus::Instance().SetSourceOSDetails(os_details);
}
if (setError)
{
if (os_version.empty() ||
!IsSupportedOS(os_version))
{
bSuccess = false;
TRACE_ERROR("Unsupported OS. OS Version: %s OS Details: %s\n",
os_version.c_str(),
os_details.c_str());
// Set hydration error code as unsupported OS.
RecoveryStatus::Instance().SetStatusErrorCode(
E_RECOVERY_OS_UNSUPPORTED,
os_version);
SetLastErrorMsg("Unsupported OS version detected.");
}
}
} while (false);
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : PrepareSourceOSForAzure
Description : Prepares the source OS for Azure.
Parameters :
Return : true on success, otherwise false.
*/
bool PrepareSourceOSForAzure()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
std::stringstream pre_os_script;
pre_os_script << GetWorkingDir()
<< ACE_DIRECTORY_SEPARATOR_STR
<< PREPARE_OS_FOR_AZURE_SCRIPT;
std::stringstream pre_os_script_args;
pre_os_script_args << SMS_AZURE_CHROOT
<< " "
<< GetHydrationConfigSettings();
std::stringstream scriptOut;
DWORD dwRet = RunCommand(pre_os_script.str(),
pre_os_script_args.str().c_str(),
scriptOut);
// Always record error data and telemetry data.
std::string err_line, err_data, telemetry_data;
while (std::getline(scriptOut, err_line))
{
boost::trim(err_line);
if (boost::istarts_with(err_line,
PrepareForAzureScript::ERROR_DATA_LINE_BEGIN))
{
err_data = boost::erase_first_copy(
err_line,
PrepareForAzureScript::ERROR_DATA_LINE_BEGIN);
}
else if (boost::istarts_with(err_line,
PrepareForAzureScript::TELEMETRY_DATA_LINE_BEGIN))
{
telemetry_data = boost::erase_first_copy(
err_line,
PrepareForAzureScript::TELEMETRY_DATA_LINE_BEGIN);
}
}
if (E_RECOVERY_SUCCESS != dwRet)
{
bSuccess = false;
TRACE_ERROR("Prepare for Azure script failed with error %d\n",
dwRet);
// Map the script exit code to recovery error code.
int error_code = E_RECOVERY_SUCCESS;
switch (dwRet)
{
case PrepareForAzureScript::E_INTERNAL:
error_code = E_RECOVERY_INTERNAL;
break;
case PrepareForAzureScript::E_SCRIPT_SYNTAX_ERROR:
error_code = E_RECOVERY_SCRIPT_SYNTAX_ERROR;
break;
case PrepareForAzureScript::E_TOOLS_MISSING:
error_code = E_RECOVERY_TOOLS_MISSING;
break;
case PrepareForAzureScript::E_ARGS:
error_code = E_RECOVERY_INTERNAL;
break;
case PrepareForAzureScript::E_OS_UNSUPPORTED:
error_code = E_RECOVERY_OS_UNSUPPORTED;
break;
case PrepareForAzureScript::E_CONF_MISSING:
error_code = E_RECOVERY_SYS_CONF_MISSING;
break;
case PrepareForAzureScript::E_INITRD_IMAGE_GENERATION_FAILED:
error_code = E_RECOVERY_INITRD_IMAGE_GENERATION_FAILED;
break;
case PrepareForAzureScript::E_HV_DRIVERS_MISSING:
error_code = E_RECOVERY_HV_DRIVERS_MISSING;
break;
case PrepareForAzureScript::E_AZURE_GA_INSTALLATION_FAILED:
error_code = E_RECOVERY_GUEST_AGENT_INSTALLATION_FAILED;
break;
case PrepareForAzureScript::E_ENABLE_DHCP_FAILED:
error_code = E_RECOVERY_ENABLE_DHCP_FAILED;
break;
default:
// Any other error code is an internal error.
error_code = E_RECOVERY_INTERNAL;
break;
}
RecoveryStatus::Instance().SetStatusErrorCode(
error_code,
err_data);
}
RecoveryStatus::Instance().SetTelemetryData(
telemetry_data);
TRACE_INFO("Prepare for Azure log:\n%s\n",
scriptOut.str().c_str());
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : FixSourceFstabEntries
Description : Verifies and fixes the source fstab entries.
Parameters :
Return : true on success, otherwise false.
*/
bool FixSourceFstabEntries()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
BOOST_ASSERT(FileExists(SMS_AZURE_CHROOT_FSTAB));
TRACE("Fixing source fstab file entries.\n");
std::vector<volume_details> volumes;
BlockDeviceDetails::Instance().GetVolumeDetails(volumes);
// Get device to uuid mapping if azure_sms_blkid.out file is present.
std::map<std::string, std::string> src_blkid_map;
GetSourceBlkidDetails(SMS_AZURE_BLKID_OUT, src_blkid_map);
SourceFilesystemTree &sft = SourceFilesystemTree::Instance();
std::ifstream ifstab(SMS_AZURE_CHROOT_FSTAB);
std::ofstream ofstab_new(SMS_AZURE_CHROOT_FSTAB_NEW);
std::string fstab_line;
while (std::getline(ifstab, fstab_line))
{
TRACE("Processing fstab line: %s.\n", fstab_line.c_str());
// We skip processing commented lines and malformed entries
// and write such lines as is to the new fstab file.
fstab_entry fe;
if (fstab_entry::Fill(fe, fstab_line))
{
if (fe.IsStandardDeviceName())
{
// The detection logic builds source => target disk mapping
// when it detected some of the system partitions. If the
// partition name in current fstab entry belong to one of
// those disks then we apply some heuristics to find
// corresponding partitions and replace the standard partition
// name with uuid. If the volume is not detected then the current
// fstab entry will be commented.
volume_details tgt_vol;
if (sft.FindVolForFstabEntry(fe, tgt_vol) &&
!boost::trim_copy(tgt_vol.uuid).empty())
{
// Replace device with UUID.
fe.device = "UUID=" + boost::trim_copy(tgt_vol.uuid);
fstab_line = fe.GetFstabLine();
}
else if (src_blkid_map.find(fe.device) != src_blkid_map.end())
{
// Found device in uuid using source blkid output.
fe.device = "UUID=" + src_blkid_map[fe.device];
TRACE_INFO("Device uuid is found using source blkid data: %s.\n",
fe.device.c_str());
fstab_line = fe.GetFstabLine();
}
else
{
TRACE_WARNING("Could not determine UUID for the partition %s. Commenting it's fstab line.\n",
fe.device.c_str());
// If above two condition were not met then comment it.
fstab_line = "# " + fstab_line;
}
}
else if (fe.IsNfsDevice())
{
TRACE_WARNING("Network filesystem detected, commenting the entry.\n");
fstab_line = "# " + fstab_line;
}
else if (fe.IsPersistentLocalDevice())
{
bool bDevFound = false;
BOOST_FOREACH(const volume_details &vol, volumes)
{
// If device is found then stop the lookup.
if (vol == fe)
{
bDevFound = true;
break;
}
}
// If device is not found then add nofail to options,
// otherwise no change needed for the entry.
if (bDevFound)
{
TRACE_INFO("Volume [%s] found in replicated disks.\n",
fe.device.c_str());
}
else
{
fstab_line = fe.GetFstabLineWithOptions("nofail");
TRACE_WARNING("Volume [%s] not found in replicated disks.\n",
fe.device.c_str());
}
}
else
{
TRACE_INFO("[%s] could be a non-persistent device, ignoring it.\n",
fe.device.c_str());
}
}
// Write line to new fstab
ofstab_new << fstab_line << std::endl;
}
// Swap the files.
bSuccess =
RenameFile(SMS_AZURE_CHROOT_FSTAB, SMS_AZURE_CHROOT_FSTAB_BCK) &&
RenameFile(SMS_AZURE_CHROOT_FSTAB_NEW, SMS_AZURE_CHROOT_FSTAB);
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : UnmountSourceSystemPartitions
Description : Unmounts chroot directory.
Parameters :
Return : true on success, otherwise false.
*/
bool UnmountSourceSystemPartitions()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
std::stringstream mountinfo;
DWORD dwRet = RunCommand(CmdLineTools::FindMnt,
SMS_AZURE_CHROOT,
mountinfo);
TRACE_INFO("%s mount info:\n%s\n",
SMS_AZURE_CHROOT,
mountinfo.str().c_str());
// If no partition is not mounted at chroot mountpoint
// then skip unmount attempt on it.
if (dwRet == 1)
{
TRACE_WARNING("%s is not a valid mountpoint. Skipping unmount.\n",
SMS_AZURE_CHROOT);
}
else if (UnMountPartition(SMS_AZURE_CHROOT, true, true) != 0)
{
bSuccess = false;
TRACE_ERROR("Could not unmount the source root parition.\n");
}
else
{
TRACE_INFO("Successfully unmounted %s\n.",
SMS_AZURE_CHROOT);
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : DeactivateAllVgs
Description : Deactivates all the VGs visible to the system.
Parameters :
Return : true on success, otherwise false.
*/
bool DeactivateAllVgs()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
// Get list of VGs visible to the system.
std::vector<std::string> vgs;
GetVgList(vgs);
BOOST_FOREACH(const std::string& vg, vgs)
{
TRACE_INFO("Deactivating the VG: %s.\n", vg.c_str());
// Ignoring the return code.
DeactivateVG(vg);
}
TRACE_FUNC_END;
return bSuccess;
}
/*
Method : FlushAllBlockDevices
Description : Flushes the buffers of all block devices in the system.
Parameters :
Return : true on success, otherwise false.
*/
bool FlushAllBlockDevices()
{
bool bSuccess = true;
TRACE_FUNC_BEGIN;
// Get list of disk devices in the system.
std::vector<std::string> devices;
BlockDeviceDetails::Instance().GetDiskNames(devices);
// Flush all the devices.
bSuccess = FlushBlockDevices(devices) == 0;
TRACE_FUNC_END;
return bSuccess;
}
} // ~AzureRecovery