/* To send start request to remote dispatcher and wait reply */
PRL_RESULT Task_MigrateCtSource::SendStartRequest()
{
	CDispToDispCommandPtr pCmd;
	IOSendJob::Response pResponse;

	CDispToDispCommandPtr pMigrateStartCmd = CDispToDispProtoSerializer::CreateVmMigrateStartCommand(
				m_pVmConfig->toString(),
				QString(),
				m_sTargetServerCtHomePath,
				QString(),
				0,
				0,
				m_nMigrationFlags,
				m_nReservedFlags | PVM_CT_MIGRATE | PVM_FULL_DISP_TASK,
				m_nPrevVmState);

	SmartPtr<IOPackage> pPackage = DispatcherPackage::createInstance(
			pMigrateStartCmd->GetCommandId(),
			pMigrateStartCmd->GetCommand()->toString());

	m_hStartReqJob = m_pIoClient->sendPackage(pPackage);
	if (m_pIoClient->waitForSend(m_hStartReqJob, m_nTimeout) != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Package sending failure");
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}
	if (m_pIoClient->waitForResponse(m_hStartReqJob, m_nTimeout) != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Responce receiving failure");
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}
	pResponse = m_pIoClient->takeResponse(m_hStartReqJob);
	if (pResponse.responseResult != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Job failure: responseResult:%x", pResponse.responseResult);
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}

	m_pReply = pResponse.responsePackages[0];
	if (!m_pReply.isValid()) {
		WRITE_TRACE(DBG_FATAL, "Invalid reply");
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}

	if ((m_pReply->header.type != DispToDispResponseCmd) && (m_pReply->header.type != VmMigrateReply)) {
		WRITE_TRACE(DBG_FATAL, "Unexpected response type on migration start command: %d", m_pReply->header.type);
		return PRL_ERR_OPERATION_FAILED;
	}

	pCmd = CDispToDispProtoSerializer::ParseCommand(
			(Parallels::IDispToDispCommands)m_pReply->header.type,
			UTF8_2QSTR(m_pReply->buffers[0].getImpl()));

	if (m_pReply->header.type == DispToDispResponseCmd) {
		CDispToDispResponseCommand *pResponseCmd =
			CDispToDispProtoSerializer::CastToDispToDispCommand<CDispToDispResponseCommand>(pCmd);
		getLastError()->fromString(pResponseCmd->GetErrorInfo()->toString());
		return pResponseCmd->GetRetCode();
	}

	return PRL_ERR_SUCCESS;
}
void CDispToDispProtoSerializerTest::testParseDispToDispResponseCommand()
{
	RESPONSE_CMD_PARAMS_DECLARE
	SmartPtr<CVmEvent> _pkg( new CVmEvent );
	_pkg->setEventCode(nRetCode);
	_pkg->addEventParameter(new CVmEventParameter(PVE::String, pErrorEvent->toString(),
							EVT_PARAM_DISP_TO_DISP_RESPONSE_CMD_ERROR_INFO));
	_pkg->addEventParameter(new CVmEventParameter(PVE::UnsignedInt, QString("%1").arg(nRequestCmdId),
							EVT_PARAM_DISP_TO_DISP_RESPONSE_CMD_REQUEST_ID));
	_pkg->addEventParameter(new CVmEventParameterList(PVE::String, QStringList(),
							EVT_PARAM_DISP_TO_DISP_RESPONSE_CMD_PARAMS_LIST));
	CDispToDispCommandPtr pCmd = CDispToDispProtoSerializer::ParseCommand(DispToDispResponseCmd, _pkg->toString());
	QVERIFY(pCmd->IsValid());
	CDispToDispResponseCommand *pDispToDispResponseCmd =
				CDispToDispProtoSerializer::CastToDispToDispCommand<CDispToDispResponseCommand>(pCmd);
	QCOMPARE(pErrorEvent->toString(), pDispToDispResponseCmd->GetErrorInfo()->toString());
	QCOMPARE((quint32)nRequestCmdId, (quint32)pDispToDispResponseCmd->GetRequestCommandId());
	QCOMPARE((quint32)nRetCode, (quint32)pDispToDispResponseCmd->GetRetCode());
}
void CDispToDispProtoSerializerTest::testDispToDispResponseCommandProcessingListOfParams()
{
	RESPONSE_CMD_PARAMS_DECLARE
	CDispToDispCommandPtr pCmd =
		CDispToDispProtoSerializer::CreateDispToDispResponseCommand(
			nRequestCmdId,
			nRetCode,
			pErrorEvent.getImpl()
		);
	QVERIFY(pCmd->IsValid());
	CDispToDispResponseCommand *pResponseCmd =
		CDispToDispProtoSerializer::CastToDispToDispCommand<CDispToDispResponseCommand>(pCmd);
	pResponseCmd->AddParam(pErrorEvent->toString());
	pResponseCmd->AddParam(pErrorEvent->toString());
	pResponseCmd->AddParam(pErrorEvent->toString());
	pCmd = CDispToDispProtoSerializer::ParseCommand(DispToDispResponseCmd, pCmd->GetCommand()->toString());
	pResponseCmd = CDispToDispProtoSerializer::CastToDispToDispCommand<CDispToDispResponseCommand>(pCmd);
	QStringList lstParams = pResponseCmd->GetParams();
	QCOMPARE(quint32(lstParams.size()), quint32(3));
	foreach(QString sParam, lstParams)
		QCOMPARE(pErrorEvent->toString(), sParam);
}
/*
* Check preconditions
* return PRL_ERR_SUCCESS - no errors
* 	 PRL_ERR_VM_MIGRATE_CHECKING_PRECONDITIONS_FAILED - precondition error
*	 other - internal error
*/
PRL_RESULT Task_MigrateCtSource::CheckVmMigrationPreconditions()
{
	PRL_RESULT nRetCode = PRL_ERR_SUCCESS;

	CDispToDispCommandPtr pRequest =
		CDispToDispProtoSerializer::CreateVmMigrateCheckPreconditionsCommand(
			m_pVmConfig->toString(),
			m_cHostInfo.toString(),
			m_sTargetServerCtHomePath,
			QString(),
			QStringList(),
			QString(),
			0,
			m_nMigrationFlags,
			m_nReservedFlags | PVM_CT_MIGRATE | PVM_FULL_DISP_TASK,
			m_nPrevVmState
		);

	SmartPtr<IOPackage> pPackage =
		DispatcherPackage::createInstance(pRequest->GetCommandId(), pRequest->GetCommand()->toString());
	SmartPtr<IOPackage> pReply;
	IOSendJob::Response pResponse;

	m_hCheckReqJob = m_pIoClient->sendPackage(pPackage);
	if (m_pIoClient->waitForSend(m_hCheckReqJob, m_nTimeout) != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Package sending failure");
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}
	if (m_pIoClient->waitForResponse(m_hCheckReqJob, m_nTimeout) != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Responce receiving failure");
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}
	pResponse = m_pIoClient->takeResponse(m_hCheckReqJob);
	if (pResponse.responseResult != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Job failure: responseResult:%x", pResponse.responseResult);
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}

	pReply = pResponse.responsePackages[0];
	if (!pReply.isValid()) {
		WRITE_TRACE(DBG_FATAL, "Invalid reply");
		return PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
	}
	if ((pReply->header.type != DispToDispResponseCmd) && (pReply->header.type != VmMigrateCheckPreconditionsReply))
	{
		WRITE_TRACE(DBG_FATAL, "Unexpected response type (%d)", pReply->header.type);
		return PRL_ERR_OPERATION_FAILED;
	}

	CDispToDispCommandPtr pCmd =
		CDispToDispProtoSerializer::ParseCommand(
			DispToDispResponseCmd,
			UTF8_2QSTR(pReply->buffers[0].getImpl())
		);

	if (pReply->header.type == DispToDispResponseCmd) {
		CDispToDispResponseCommand *pResponseCmd =
			CDispToDispProtoSerializer::CastToDispToDispCommand<CDispToDispResponseCommand>(pCmd);
		nRetCode = pResponseCmd->GetRetCode();
		getLastError()->fromString(pResponseCmd->GetErrorInfo()->toString());
		return nRetCode;
	} else {
		CVmMigrateCheckPreconditionsReply *pResponseCmd =
			CDispToDispProtoSerializer::CastToDispToDispCommand<CVmMigrateCheckPreconditionsReply>(pCmd);
		m_lstCheckPrecondsErrors = pResponseCmd->GetCheckPreconditionsResult();
		m_nReservedFlags = pResponseCmd->GetCommandFlags();
		if (!(m_nReservedFlags & PVM_CT_MIGRATE)) {
			/* migration of the containers does not supports */
			WRITE_TRACE(DBG_FATAL, "Remote server does not support migration of the containers");
			return PRL_ERR_UNIMPLEMENTED;
		}
	}
	if (!m_lstCheckPrecondsErrors.isEmpty())
		nRetCode = PRL_ERR_VM_MIGRATE_CHECKING_PRECONDITIONS_FAILED;

	return nRetCode;
}
PRL_RESULT Task_MigrateCtSource::run_body()
{
	SmartPtr<CVmEvent> pEvent;
	SmartPtr<IOPackage> pPackage;
	IOSendJob::Response pResponse;
	CDispToDispCommandPtr pCmd;
	CDispToDispResponseCommand *pRespCmd;

	//https://bugzilla.sw.ru/show_bug.cgi?id=267152
	CAuthHelperImpersonateWrapper _impersonate( &getClient()->getAuthHelper() );

	if (operationIsCancelled())
		setLastErrorCode(PRL_ERR_OPERATION_WAS_CANCELED);

	PRL_RESULT nRetCode = PRL_ERR_SUCCESS;

	if (PRL_FAILED(nRetCode = CheckVmMigrationPreconditions()))
		goto exit;

	/* set migration mode */
	switch (m_nPrevVmState) {
	case VMS_RUNNING:
	case VMS_PAUSED:
		nRetCode = PRL_ERR_VM_MIGRATE_WARM_MODE_NOT_SUPPORTED;
		goto exit;
	default:
		m_nMigrationFlags |= PVMT_COLD_MIGRATION;
	}

	if (operationIsCancelled())
		setLastErrorCode(PRL_ERR_OPERATION_WAS_CANCELED);

	pEvent = SmartPtr<CVmEvent>(new CVmEvent(PET_DSP_EVT_VM_MIGRATE_STARTED, m_sVmUuid, PIE_DISPATCHER));
	pEvent->addEventParameter(new CVmEventParameter(PVE::Boolean, "true", EVT_PARAM_MIGRATE_IS_SOURCE));
	pPackage = DispatcherPackage::createInstance(PVE::DspVmEvent, pEvent->toString());

	m_nSteps |= MIGRATE_VM_STATE_CHANGED;
	/* and notify clients about VM migration start event */
	CDspService::instance()->getClientManager().sendPackageToVmClients(pPackage, m_sVzDirUuid, m_sVmUuid);

	/* remove target Vm config from watcher (#448235) */
	CDspService::instance()->getVmConfigWatcher().unregisterVmToWatch(m_sVmConfigPath);
	m_nSteps |= MIGRATE_UNREGISTER_VM_WATCH;

	nRetCode = migrateStoppedCt();
	if (PRL_FAILED(nRetCode))
		goto exit;

	/* wait finish reply from target (https://jira.sw.ru/browse/PSBM-9596) */
	quint32 nTimeout = m_nTimeout;

	/* wait target task finish */
	if (PVMT_CHANGE_SID & getRequestFlags())
		/* wait reply during changeSID task timeout (https://jira.sw.ru/browse/PSBM-9733) */
		nTimeout = CHANGESID_TIMEOUT;
	if (m_pIoClient->waitForResponse(m_hCheckReqJob, nTimeout) != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Finish acknowledgement receiving failure");
		nRetCode = PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
		goto exit;
	}
	pResponse = m_pIoClient->takeResponse(m_hCheckReqJob);
	if (pResponse.responseResult != IOSendJob::Success) {
		WRITE_TRACE(DBG_FATAL, "Finish acknowledgement receiving failure");
		nRetCode = PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
		goto exit;
	}
	pPackage  = pResponse.responsePackages[0];
	if (pPackage->header.type != DispToDispResponseCmd) {
		WRITE_TRACE(DBG_FATAL, "Invalid package type : %d", pPackage->header.type);
		nRetCode = PRL_ERR_CT_MIGRATE_INTERNAL_ERROR;
		goto exit;
	}

	pCmd = CDispToDispProtoSerializer::ParseCommand(
		DispToDispResponseCmd, UTF8_2QSTR(pPackage->buffers[0].getImpl()));
	pRespCmd = CDispToDispProtoSerializer::CastToDispToDispCommand<CDispToDispResponseCommand>(pCmd);
	nRetCode = pRespCmd->GetRetCode();
	if (PRL_FAILED(nRetCode))
		getLastError()->fromString(pRespCmd->GetErrorInfo()->toString());

exit:
	setLastErrorCode(nRetCode);
	return nRetCode;
}