host/AzureRecoveryUtil/main.cpp (484 lines of code) (raw):
/*
+------------------------------------------------------------------------------------+
Copyright(c) Microsoft Corp. 2015
+------------------------------------------------------------------------------------+
File : main.cpp
Description : Implements the command line interface for AzureRecoveryUtil.
History : 7-5-2015 (Venu Sivanadham) - Created
+------------------------------------------------------------------------------------+
*/
#include <string>
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include "command_options.h"
#include "AzureRecovery.h"
#include "common/Trace.h"
namespace po = boost::program_options;
/*
Method : Usage
Description : Disaplays the AzureRecovery Util usage
Parameters : [in] options: options description
[in] optype: represents type of operation. It can be empty if operation-type info
not available.
Return code : None
*/
void Usage(po::options_description& options, const std::string& optype = "")
{
std::string operation_sepcifc_msg = "Usage: --operation " +
(optype.empty() ? std::string("<operation-type>") : optype) +
" [other options]";
std::cout << std::endl
<< operation_sepcifc_msg << std::endl
<< std::endl
<< options << std::endl;
}
/*
Method : UpdateMetadataStatus
Description : Uploads the Metadata status to blob or local test file.
Parameters : [in] operationScenario: OperationScenario.
Return code : true is update is successful, false otherwise.
*/
bool UpdateMetadataStatus(const std::string& operationScenario)
{
if (boost::iequals(operationScenario, OPERATION::GENCONVERSION) ||
boost::iequals(operationScenario, OPERATION::RECOVERY) ||
boost::iequals(operationScenario, OPERATION::MIGRATION))
{
if (UpdateStatusToBlob(GetCurrentStatus()))
{
std::cerr << "Status update error" << std::endl;
}
else
{
return true;
}
}
else
{
if (UpdateStatusToTestFile(GetCurrentStatus()))
{
std::cerr << "Status update to local file failed." << std::endl;
}
else
{
return true;
}
}
return false;
}
/*
Method : StartStatusUpdate
Description : Updates the status information specified on command line options to status blob.
Parameters : command line arguments
Return code : 0 on success, any other interger on failure.
*/
int StartStatusUpdate(int argc, char* argv[], const std::string& operationScenario, bool help)
{
std::string rec_conf_file;
std::string task_desc;
std::string error_msg;
std::string execution_status;
int error_code = 0;
int progress = 0;
try
{
po::options_description status_opt("Status Update options");
status_opt.add_options()
(CMD_OPTION::RECV_INFO_FILE,
po::value<std::string>(&rec_conf_file),
"Recovery config filepath"
)
(CMD_OPTION::STATUS,
po::value<std::string>(&execution_status),
"Execution status [Processing/Success/Failed]"
)
(CMD_OPTION::TASK_DESC,
po::value<std::string>(&task_desc),
"Description of current task"
)
(CMD_OPTION::ERROR_CODE,
po::value<int>(&error_code),
"Error code (number)"
)
(CMD_OPTION::ERROR_MSG,
po::value<std::string>(&error_msg),
"Error Message"
)
(CMD_OPTION::PROGRESS,
po::value<int>(&progress),
"Execution progress [0 - 100]"
);
if (help)
{
Usage(status_opt, OPERATION::UPDATE_STATUS);
return 0;
}
po::command_line_parser parser(argc, argv);
parser.options(status_opt).allow_unregistered();
po::variables_map cmd_values;
po::store(parser.run(), cmd_values);
po::notify(cmd_values);
if (!cmd_values.count(CMD_OPTION::STATUS) ||
!cmd_values.count(CMD_OPTION::TASK_DESC) ||
!cmd_values.count(CMD_OPTION::ERROR_CODE) ||
!cmd_values.count(CMD_OPTION::ERROR_MSG) ||
!cmd_values.count(CMD_OPTION::PROGRESS) ||
!cmd_values.count(CMD_OPTION::RECV_INFO_FILE))
{
std::cout << "Command error: required argument missing" << std::endl;
Usage(status_opt, OPERATION::UPDATE_STATUS);
return 1;
}
}
catch (boost::program_options::error& exp)
{
std::cout << "Status update Command error:" << std::endl;
std::cerr << exp.what() << std::endl;
return 1;
}
//
// Log for status updates. This log will not be uploaded to status blob.
//
Trace::Init(STATUS_WORKFLOW_LOG_FILE);
if (0 != InitStatusConfig(rec_conf_file))
{
TRACE_ERROR("Could not update status\n");
return 1;
}
RecoveryUpdate statusUpdate;
statusUpdate.ErrorCode = error_code;
statusUpdate.ErrorMsg = error_msg;
statusUpdate.Status = execution_status;
statusUpdate.Progress = progress;
statusUpdate.TaskDescription = task_desc;
return UpdateStatusToBlob(statusUpdate);
}
/*
Method : StartRecovery
Description : Starts the pre-recovery steps execution if the command line options are valid.
Parameters : command line arguments
Return code : 0 on success, any other interger on failure. Even on recovery steps execution failure
the function still returns 0, but the status would be set to failed on status blob.
*/
int StartRecovery(int argc, char* argv[], const std::string& operationScenario, bool help)
{
std::string hostinfo_file;
std::string recv_info_file;
std::string working_dir;
std::string hydration_config_settings;
try
{
po::options_description rec_opt("Recovery options");
rec_opt.add_options()
(CMD_OPTION::RECV_INFO_FILE,
po::value<std::string>(&recv_info_file),
"Recovery configuration file path"
)
(CMD_OPTION::HOSTINFO_FILE,
po::value<std::string>(&hostinfo_file),
"Host info xml file path"
)
(CMD_OPTION::WORKING_DIR,
po::value<std::string>(&working_dir),
"working directory for the tool"
)
(CMD_OPTION::HYDRATION_CONFIG_SETTINGS,
po::value<std::string>(&hydration_config_settings),
"Config settings for operations in hydration");
if (help)
{
Usage(rec_opt, OPERATION::RECOVERY);
return 0;
}
po::command_line_parser parser(argc, argv);
parser.options(rec_opt).allow_unregistered();
po::variables_map cmd_values;
po::store(parser.run(), cmd_values);
po::notify(cmd_values);
if (!cmd_values.count(CMD_OPTION::WORKING_DIR) ||
!cmd_values.count(CMD_OPTION::HOSTINFO_FILE) ||
!cmd_values.count(CMD_OPTION::RECV_INFO_FILE))
{
std::cout << "Command error: required argument missing" << std::endl;
Usage(rec_opt, OPERATION::RECOVERY);
return 1;
}
}
catch (boost::program_options::error& exp)
{
std::cout << "Recovery Command error:" << std::endl;
std::cerr << exp.what() << std::endl;
return 1;
}
//
// Initialize logger
//
std::string LogFile = working_dir +
( boost::ends_with(working_dir, ACE_DIRECTORY_SEPARATOR_STR_A) ?
"" : ACE_DIRECTORY_SEPARATOR_STR_A ) +
std::string(RECOVERY_UTIL_LOG_FILE);
Trace::Init(LogFile);
do
{
//
// Initialize the configuration objects. If initialization fails then update the
// status to blob and exit.
//
int ret_code = InitRecoveryConfig(recv_info_file, hostinfo_file, working_dir, hydration_config_settings);
//
// Update the current execution status to status-blob if it's a production scenario.
//
if (boost::iequals(operationScenario, OPERATION::RECOVERY) &&
UpdateStatusToBlob(GetCurrentStatus()))
{
std::cerr << "Status update error" << std::endl;
}
if (0 != ret_code)
break;
//
// Start data massaging steps. Here the StartRecovery return code is not considering
// as its already reflected to execution status error code.
//
StartRecovery();
//
// Upload the log file content to status blob.
// Do not call for tests.
//
if (boost::iequals(operationScenario, OPERATION::RECOVERY))
{
UploadCurrentTraceLog(LogFile);
}
//
// Update the current execution status to status-blob
//
int retryCount = 3;
while (retryCount-- > 0)
{
UpdateMetadataStatus(operationScenario);
if (retryCount == 0)
{
return 1;
}
}
} while (false);
return 0;
}
/*
Method : StartUploadLog
Description : Uploads the the execution trace log as status blob content if the commandline
arguments are proper.
Parameters : command line arguments
Return code : 0 on success, any other interger on failure.
*/
int StartUploadLog(int argc, char* argv[], bool help)
{
std::string rec_conf_file;
std::string log_file;
try
{
po::options_description log_opt("Execution Log upload options");
log_opt.add_options()
(CMD_OPTION::RECV_INFO_FILE,
po::value<std::string>(&rec_conf_file),
"Recovery config filepath"
)
(CMD_OPTION::LOG_FILE,
po::value<std::string>(&log_file),
"Upload log file path"
);
if (help)
{
Usage(log_opt, OPERATION::UPLOAD_LOG);
return 0;
}
po::command_line_parser parser(argc, argv);
parser.options(log_opt).allow_unregistered();
po::variables_map cmd_values;
po::store(parser.run(), cmd_values);
po::notify(cmd_values);
if (!cmd_values.count(CMD_OPTION::LOG_FILE) ||
!cmd_values.count(CMD_OPTION::RECV_INFO_FILE))
{
std::cout << "Command error: required argument missing" << std::endl;
Usage(log_opt, OPERATION::UPLOAD_LOG);
return 1;
}
}
catch (boost::program_options::error& exp)
{
std::cout << "Log upload Command error:" << std::endl;
std::cerr << exp.what() << std::endl;
return 1;
}
//
// Trace log for logupload. This log will not be uploaded to status blob.
//
Trace::Init(STATUS_WORKFLOW_LOG_FILE);
int ret_code = InitStatusConfig(rec_conf_file);
if (0 == ret_code)
{
ret_code = UploadExecutionLog(log_file);
}
return ret_code;
}
/*
Method : StartMigration
Description : Starts the migration steps execution if the command line options are valid.
Parameters : command line arguments
operationScenario: Differentiates between Production and Test scenarion for Status and Hydration log updates.
Return code : 0 on success, any other integer on failure. Even on recovery steps execution failure
the function still returns 0, but the status would be set to failed on status blob.
*/
int StartMigration(int argc, char* argv[], const std::string& operationScenario, bool help)
{
std::string recv_info_file;
std::string working_dir;
std::string hydration_config_settings;
try
{
po::options_description rec_opt("Migration options");
rec_opt.add_options()
(CMD_OPTION::RECV_INFO_FILE,
po::value<std::string>(&recv_info_file),
"Recovery configuration file path")
(CMD_OPTION::WORKING_DIR,
po::value<std::string>(&working_dir),
"working directory for the tool")
(CMD_OPTION::HYDRATION_CONFIG_SETTINGS,
po::value<std::string>(&hydration_config_settings),
"Config settings for operations in hydration");
if (help)
{
Usage(rec_opt, OPERATION::MIGRATION);
return 0;
}
po::command_line_parser parser(argc, argv);
parser.options(rec_opt).allow_unregistered();
po::variables_map cmd_values;
po::store(parser.run(), cmd_values);
po::notify(cmd_values);
if (!cmd_values.count(CMD_OPTION::WORKING_DIR) ||
!cmd_values.count(CMD_OPTION::RECV_INFO_FILE))
{
std::cout << "Command error: required argument missing" << std::endl;
Usage(rec_opt, OPERATION::MIGRATION);
return 1;
}
}
catch (boost::program_options::error& exp)
{
std::cout << "Recovery Command error:" << std::endl;
std::cerr << exp.what() << std::endl;
return 1;
}
//
// Initialize logger
//
std::string LogFile = working_dir +
(boost::ends_with(working_dir, ACE_DIRECTORY_SEPARATOR_STR_A) ?
"" : ACE_DIRECTORY_SEPARATOR_STR_A) +
std::string(RECOVERY_UTIL_LOG_FILE);
Trace::Init(LogFile);
do
{
//
// Initialize the configuration objects. If initialization fails then update the
// status to blob and exit.
//
int ret_code = InitMigrationConfig(recv_info_file, working_dir, hydration_config_settings);
//
// Update the current execution status to status-blob
//
if (boost::iequals(operationScenario, OPERATION::MIGRATION) &&
UpdateStatusToBlob(GetCurrentStatus()))
{
std::cerr << "Status update error" << std::endl;
}
if (0 != ret_code)
break;
//
// Start migration steps.
// Here the StartMigration return code is not considering
// as its already reflected to execution status error code.
//
StartMigration();
//
// Upload the log file content to status blob
//
if (boost::iequals(operationScenario, OPERATION::MIGRATION))
{
UploadCurrentTraceLog(LogFile);
}
//
// Update the current execution status to status-blob
//
int retryCount = 3;
while (retryCount-- > 0)
{
UpdateMetadataStatus(operationScenario);
if (retryCount == 0)
{
return 1;
}
}
} while (false);
return 0;
}
/*
Method : StartGenConversion
Description : Starts the gen conversion steps execution if the command line options are valid.
Parameters : command line arguments
Return code : 0 on success, any other interger on failure. Even on failure the function
returns 0, but the status would be set to failed on status blob.
*/
int StartGenConversion(int argc, char* argv[], const std::string& operationScenario, bool help)
{
std::string recv_info_file;
std::string working_dir;
std::string hydration_config_settings;
try
{
po::options_description rec_opt("Gen conversion options");
rec_opt.add_options()
(CMD_OPTION::RECV_INFO_FILE,
po::value<std::string>(&recv_info_file),
"Recovery configuration file path")
(CMD_OPTION::WORKING_DIR,
po::value<std::string>(&working_dir),
"working directory for the tool")
(CMD_OPTION::HYDRATION_CONFIG_SETTINGS,
po::value<std::string>(&hydration_config_settings),
"Config settings for operations in hydration");
if (help)
{
Usage(rec_opt, OPERATION::GENCONVERSION);
return 0;
}
po::command_line_parser parser(argc, argv);
parser.options(rec_opt).allow_unregistered();
po::variables_map cmd_values;
po::store(parser.run(), cmd_values);
po::notify(cmd_values);
if (!cmd_values.count(CMD_OPTION::WORKING_DIR) ||
!cmd_values.count(CMD_OPTION::RECV_INFO_FILE))
{
std::cout << "Command error: required argument missing" << std::endl;
Usage(rec_opt, OPERATION::MIGRATION);
return 1;
}
}
catch (boost::program_options::error& exp)
{
std::cout << "GenConversion Command error:" << std::endl;
std::cerr << exp.what() << std::endl;
return 1;
}
//
// Initialize logger
//
std::string LogFile = working_dir +
(boost::ends_with(working_dir, ACE_DIRECTORY_SEPARATOR_STR_A) ?
"" : ACE_DIRECTORY_SEPARATOR_STR_A) +
std::string(RECOVERY_UTIL_LOG_FILE);
Trace::Init(LogFile);
do
{
//
// Initialize the configuration objects. If initialization fails
// then update the status to blob and exit.
// Config initialization is same for migration and genconversion.
//
int ret_code = InitMigrationConfig(recv_info_file, working_dir, hydration_config_settings);
//
// Update the current execution status to status-blob
//
if (boost::iequals(operationScenario, OPERATION::GENCONVERSION) &&
UpdateStatusToBlob(GetCurrentStatus()))
{
std::cerr << "Status update error" << std::endl;
}
if (0 != ret_code)
break;
//
// Start genconversion steps.
// Here the StartGenConversion return code is not considering
// as its already reflected to execution status error code.
//
StartGenConversion();
//
// Upload the log file content to status blob
//
if (boost::iequals(operationScenario, OPERATION::GENCONVERSION))
{
UploadCurrentTraceLog(LogFile);
}
//
// Update the current execution status to status-blob
// Retry a maximum of three times.
//
int retryCount = 3;
while (retryCount-- > 0)
{
UpdateMetadataStatus(operationScenario);
if (retryCount == 0)
{
return 1;
}
}
} while (false);
return 0;
}
int main(int argc, char* argv[])
{
int ret_code = 0;
bool bHelp = false;
std::string operationtype;
GlobalInit();
try
{
po::options_description common("Azure Recovery tool command line options");
common.add_options()
(CMD_OPTION::OPERATION,
po::value<std::string>(&operationtype),
"Type of operation [recovery/updatestatus/uploadlog/migration]"
)
(CMD_OPTION::HELP,
po::value<std::string>(&operationtype),
"Help options [recovery/updatestatus/uploadlog/migration]"
);
po::command_line_parser parser(argc, argv);
parser.options(common).allow_unregistered();
po::variables_map cmd_values;
po::store(parser.run(), cmd_values);
po::notify(cmd_values);
bHelp = cmd_values.count(CMD_OPTION::HELP);
if (boost::iequals(operationtype, OPERATION::RECOVERY))
{
ret_code = StartRecovery(argc, argv, (std::string)OPERATION::RECOVERY, bHelp);
}
else if (boost::iequals(operationtype, OPERATION::UPDATE_STATUS))
{
ret_code = StartStatusUpdate(argc, argv, (std::string)OPERATION::UPDATE_STATUS, bHelp);
}
else if (boost::iequals(operationtype, OPERATION::UPLOAD_LOG))
{
ret_code = StartUploadLog(argc, argv, bHelp);
}
else if (boost::iequals(operationtype, OPERATION::MIGRATION))
{
ret_code = StartMigration(argc, argv, (std::string)OPERATION::MIGRATION ,bHelp);
}
else if (boost::iequals(operationtype, OPERATION::GENCONVERSION))
{
ret_code = StartGenConversion(argc, argv, (std::string)OPERATION::GENCONVERSION,bHelp);
}
else if (boost::iequals(operationtype, OPERATION::MIGRATION_TEST))
{
ret_code = StartMigration(argc, argv, (std::string)OPERATION::MIGRATION_TEST, bHelp);
}
else if (boost::iequals(operationtype, OPERATION::GENCONVERSION_TEST))
{
ret_code = StartGenConversion(argc, argv, (std::string)OPERATION::GENCONVERSION_TEST, bHelp);
}
else if (boost::iequals(operationtype, OPERATION::RECOVERY_TEST))
{
ret_code = StartRecovery(argc, argv, (std::string)OPERATION::RECOVERY_TEST, bHelp);
}
else if (boost::iequals(operationtype, OPERATION::STATUS_UPDATE_TEST))
{
ret_code = StartStatusUpdate(argc, argv, (std::string)OPERATION::STATUS_UPDATE_TEST, bHelp);
}
else
{
std::cout << "Command Error: Invalid command option" << std::endl << std::endl;
Usage(common);
ret_code = 1;
}
}
catch (boost::program_options::error& exp)
{
std::cout << "Command error:" << std::endl << std::endl;
std::cerr << exp.what() << std::endl;
ret_code = 1;
}
catch (...)
{
std::cout << "Unkown exception" << std::endl << std::endl;
ret_code = 1;
}
GlobalUnInit();
return ret_code;
}