public void Skip(string followedPath) { TodoJob tmp = new TodoJob(-1, followedPath); while (tmp.HasNextAction()) { Action cTmp = tmp.NextAction(); Action mTmp = NextAction(); if (!cTmp.relativePath.Equals(mTmp.relativePath)) { throw new ArgumentException("Paths are different!\n " + _actionPath + " - " + tmp._actionPath); } } }
// This is the method executed by the thread. It will manage the interaction with the guest process, sending commands and receiving informations about the Intaller UI and logging system. private void Run() { #region Declaration and initializations _status = NetworkWorkerStatus.Busy; _dbm = DBManagerFactory.NewManager(); _noLoop = new List <string>(); _mainJob = null; _todoJob = null; string followedPath = null; _followedPathBitmaps = new List <string>(); string remoteIp = ((IPEndPoint)_socket.Client.RemoteEndPoint).Address.ToString(); int remotePort = ((IPEndPoint)_socket.Client.RemoteEndPoint).Port; #endregion /*------------------------------------------------------------ * * PHASE 1 * Retrive job from DB and send it to the guest. * * ----------------------------------------------------------- */ try { #region First Setp: Get The Job from the DB and send it to the remote guest /*----------------------------------------------------------- * ---------------------------------------------------------- * Get the Job from the DB and send it to the Guest * ---------------------------------------------------------- * ----------------------------------------------------------*/ while (_todoJob == null) { // Now the machine will send a Request every minute to get more work. Expect it! ReceiveCallForWork(); lock (_lock) { _todoJob = _dbm.PopTodoJob(out _mainJob); } if (_mainJob == null) { // There's nothing to do... wait please! SendNoJobs(); Log("No jobs found for that worker..."); Log("Worker thread started with guest " + ((IPEndPoint)_socket.Client.RemoteEndPoint).Address + ":" + ((IPEndPoint)_socket.Client.RemoteEndPoint).Port); _status = NetworkWorkerStatus.Ready; } else { SendWorkReady(); // Now the loop will stop // break; } } // Send Work to the remote guest Log("Sending work to remote guest: MAINJOIB ID " + _mainJob.Id + " (" + _mainJob.InstallerPath + "), TODOJOB ID " + _todoJob.Id + " (" + _todoJob.ActionPath + ")."); SendWork(); // Wait for ACK, meaning Process started correctly Log("Waiting for remote process start"); ReceiveAck(); Log("Remote process started"); //Stats.Instance.notifyWork((IPEndPoint)_socket.Client.RemoteEndPoint,_mainJob,_todoJob); #endregion } catch (Exception e) // Error handling for phase 1 { // In case of exception, if any job has been retrived, // remove it from TODO-Table and move it to the done table. // Add error logs both to the local log and to the XML logfile in the DONE table. #region Error handling for phase 1 // The job has been popped from db. // Log the error locally Logger.Instance.logError("Unhandled exception in Worker Manager in phase 1. TodoJob ID = " + _todoJob.Id, e); Log("Unhandled exception in Worker Manager in phase 1. TodoJob ID = " + _todoJob.Id + "\n" + e.Message + "\n" + e.StackTrace); // Log it into the db string xmlErrorLog = BuildHostXmlError("Unhandled exception in Worker Manager in phase 1", e, _todoJob, _mainJob, _vmName, followedPath); if (_todoJob != null) { // Set that job as done _dbm.InsertDoneWithError(NetworkProtocol.Protocol.URI_APP_START, _mainJob, xmlErrorLog); // Now delete the todo job from the db _dbm.DeleteTodoJob(_todoJob); } // Finally revert the VM VMManager.Instance.RevertVm(_vmName); // Clear stuff and return _dbm.Dispose(); _sReader.Close(); _sWriter.Close(); _socket.Close(); return; #endregion } /*------------------------------------------------------------ * * PHASE 2 * Following the stored path of the current todojob. * * ----------------------------------------------------------- */ followedPath = Protocol.URI_APP_START; try { #region Second Step: interact with UI until FollowedPath == todojob.FullPath Log("Following path: " + _todoJob.ActionPath); while (followedPath != _todoJob.ActionPath) { // 1. Check that remote process is running Log("Receiving process status"); Int16 procStatus = ReceiveProcessStatus(); if (procStatus != Protocol.PROCESS_GOING) { // This is unespected, so throw an exception throw new ProtocolException("Error during path following: The remote process is not running."); } Log("Receiving possible interactions"); // 2. Receive the list of possible interaction to perform with the UI PossibleInteraction res = ReceivePossibleInteractions(); // 3. Check if the window has been already scanned: if yes, throw an exception, otherwise add the hash to the scanned windows list if (_noLoop.Contains(res.winStatusHash)) { throw new LoopException(); } else { _noLoop.Add(res.winStatusHash); } // 4. Check if the next action to perform is into the list of the actions received by the Guest TodoJob.Action next = _todoJob.NextAction(); /* * bool controlFound = false; * bool interactionFound = false; * for (int i = 0; i < res.ids.Length; i++) * { * if (next.controlId == res.ids[i]) * { * controlFound = true; * for (int j = 0; j < res.interactions[i].Length; j++) * { * if (res.interactions[i][j] == next.interactionType) * { * interactionFound = true; * break; * } * } * break; * } * } * if (!interactionFound) * { * Log("Cannot follow the written path. The control I was looking for is absent."); * // The interaction we were looking for is not present into the collection received by the guest. Throw an exception then! * if (!controlFound) * throw new InconsistentPathException("Error: inconsistent path. The guest has no control ID " + next.controlId); * else * throw new InconsistentPathException("Error: inconsistent path. The control ID " + next.controlId + " doesn't allow interactiontype " + next.interactionType); * } */ // 5. Execute the interaction Log("Executing interaction..."); if (SendCommand(next.controlId, next.interactionType)) { // Mark the choosen element int id = -1; for (int i = 0; i < res.ids.Length; i++) { if (next.controlId == res.ids[i]) { id = i; break; } } if (id == -1) { throw new ApplicationException("Inconsistency error: no control id found within the RES array."); } DrawAndStoreBitmap(res.bitmap, (int)res.topLeftX[id], (int)res.topLeftY[id], (int)(res.bottomRightX[id] - res.topLeftX[id]), (int)(res.topLeftY[id] - res.bottomRightY[id])); // Everything is good, update the path followedPath += next.relativePath + Protocol.URI_PATH_SEP; Log("Interaction completed succesfully. New path is: " + followedPath); } else { throw new GuestCommandFailedException("Guest reported a problem when executing the action " + next.controlId + "=" + next.interactionType); } } #endregion } catch (Exception e) { // If a loop is detected, save the path in DONE table with an appropriate error message. Then remove that TODO entry and all the others // starting like that from the TODO table. After that,simply ask the remote guest to reboot. #region Error Handling for phase 2 Log("Loop exception catched during Pahse 2."); string xmlError = BuildHostXmlError("Loop detected during phase 2.", e, _todoJob, _mainJob, _vmName, followedPath); Log("Inserting done job to the DB"); _dbm.InsertDoneWithError(NetworkProtocol.Protocol.URI_APP_START, _mainJob, xmlError); Log("Deleting todojob starting with: " + followedPath); _dbm.DeleteTodoJobsStartingWithPath(followedPath); Log("Deleting this todojob: " + _todoJob.ActionPath); _dbm.DeleteTodoJob(_todoJob); Log("Reverting VM..."); VMManager.Instance.RevertVm(_vmName); Stats.Instance.notifyLoop(); Log("Clearing resources and returning."); _sReader.Close(); _sWriter.Close(); _socket.Close(); _dbm.Dispose(); Log("Returning."); return; #endregion } /*------------------------------------------------------------ * * PHASE 3 * Discovering paths * * ----------------------------------------------------------- */ try { #region Phase 3: interact with the UI until the remtoe process is running Int16 guestStatus; Log("Path completely followed, now it begins path discovery."); while ((guestStatus = ReceiveProcessStatus()) == Protocol.PROCESS_GOING) { Log("Process status RUNNING received."); // 1. Receive all possible interactions. This means also the Window has will be received. Log("Getting possible interactions..."); PossibleInteraction res = ReceivePossibleInteractions(); // 2. Check the window hash: if it is already present into the noLoop array, throw a loop exception. Otherwise simply add the has to that array if (_noLoop.Contains(res.winStatusHash)) { throw new LoopException(); } else { _noLoop.Add(res.winStatusHash); } // 3. Now filter all the possible interactions received. Must delete all the already done actions (DONE TABLE), all the ones which are already present into the TODO table. List <string> allPossibleInteractions = BuildPaths(followedPath, res); List <string> toDo = _dbm.FilterDoneOrTodoByPath(allPossibleInteractions); Log("Discovered " + toDo.Count + " new paths."); if (toDo.Count == 0) { Log("I don't know what to do next!!!!!!!!"); // There's nothing to do next: Reboot the guest _dbm.DeleteTodoJob(_todoJob); VMManager.Instance.RevertVm(_vmName); //throw new NotSupportedException("Check this: should I add this done path to the DB?"); // I guess I should break in that case, collect info and reboot the remote guest // break; } else { // 4. Pop one of the actions filtered and save the others into the TODO table string fullPathToPerform = toDo.First(); // Copied or not? toDo.RemoveAt(0); // Now is fullPathToPerform still valid? foreach (string s in toDo) { _dbm.InsertAction(_mainJob.Id, s); } // Now proceed with the choosen one string tmp = fullPathToPerform.Split(new string[] { Protocol.URI_PATH_SEP }, StringSplitOptions.RemoveEmptyEntries).Last(); string cId = tmp.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[0]; int cType = int.Parse(tmp.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries)[1]); Log("Saved all the others command to the DB. I'bve choosen the following: " + cId + "=" + cType); if (!SendCommand(cId, cType)) { Log("Error during remote command execution... command failed."); throw new GuestCommandFailedException("The guest failed executing command " + cId + "=" + cType); } else { // Mark the choosen element int id = -1; for (int i = 0; i < res.ids.Length; i++) { if (cId == res.ids[i]) { id = i; break; } } if (id == -1) { throw new ApplicationException("Inconsistency error: no control id found within the RES array."); } DrawAndStoreBitmap(res.bitmap, (int)(res.topLeftX[id]), (int)res.topLeftY[id], (int)(res.bottomRightX[id] - res.topLeftX[id]), (int)(res.bottomRightY[id] - res.topLeftY[id])); followedPath += cId + "=" + cType + Protocol.URI_PATH_SEP; Log("Command executed. the remote path is " + followedPath); } } } #endregion } catch (GuestCommandFailedException e) { #region Error handling Log("Error during phase 3: Remote Guest has reported a command execution failure."); string xmlErrorLog = BuildHostXmlError("Error during phase 3: Remote Guest has reported a command execution failure.", e, _todoJob, _mainJob, _vmName, followedPath); Log("Inserting done job to the DB"); _dbm.InsertDoneWithError(followedPath, _mainJob, xmlErrorLog); Log("Deleting similar branches from db"); _dbm.DeleteTodoJobsStartingWithPath(followedPath); Log("Freeing resources"); _sWriter.Close(); _sReader.Close(); _socket.Close(); _dbm.Dispose(); Log("Reverting VM"); //_fileLogger.Close(); VMManager.Instance.RevertVm(_vmName); return; #endregion } catch (LoopException e) { #region Error Handling Log("Error during phase 3: Loop detected."); string xmlErrorLog = BuildHostXmlError("Error during phase 3: Loop detected.", e, _todoJob, _mainJob, _vmName, followedPath); Log("Inserting done job to the DB"); _dbm.InsertDoneWithError(followedPath, _mainJob, xmlErrorLog); Log("Deleting similar branches from db"); _dbm.DeleteTodoJobsStartingWithPath(followedPath); Log("Freeing resources"); _sWriter.Close(); _sReader.Close(); _socket.Close(); _dbm.Dispose(); Stats.Instance.notifyLoop(); Log("Reverting VM"); //_fileLogger.Close(); VMManager.Instance.RevertVm(_vmName); Log("Returning."); return; #endregion } catch (Exception e) { #region Error Handling Log("Unhandled Exception during phase 3. \n" + e.Message + "\n" + e.StackTrace); string xmlErrorLog = BuildHostXmlError("Unhandled Exception during phase 3.", e, _todoJob, _mainJob, _vmName, followedPath); Log("Inserting done job to the DB"); _dbm.InsertDoneWithError(followedPath, _mainJob, xmlErrorLog); Log("Deleting similar branches from db"); _dbm.DeleteTodoJobsStartingWithPath(followedPath); Log("Freeing resources"); _sWriter.Close(); _sReader.Close(); _socket.Close(); _dbm.Dispose(); Log("Reverting VM"); //_fileLogger.Close(); VMManager.Instance.RevertVm(_vmName); return; #endregion } /*------------------------------------------------------------ * * PHASE 4 * Collet info about the program which has just exited * * ----------------------------------------------------------- */ try { #region Phase 4: collect info about remote guest Log("Remote Guest has exited. Collecting information."); Log("Receiving xml info..."); XmlDocument xmlInfo = ReceiveMachineInfo(); XmlElement uisteps = xmlInfo.CreateElement("UISteps"); for (int i = 0; i < _followedPathBitmaps.Count; i++) { XmlElement el = xmlInfo.CreateElement("STEP"); el.SetAttribute("ID", "" + i); el.InnerText = _followedPathBitmaps[i]; uisteps.AppendChild(el); } xmlInfo.GetElementsByTagName("ROOT")[0].AppendChild(uisteps); // Create the file on the REPORTS folder Log("Saving report..."); string reportPath = SaveReport(xmlInfo, _mainJob, _todoJob); Log("Updating db..."); _dbm.InsertDone(followedPath, _mainJob, reportPath); _dbm.DeleteTodoJob(_todoJob); Log("Rebooting guest..."); // Read the ACK from guest: is that a VM or not? Int16 ans = _sReader.ReadInt16(); if (ans == Protocol.ACK_REMOTE_REBOOT) { // It is a VM, so reboot it string vmName = _sReader.ReadString(); // reboot moved to finally block //fixme, i'm ugly! } else if (ans == Protocol.ACK_LOCAL_REBOOT) { throw new NotSupportedException("Bare metal support isn't available yet."); } else { throw new ProtocolException("Error: i was expecting an ACK for REMTOEREBOOT or LOCALREBOOT. I received " + ans + " instead."); } #endregion } catch (Exception e) { #region Error handling Log("Unhandled error during phase 4.\n" + e.Message + "\n" + e.StackTrace); string xmlErrorLog = BuildHostXmlError("Unhandled error during phase 4.", e, _todoJob, _mainJob, _vmName, followedPath); Log("Inserting done job to the DB"); _dbm.InsertDoneWithError(followedPath, _mainJob, xmlErrorLog); Log("Deleting similar branches from db"); _dbm.DeleteTodoJobsStartingWithPath(followedPath); #endregion } finally { #region Final resource cleaning _sReader.Close(); _sWriter.Close(); _socket.Close(); Log("Cleaning resourced for this thread and exiting."); //_fileLogger.Dispose(); _dbm.Dispose(); Stats.Instance.notifyCompletedPath(); VMManager.Instance.RevertVm(_vmName); #endregion } return; }