host/common/win32/hostrecoverymanagermajor.cpp (319 lines of code) (raw):

//--------------------------------------------------------------- // <copyright file="hostrecoverymanager.cpp" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> // // Description: Windows implementation for HostRecoveryManager // // History: 15-Aug-2016 veshivan Created // //---------------------------------------------------------------- #include "hostrecoverymanager.h" #include "registry.h" #include "localconfigurator.h" #include "service.h" #include <winioctl.h> #include <versionhelpers.h> #include "InmFltIoctl.h" #include "InmFltInterface.h" #include "DiskHelpers.h" #include <map> #include <set> #include <sstream> #include <boost/filesystem.hpp> #include <boost/tokenizer.hpp> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/lexical_cast.hpp> #include <boost/scope_exit.hpp> #include <regex> #include "InmageDriverInterface.h" #include "RegistryOperations.h" using namespace std; #define VMWARE_TOOLS_SERVICE_NAME "VMTools" void HostRecoveryManager::DisableEnablePlatformServices(std::map<std::string, InmServiceStartType> platformServices) { DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); InmServiceStatus svcStatus; InmServiceStartType startType; InmServiceStartType curStartType; std::string strStartType; for (auto itService : platformServices) { getServiceStatus(itService.first, svcStatus); if (INM_SVCSTAT_NOTINSTALLED == svcStatus) { DebugPrintf(SV_LOG_INFO, "%s Service not installed on this system.\n", itService.first.c_str()); continue; } startType = itService.second; if (!getStartServiceType(itService.first, curStartType, strStartType)) { std::stringstream exceptionMsg; exceptionMsg << "Could not get start type for service: " << itService.first; DebugPrintf(SV_LOG_ERROR, exceptionMsg.str().c_str()); continue; } DebugPrintf(SV_LOG_DEBUG, "Service %s Start Type is %s\n", itService.first.c_str(), strStartType.c_str()); // Change service start type if needed if (startType != curStartType) { if (changeServiceType(itService.first, startType) != SVS_OK) { std::stringstream exceptionMsg; exceptionMsg << "Could not enable the service: " << itService.first; DebugPrintf(SV_LOG_ERROR, exceptionMsg.str().c_str()); continue; } DebugPrintf(SV_LOG_DEBUG, "Changed Service %s Start Type to %d\n", itService.first.c_str(), startType); } if (INM_SVCTYPE_DISABLED == startType) { if (INM_SVCSTAT_STOPPED == svcStatus) { DebugPrintf(SV_LOG_DEBUG, "Service %s is already stopped. Skipping stop\n", itService.first.c_str()); continue; } // Stop the service if (StpService(itService.first) != SVS_OK) { std::stringstream exceptionMsg; exceptionMsg << "Could not stop the service: " << itService.first; DebugPrintf(SV_LOG_ERROR, exceptionMsg.str().c_str()); } DebugPrintf(SV_LOG_DEBUG, "Stopped Service %s successfully\n", itService.first.c_str()); continue; } if (INM_SVCTYPE_AUTO == startType) { if (INM_SVCSTAT_RUNNING == svcStatus) { DebugPrintf(SV_LOG_DEBUG, "Service %s is already running. Skipping starting\n", itService.first.c_str()); continue; } if (StartSvc(itService.first) != SVS_OK) { std::stringstream exceptionMsg; exceptionMsg << "Could not start the service: " << itService.first; DebugPrintf(SV_LOG_ERROR, exceptionMsg.str().c_str()); continue; } DebugPrintf(SV_LOG_DEBUG, "Started Service %s successfully\n", itService.first.c_str()); } } DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); } void HostRecoveryManager::DisableEnableVMWareTools(bool bEnable) { DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); std::map<std::string, InmServiceStartType> platformServices; InmServiceStartType startType = bEnable ? INM_SVCTYPE_AUTO : INM_SVCTYPE_DISABLED; platformServices.insert(std::make_pair(VMWARE_TOOLS_SERVICE_NAME, startType)); DisableEnablePlatformServices(platformServices); DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); } void HostRecoveryManager::DisableEnableAzureServices(bool bEnable) { DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); std::map<std::string, InmServiceStartType> platformServices; InmServiceStartType startType = bEnable ? INM_SVCTYPE_AUTO : INM_SVCTYPE_DISABLED; try { LocalConfigurator localConfigurator; std::string azureServices = localConfigurator.getAzureServices(); boost::char_separator<char> delimiter(","); boost::tokenizer < boost::char_separator<char> > services(azureServices, delimiter); for (auto service : services) { // Remove all leading and trailing whitespace auto serviceName = std::regex_replace(service, std::regex("^ +| +$"), "$1"); DebugPrintf(SV_LOG_DEBUG, "Adding service %s StartType: %d\n", serviceName.c_str(), startType); platformServices.insert(std::make_pair(serviceName, startType)); } } catch (const std::exception &ex) { DebugPrintf(SV_LOG_ERROR, "Failed to add azure services. exception: %s\n", ex.what()); return; } DisableEnablePlatformServices(platformServices); DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); } void HostRecoveryManager::ResetReplicationState() { // // Resets the replication state on recovered VM by making following changes: // 1. Reset the filter driver state & clear the bitmap files if exist // 2. Clear the cache settings // DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); InmageDriverInterface inmageDriverInterface; if (!inmageDriverInterface.StopFilteringAll()) { THROW_HOST_REC_EXCEPTION( "Replication state cleanup failed. Manual intervention is required." ); } // Set Device Ids // Query with an invalid device id triggers device rescan // for all uninitialized disks std::string sDeviceId("invaliddevicid"); inmageDriverInterface.GetDriverStats(sDeviceId); // Print all available devices inmageDriverInterface.GetDriverStats(); LocalConfigurator lConfig; // // Clear chache settings files. // std::list<std::string> lstFilesToRemove; std::string configFilesPath; if (LocalConfigurator::getConfigDirname(configFilesPath)) { BOOST_ASSERT(!configFilesPath.empty()); boost::trim(configFilesPath); if (!boost::ends_with(configFilesPath, ACE_DIRECTORY_SEPARATOR_STR_A)) configFilesPath += ACE_DIRECTORY_SEPARATOR_STR_A; std::string cleanupFileList = lConfig.getRecoveryCleanupFileList(); boost::char_separator<char> delm(","); boost::tokenizer < boost::char_separator<char> > strtokens(cleanupFileList, delm); for (boost::tokenizer< boost::char_separator<char> > ::iterator it = strtokens.begin(); it != strtokens.end(); ++it) { /// remove leading and trailing white space if any std::string filename = *it; boost::trim(filename); lstFilesToRemove.push_back(configFilesPath + filename); } } else { THROW_HOST_REC_EXCEPTION( "Could not get the config directory path. Cache files cleanup won't happen" ); } std::list<std::string>::const_iterator iterFile = lstFilesToRemove.begin(); for (; iterFile != lstFilesToRemove.end(); iterFile++) { boost::filesystem::path cache_file(*iterFile); if (boost::filesystem::exists(cache_file)) { DebugPrintf(SV_LOG_INFO, "Removing the file %s\n", iterFile->c_str()); boost::filesystem::remove(cache_file); } else { DebugPrintf(SV_LOG_INFO, "File %s does not exist.\n", iterFile->c_str()); } } DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); } bool HostRecoveryManager::OnlineOfflineResourceDisk(bool bOnline) { DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); InmageDriverInterface inmageDriverIntf; std::set<SV_ULONG> diskIndexList; if (!inmageDriverIntf.GetDiskIndexList(diskIndexList)) { DebugPrintf(SV_LOG_ERROR, "Driver GetDiskIndexList failed with error=%d\n", GetLastError()); return false; } std::set<SV_ULONG> systemDiskIndices; std::string err; DWORD errcode; if (!GetSystemDiskList(systemDiskIndices, err, errcode)) { DebugPrintf(SV_LOG_ERROR, "Failed to get system disk err=%s errCode=%d\n", err.c_str(), errcode); return false; } if (systemDiskIndices.size() == 0) { DebugPrintf(SV_LOG_ERROR, "Failed to get system disk.. No system disk\n"); return false; } std::stringstream ssError; if (systemDiskIndices.size() > 1) { ssError << "More than one system disks:"; for (auto diskIndex : systemDiskIndices) { ssError << " " << diskIndex; } DebugPrintf(SV_LOG_ERROR, "%s\n", ssError.str().c_str()); return false; } SV_ULONG ulSystemDiskIndex = *(systemDiskIndices.begin()); //Offline/Online Resource Disk for (auto diskIndex : diskIndexList) { if (ulSystemDiskIndex == diskIndex) { DebugPrintf(SV_LOG_DEBUG, "Resource Disk Detection: Skipping disk %d as it is boot disk\n", diskIndex); continue; } DiskInterface diskIntf(diskIndex); if (diskIntf.IsResourceDisk()) { DebugPrintf(SV_LOG_INFO, "%s Resource Disk %d\n", (bOnline)? "ONLINE" : "OFFLINE", diskIndex); return (bOnline) ? diskIntf.OnlineDisk() : diskIntf.OfflineDisk(); } } DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); return false; } bool HostRecoveryManager::IsDiskRecoveryRequired(InmageDriverInterface& inmageDriverIntf) { DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); LocalConfigurator lConfig; bool isRecoveryRequired = false; SV_UINT uiWaitTimeSec = lConfig.GetDiskRecoveryWaitTime(); if (uiWaitTimeSec > 0) { DebugPrintf(SV_LOG_ALWAYS, "Waiting for %d secs before invoking Disk Recovery WF\n", uiWaitTimeSec); Sleep(uiWaitTimeSec * 1000); } if (IsWindowsVistaOrGreater() && !IsWindows7OrGreater()) { DebugPrintf(SV_LOG_ALWAYS, "Disk Recovery always needed on windows 2008\n"); return true; } if (inmageDriverIntf.IsDiskRecoveryRequired()) { DebugPrintf(SV_LOG_ALWAYS, "Disk recovery set by driver\n"); DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); return true; } // If system crashed after driver updated boot information.. we need to rely upon registry keys std::map<std::string, DWORD> DriversMap = { { "storvsc", SERVICE_BOOT_START }, { "vmbus", SERVICE_BOOT_START } }; DWORD dwSuccess = RegistryInfo::GetDriversStartType(DriversMap); if (ERROR_SUCCESS != dwSuccess) { DebugPrintf(SV_LOG_DEBUG, "Failed to query start type for drivers stovsc, vmbus\n"); return false; } for (auto driver : DriversMap) { DebugPrintf(SV_LOG_DEBUG, "Driver %s StartType: %d\n", driver.first.c_str(), driver.second); if (SERVICE_BOOT_START != driver.second) { DebugPrintf(SV_LOG_ALWAYS, "Driver %s Type %d Disk Recovery is required\n", driver.first.c_str(), driver.second); isRecoveryRequired = true; break; } } DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); return isRecoveryRequired; } void HostRecoveryManager::RunDiskRecoveryWF(bool& isRebootRequired) { DebugPrintf(SV_LOG_DEBUG, "Entering %s\n", FUNCTION_NAME); LocalConfigurator lc; std::set<SV_ULONG> diskIndexList; InmageDriverInterface inmageDriverIntf; isRebootRequired = false; if (!lc.isMobilityAgent()) { DebugPrintf(SV_LOG_DEBUG, "Skipping resetting vm details as it is not mobility agent\n"); return; } std::map<std::string, DWORD> DriversMap = { { "storvsc", SERVICE_BOOT_START }, { "vmbus", SERVICE_BOOT_START } }; bool isRecoveryRequired = IsDiskRecoveryRequired(inmageDriverIntf); if (!isRecoveryRequired) { DebugPrintf(SV_LOG_ALWAYS, "Disk recovery not needed.. Skipping recovery\n"); DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); return; } for (auto driver : DriversMap) { DriversMap[driver.first] = SERVICE_BOOT_START; } RegistryInfo::SetDriversStartType(DriversMap); HRESULT hr = S_OK; bool isDynamic = false; if (!inmageDriverIntf.GetDiskIndexList(diskIndexList)) { DebugPrintf(SV_LOG_ERROR, "Driver GetDiskIndexList failed with error=%d\n", GetLastError()); return; } for (auto diskIndex : diskIndexList) { DWORD dwErr = IsDiskDynamic(diskIndex, isDynamic); if (ERROR_SUCCESS != dwErr) { DebugPrintf(SV_LOG_ERROR, "Failed to Query DiskType for Disk %d.. Reboot Required", diskIndex); isRebootRequired = true; break; } if (isDynamic) { DebugPrintf(SV_LOG_ALWAYS, "Disk %d is Dynamic.. Reboot Required", diskIndex); isRebootRequired = true; break; } DebugPrintf(SV_LOG_INFO, "Disk %d is Basic\n", diskIndex); } if (!isRebootRequired) { for (auto diskIndex : diskIndexList) { DiskInterface diskIntf(diskIndex); if (!diskIntf.IsResourceDisk()) { DebugPrintf(SV_LOG_ALWAYS, "Onlining Disk %d\n", diskIndex); diskIntf.OnlineDisk(); } } DebugPrintf(SV_LOG_ALWAYS, "Onlining Resource Disk\n"); OnlineOfflineResourceDisk(true); DebugPrintf(SV_LOG_ALWAYS, "Reboot not required as system contains only basic disks\n"); return; } DebugPrintf(SV_LOG_ALWAYS, "Setting san policy to online for all disks\n"); inmageDriverIntf.SetSanPolicyToOnlineForAllDisks(); DebugPrintf(SV_LOG_DEBUG, "Exiting %s\n", FUNCTION_NAME); }