/// <summary> /// Setups up Host/CM target and checks for missing params /// Common code used by all CM commands, but spefcific to this host /// </summary> /// <param name="target">The HostType to target</param> /// <returns>Tuple containing the target Host/Cm, validated</returns> internal override sealed (VibesHost, VibesCm, ObservableCollection <VibesCm>) SetTargets(object target) { VibesHost hostToPoll = null; VibesCm cmToPoll = null; ObservableCollection <VibesCm> cms = null; switch (target) { case HostTypes.COMM1: CheckSingleParameters(SelectedHostCommOne, SelectedCmCommOne); hostToPoll = SelectedHostCommOne; cmToPoll = SelectedCmCommOne; cms = CmsDisplayCommOne; break; case HostTypes.COMM2: CheckSingleParameters(SelectedHostCommTwo, SelectedCmCommTwo); hostToPoll = SelectedHostCommTwo; cmToPoll = SelectedCmCommTwo; cms = CmsDisplayCommTwo; break; } return(hostToPoll, cmToPoll, cms); }
/// <summary> /// Gets deployment.properties from specified CM /// </summary> /// <param name="host">The host on which the CM is installed</param> /// <param name="cm">The CM to query</param> /// <param name="hashCode">The hash of the sending VM</param> public static void GetCmParams(VibesHost host, VibesCm cm, int hashCode) { ValidateParameters(host, cm); string sshCommand = SshCommands.EchoCmProperties(cm); Task <string> sshResult = Task.Run(() => ExecuteSshCommand(host, sshCommand)); OnCmCommandComplete(host, cm, sshResult.Result, hashCode: hashCode); }
/// <summary> /// Uses SSH command to start remote CM /// Will send back OnCmCommandComplete to all subscribed VM's /// </summary> /// <param name="host">The VibesHost to connect to</param> /// <param name="cm">The VibesCm to start</param> /// <param name="hashCode">The hash of the sending VM</param> public static void StartCm(VibesHost host, VibesCm cm, int hashCode) { ValidateParameters(host, cm); string sshCommand = SshCommands.StartRemoteCmCommand(host, cm); string sshResult = ExecuteSshCommand(host, sshCommand); OnCmCommandComplete(cm, sshResult.Contains("running") ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable, hashCode: hashCode); }
/// <summary> /// Polls CM asyncronously from code /// An event is expected back on poll complete, which requires subscribing to CmHttpHelper.PollComplete, in order to update the GUI /// </summary> /// <param name="cmToPoll">Reference to CM to be polled</param> internal void PollCmAsync(VibesCm cmToPoll) { using (DataContext context = new DataContext()) { VibesHost hostToPoll = context.EnvironmentHosts.Single(h => h.Id == cmToPoll.VibesHostId); Task.Run(() => CmHttpHelper.CheckCmStatus(hostToPoll, cmToPoll, GetHashCode())); cmToPoll.CmStatus = CmStates.Polling; } }
/// <summary> /// Sets up up Host/CM target and checks for missing params /// Common code used by all CM commands, but specific to this host /// </summary> /// <param name="target">The HostType to target</param> /// <returns>Tuple containing the target Host/Cm, validated</returns> internal override sealed (VibesHost, VibesCm, ObservableCollection <VibesCm>) SetTargets(object target) { VibesHost hostToPoll = null; VibesCm cmToPoll = null; ObservableCollection <VibesCm> cms = null; switch (target) { case HostTypes.EXEC: CheckSingleParameters(SelectedHostExec, SelectedCmExec); hostToPoll = SelectedHostExec; cmToPoll = SelectedCmExec; cms = CmsDisplayExec; break; case HostTypes.OPERDB: CheckSingleParameters(SelectedHostOperDb, SelectedCmOperDb); hostToPoll = SelectedHostOperDb; cmToPoll = SelectedCmOperDb; cms = CmsDisplayOperDb; break; case HostTypes.OPERAPP1: CheckSingleParameters(SelectedHostOperAppOne, SelectedCmOperAppOne); hostToPoll = SelectedHostOperAppOne; cmToPoll = SelectedCmOperAppOne; cms = CmsDisplayOperAppOne; break; case HostTypes.OPERAPP2: CheckSingleParameters(SelectedHostOperAppTwo, SelectedCmOperAppTwo); hostToPoll = SelectedHostOperAppTwo; cmToPoll = SelectedCmOperAppTwo; cms = CmsDisplayOperAppTwo; break; case HostTypes.MS: CheckSingleParameters(SelectedHostMs, SelectedCmMs); hostToPoll = SelectedHostMs; cmToPoll = SelectedCmMs; cms = CmsDisplayMs; break; } return(hostToPoll, cmToPoll, cms); }
/// <summary> /// Uses bash sed command to edit deployment properties file /// Will send back OnCmCommandCOmplete to all subscribed VM's /// </summary> /// <param name="host">The VibesHost to conenct to</param> /// <param name="cm">The VibesCm to edit</param> /// <param name="paramToEdit">The text pattern to find</param> /// <param name="paramToReplace">The text pattern to replace with</param> /// <param name="hashCode">The hash of the sending VM</param> public static void AlterCm(VibesHost host, VibesCm cm, string paramToEdit, string paramToReplace, int hashCode) { ValidateParameters(host, cm); if (string.IsNullOrEmpty(paramToEdit) || string.IsNullOrEmpty(paramToReplace)) { throw new ArgumentNullException($"Attempted to alter CM {cm.CmResourceName} without parameters to add/remove"); } string sshCommand = SshCommands.AlterRemoteCmCommand(host, cm, paramToEdit, paramToReplace); ExecuteSshCommand(host, sshCommand); OnCmCommandComplete(cm, HttpStatusCode.NoContent, hashCode: hashCode); }
/// <summary> /// Validates host/cm parameters to ensure command success /// </summary> /// <param name="host">The host for which parameters need validating</param> /// <param name="cm">Optional, the CM for which parameters need validating</param> private static void ValidateParameters(VibesHost host, VibesCm cm = null) { if (string.IsNullOrEmpty(host.Url)) { throw new ArgumentNullException("Attempted to command CM without URL"); } if (string.IsNullOrEmpty(host.SshUsername) || string.IsNullOrEmpty(host.SshPassword)) { throw new ArgumentNullException("Attempted to command CM without SSH credentials"); } if (cm != null && (string.IsNullOrEmpty(cm.CmCorePath) || string.IsNullOrEmpty(cm.CmPath) || string.IsNullOrEmpty(cm.CmResourceName))) { throw new ArgumentNullException("Attempted to command CM without CM metadata"); } }
/// <summary> /// Stores deployment properties if returned by SSH command /// Used to populate VibesCmSwapView and EcSwapView /// </summary> /// <param name="cmChanged">The CM which was queried</param> /// <param name="deploymentProperties">The deployment properties file fetched</param> internal void StoreDeploymentProperties(VibesCm cmChanged, string deploymentProperties) { using (DataContext context = new DataContext()) { VibesCm cmToUpdate = context.HostCms.SingleOrDefault(c => c.Id == cmChanged.Id); XDocument xProperties = XDocument.Parse(deploymentProperties); // Loop through deployment properties and only fetch desired properties foreach (XElement node in xProperties.Descendants("parameter")) { // Property types to search for if ( node.Value.ToLower().Contains("http:") || node.FirstAttribute.Value.Contains("URL_TO") || node.FirstAttribute.Value == "useVibes" || node.FirstAttribute.Value.Contains("smart_suite_server_name") || node.FirstAttribute.Value.Contains("smart_suite_username") || node.FirstAttribute.Value.Contains("smart_suite_password") ) { string propertyKey = node.Attribute("name").Value; string propertyVal = node.Value; // Property Exists, update if (context.DeploymentProperties.Any(p => p.CmId == cmChanged.Id && p.PropertyKey == propertyKey)) { DeploymentProperty propToUpdate = context.DeploymentProperties.SingleOrDefault(p => p.CmId == cmChanged.Id && p.PropertyKey == propertyKey); propToUpdate.PropertyValue = propertyVal; context.Update(propToUpdate); continue; } else { // Property does not exist, add context.DeploymentProperties.Add(new DeploymentProperty { PropertyKey = propertyKey, PropertyValue = propertyVal, Cm = context.HostCms.Single(c => c.Id == cmChanged.Id), CmId = context.HostCms.Single(c => c.Id == cmChanged.Id).Id }); } } } context.SaveChanges(); } }
/// <summary> /// Load data from database /// </summary> /// <param name="parameter">Not used, only for GUI binding</param> private void LoadData(object parameter) { try { // Single CM is updated, only update that one CM if (parameter is VibesCm cmChanged) { using (DataContext context = new DataContext()) { VibesCm newCm = context.HostCms.Where(c => c.Id == cmChanged.Id).Include(c => c.DeploymentProperties).Include(c => c.VibesHost).FirstOrDefault().DeepCopy(); newCm.CmStatus = CmStates.Updated; if (CmsDisplayCommOne.Contains(cmChanged)) { CmsDisplayCommOne.Remove(CmsDisplayCommOne.Single(c => c.Id == cmChanged.Id)); CmsDisplayCommOne.Add(newCm); SelectedCmCommOne = newCm; } if (CmsDisplayCommTwo.Contains(cmChanged)) { CmsDisplayCommTwo.Remove(CmsDisplayCommTwo.Single(c => c.Id == cmChanged.Id)); CmsDisplayCommTwo.Add(newCm); SelectedCmCommTwo = newCm; } return; } } // Comm1 CmsDisplayCommOne.Clear(); LoadCmForSwap(CmsDisplayCommOne, HostTypes.COMM1); SelectedCmCommOne = CmsDisplayCommOne.FirstOrDefault(); // Comm2 CmsDisplayCommTwo.Clear(); LoadCmForSwap(CmsDisplayCommTwo, HostTypes.COMM2); SelectedCmCommTwo = CmsDisplayCommTwo.FirstOrDefault(); } catch (Exception ex) { LogAndReportException(ex, "Error loading data to GUI", true); } }
/// <summary> /// Loads data from database /// Calls LoadBoilerplate if no data is loaded /// </summary> /// <param name="type">The GuiObjectType to load</param> /// <param name="objectId">If an object is selected prior to refreshed, it's ID can be passed so it remains selected</param> private void LoadData(GuiObjectTypes type, int objectId = 0) { try { switch (type) { case GuiObjectTypes.VibesHost: using (DataContext context = new DataContext()) { DisplayHosts.Clear(); // Nothing found, load boilerplate and terminate if (!context.EnvironmentHosts.Any()) { LoadBoilerPlate(); return; } // Load Hosts from DB foreach (VibesHost host in context.EnvironmentHosts.OrderBy(h => h.Name)) { VibesHost newHost = host.DeepCopy(); newHost.PropertyChanged += new PropertyChangedEventHandler(PersistTargetChanges); DisplayHosts.Add(newHost); } // Set GUI particulars and reload CM's SelectedHost = objectId == 0 ? DisplayHosts.FirstOrDefault() : DisplayHosts.SingleOrDefault(h => h.Id == objectId); CanEditHost = true; LoadData(GuiObjectTypes.VibesCm); } break; case GuiObjectTypes.VibesCm: using (DataContext context = new DataContext()) { DisplayCms.Clear(); // Nothing found, load boilerplate and terminate if (!context.HostCms.Any(c => c.VibesHostId == SelectedHost.Id)) { LoadBoilerPlate(); return; } // Load CMs from DB DisplayCms.Clear(); foreach (VibesCm cm in context.HostCms.Where(c => c.VibesHostId == SelectedHost.Id).OrderBy(c => c.CmResourceName)) { VibesCm newCm = cm.DeepCopy(); newCm.PropertyChanged += new PropertyChangedEventHandler(PersistTargetChanges); DisplayCms.Add(newCm); } // Set GUI particulars SelectedCm = objectId == 0 ? DisplayCms.FirstOrDefault() : DisplayCms.SingleOrDefault(c => c.Id == objectId); CanEditCm = true; } break; } } catch (Exception ex) { MessageBox.Show($"Error loading data to GUI: {ex.Message}"); Log.Error($"Error loading data to GUI: {ex.Message}"); } }
/// <summary> /// Raises CmCommandComplete event for command resulting from an SSH command which fetches deployment.properties /// </summary> /// <param name="cmChanged">The CM which was changed</param> /// <param name="deploymentProperties">The deployment.properties fetched</param> /// /// <param name="hashCode">The hashcode of the sender</param> private static void OnCmCommandComplete(VibesHost targetHost, VibesCm targetCm, string deploymentProperties, int hashCode) { CmCommandComplete?.Invoke(null, new CmHelperEventArgs { Host = targetHost, CmChanged = targetCm, CmStatus = HttpStatusCode.Continue, DeploymentProperties = deploymentProperties, SubscriberHashCode = hashCode }); }
/// <summary> /// Raises CmCommandComplete event for command resulting from an SSH command /// </summary> /// <param name="cmChanged">The CM which was changed</param> /// <param name="cmStatus">The status of the CM changed</param> /// /// <param name="hashCode">The hashcode of the sender</param> private static void OnCmCommandComplete(VibesCm cmChanged, HttpStatusCode cmStatus, int hashCode) { CmCommandComplete?.Invoke(null, new CmHelperEventArgs { CmChanged = cmChanged, CmStatus = cmStatus, SubscriberHashCode = hashCode }); }
/// <summary> /// Autosave CM changes when PropertyChanged fires, used to save objects on keypress /// </summary> /// <param name="sender">The object changed</param> /// <param name="args">Propertychanged args</param> internal void PersistTargetChanges(object sender, PropertyChangedEventArgs args) { // Stop execution if sender is VM if (sender is SetupVm || sender is VibesCmSwapVm || sender is EcSwapVm) { return; } try { // Host on setup page if (sender is VibesHost newHostValues) { using (DataContext context = new DataContext()) { VibesHost hostToUpdate = context.EnvironmentHosts.Single(h => h.Id == newHostValues.Id); hostToUpdate.Name = newHostValues.Name ?? hostToUpdate.Name; hostToUpdate.Url = newHostValues.Url ?? hostToUpdate.Url; hostToUpdate.SshUsername = newHostValues.SshUsername ?? hostToUpdate.SshUsername; hostToUpdate.SshPassword = newHostValues.SshPassword ?? hostToUpdate.SshPassword; hostToUpdate.HostType = newHostValues.HostType; hostToUpdate.IndClustered = newHostValues.IndClustered; context.SaveChanges(); } } // CM on setup page else if (sender is VibesCm newCmValues) { using (DataContext context = new DataContext()) { VibesCm cmToUpdate = context.HostCms.Single(c => c.Id == newCmValues.Id); cmToUpdate.CmResourceName = newCmValues.CmResourceName ?? cmToUpdate.CmResourceName; cmToUpdate.CmPort = newCmValues.CmPort ?? cmToUpdate.CmPort; cmToUpdate.CmPath = newCmValues.CmPath ?? cmToUpdate.CmPath; cmToUpdate.CmCorePath = newCmValues.CmCorePath ?? cmToUpdate.CmCorePath; cmToUpdate.CmType = newCmValues.CmType; context.SaveChanges(); } } // CM deployment properties on swap page else if (sender is DeploymentProperty property) { Log.Information($"PropertyChanged Called, sender is {property.PropertyKey} and CM is {property.Cm.CmResourceName}"); using (DataContext context = new DataContext()) { if (context.HostCms.Any(p => p.Id == property.CmId)) { DeploymentProperty propToUpdate = context.DeploymentProperties.SingleOrDefault(p => p.Id == property.Id); propToUpdate.SearchPattern = property.SearchPattern; propToUpdate.ReplacePattern = property.ReplacePattern; context.SaveChanges(); } } } else { throw new ArgumentException("Unknown type supplied"); } } catch (Exception ex) { Log.Error($"Unable to persist target changes on {sender.GetType()}, Error: {ex.Message}"); Log.Error($"Stack Trace: {ex.StackTrace}"); } }
/// <summary> /// Polls CM for current status via call to http://host:port/status page /// </summary> /// <param name="hostToCheck">The host on which the CM is installed</param> /// <param name="cmToCheck">The CM to poll</param> /// <returns>Task<HttpStatusCode> containing CM status, though this is backup functionality, expected use is the event PollComplete</returns> public static async Task <HttpStatusCode> CheckCmStatus(VibesHost hostToCheck, VibesCm cmToCheck, int hashCode) { HttpStatusCode statusCode = HttpStatusCode.ServiceUnavailable; try { if (hostToCheck == null || cmToCheck == null) { string missingParameter = hostToCheck == null ? "Selected Host" : "Selected CM"; throw new Exception($"Error polling CM, Missing parameter {missingParameter}"); } int.TryParse(cmToCheck.CmPort, out int port); var builder = new UriBuilder("http", hostToCheck.Url, port) { Path = "status" }; Uri uri = builder.Uri; var response = await StaticHttpClient.GetAsync(uri); if (response.IsSuccessStatusCode) { var responseBody = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrEmpty(responseBody) && responseBody.Contains("CM = Alive!")) { statusCode = HttpStatusCode.OK; } } else { statusCode = HttpStatusCode.ServiceUnavailable; } } catch (Exception ex) { // HTTP timeout, CM offline if (ex.Message.Contains("A task was canceled.")) { Log.Information($"HTTP error: Request to {cmToCheck.CmResourceName} timed out"); statusCode = HttpStatusCode.RequestTimeout; OnPollComplete(cmToCheck, statusCode, hashCode); return(statusCode); } // Unkonwn Host else if (ex.InnerException.ToString().Contains("The remote name could not be resolved")) { Log.Information($"HTTP error: Unable to resolve hostname {cmToCheck.VibesHost.Url} for CM {cmToCheck.CmResourceName}"); statusCode = HttpStatusCode.NotFound; OnPollComplete(cmToCheck, statusCode, hashCode); return(statusCode); } // Other error else if (ex.InnerException.ToString().Contains("Unable to connect to the remote server")) { Log.Information($"CM {cmToCheck.CmResourceName} on {cmToCheck.VibesHost.Url} is reporting as offline"); statusCode = HttpStatusCode.ServiceUnavailable; OnPollComplete(cmToCheck, statusCode, hashCode); return(statusCode); } Log.Error($"Error polling CM: {ex.Message}"); Log.Error($"Stack Trace: {ex.StackTrace}"); OnPollComplete(cmToCheck, statusCode, hashCode); } OnPollComplete(cmToCheck, statusCode, hashCode); return(statusCode); }
/// <summary> /// Raises PollComplete event /// </summary> /// <param name="cmPolled">The CM which was polled</param> /// <param name="cmStatus">The status of the CM polled</param> public static void OnPollComplete(VibesCm cmPolled, HttpStatusCode cmStatus, int hashCode) { PollComplete?.Invoke(null, new CmHelperEventArgs { Cm = cmPolled, CmStatus = cmStatus, SubscriberHashCode = hashCode }); }
/// <summary> /// Load data from database /// </summary> /// <param name="parameter">Not used, only for GUI binding</param> private void LoadData(object parameter) { try { // Single CM is updated, only update that one CM using (DataContext context = new DataContext()) { if (parameter is VibesCm cmChanged) { VibesCm newCm = context.HostCms.Where(c => c.Id == cmChanged.Id).Include(c => c.DeploymentProperties).Include(c => c.VibesHost).FirstOrDefault().DeepCopy(); newCm.CmStatus = CmStates.Updated; if (CmsDisplayExec.Contains(cmChanged)) { CmsDisplayExec.Remove(CmsDisplayExec.Single(c => c.Id == cmChanged.Id)); CmsDisplayExec.Add(newCm); SelectedCmExec = newCm; SelectedHostExec = HostsDisplayExec.SingleOrDefault(h => h.Id == newCm.VibesHost.Id); } if (CmsDisplayOperDb.Contains(cmChanged)) { CmsDisplayOperDb.Remove(CmsDisplayOperDb.Single(c => c.Id == cmChanged.Id)); CmsDisplayOperDb.Add(newCm); SelectedCmOperDb = newCm; SelectedHostOperDb = HostsDisplayOperDb.SingleOrDefault(h => h.Id == newCm.VibesHost.Id); } if (CmsDisplayOperAppOne.Contains(cmChanged)) { CmsDisplayOperAppOne.Remove(CmsDisplayOperAppOne.Single(c => c.Id == cmChanged.Id)); CmsDisplayOperAppOne.Add(newCm); SelectedCmOperAppOne = newCm; SelectedHostOperAppOne = HostsDisplayOperAppOne.SingleOrDefault(h => h.Id == newCm.VibesHost.Id); } if (CmsDisplayOperAppTwo.Contains(cmChanged)) { CmsDisplayOperAppTwo.Remove(CmsDisplayOperAppTwo.Single(c => c.Id == cmChanged.Id)); CmsDisplayOperAppTwo.Add(newCm); SelectedCmOperAppTwo = newCm; SelectedHostOperAppTwo = HostsDisplayOperAppTwo.SingleOrDefault(h => h.Id == newCm.VibesHost.Id); } if (CmsDisplayMs.Contains(cmChanged)) { CmsDisplayMs.Remove(CmsDisplayMs.Single(c => c.Id == cmChanged.Id)); CmsDisplayMs.Add(newCm); SelectedCmMs = newCm; SelectedHostMs = HostsDisplayMs.SingleOrDefault(h => h.Id == newCm.VibesHost.Id); } return; } } // Initial Load using (DataContext context = new DataContext()) { HostsDisplayExec.Clear(); CmsDisplayExec.Clear(); if (context.EnvironmentHosts.Any(h => h.HostType == HostTypes.EXEC)) { foreach (VibesHost host in context.EnvironmentHosts.Where(h => h.HostType == HostTypes.EXEC).OrderBy(h => h.Name)) { VibesHost newHost = host.DeepCopy(); HostsDisplayExec.Add(newHost); } SelectedHostExec = HostsDisplayExec.FirstOrDefault(); } HostsDisplayOperDb.Clear(); CmsDisplayOperDb.Clear(); if (context.EnvironmentHosts.Any(h => h.HostType == HostTypes.OPERDB)) { foreach (VibesHost host in context.EnvironmentHosts.Where(h => h.HostType == HostTypes.OPERDB).OrderBy(h => h.Name)) { VibesHost newHost = host.DeepCopy(); HostsDisplayOperDb.Add(newHost); } SelectedHostOperDb = HostsDisplayOperDb.FirstOrDefault(); } HostsDisplayOperAppOne.Clear(); CmsDisplayOperAppOne.Clear(); if (context.EnvironmentHosts.Any(h => h.HostType == HostTypes.OPERAPP1)) { foreach (VibesHost host in context.EnvironmentHosts.Where(h => h.HostType == HostTypes.OPERAPP1).OrderBy(h => h.Name)) { VibesHost newHost = host.DeepCopy(); HostsDisplayOperAppOne.Add(newHost); } SelectedHostOperAppOne = HostsDisplayOperAppOne.FirstOrDefault(); } HostsDisplayOperAppTwo.Clear(); CmsDisplayOperAppTwo.Clear(); if (context.EnvironmentHosts.Any(h => h.HostType == HostTypes.OPERAPP2)) { foreach (VibesHost host in context.EnvironmentHosts.Where(h => h.HostType == HostTypes.OPERAPP2).OrderBy(h => h.Name)) { VibesHost newHost = host.DeepCopy(); HostsDisplayOperAppTwo.Add(newHost); } SelectedHostOperAppTwo = HostsDisplayOperAppTwo.FirstOrDefault(); } HostsDisplayMs.Clear(); CmsDisplayMs.Clear(); if (context.EnvironmentHosts.Any(h => h.HostType == HostTypes.MS)) { foreach (VibesHost host in context.EnvironmentHosts.Where(h => h.HostType == HostTypes.MS).OrderBy(h => h.Name)) { VibesHost newHost = host.DeepCopy(); HostsDisplayMs.Add(newHost); } SelectedHostMs = HostsDisplayMs.FirstOrDefault(); } } } catch (Exception ex) { LogAndReportException(ex, $"Error loading VIBES host's from DB: {ex.Message}", true); } }
/// <summary> /// Command to fetch properties from a remote CM /// </summary> /// <param name="cm">The CM to start</param> /// <returns>SSH command for this host/CM</returns> internal static string EchoCmProperties(VibesCm cm) { return($"cat {cm.CmPath}/conf/deployment.properties"); }
/// <summary> /// Command to alter a remote CM /// </summary> /// <param name="host">The host the CM sits on, for password and/or clustering</param> /// <param name="cm">The CM to alter</param> /// <param name="paramToEdit">The sed search pattern</param> /// <param name="paramToReplace">The sed replace pattern</param> /// <returns>SSH command for this host/CM</returns> internal static string AlterRemoteCmCommand(VibesHost host, VibesCm cm, string paramToEdit, string paramToReplace) { return($"echo '{host.SshPassword}' | sudo -S sed -i 's${paramToEdit}${paramToReplace}$' {cm.CmPath}/conf/deployment.properties"); }
/// <summary> /// Command to stop a remote CM /// </summary> /// <param name="host">The host the CM sits on, for password and/or clustering</param> /// <param name="cm">The CM to stop</param> /// <returns>SSH command for this host/CM</returns> internal static string StopRemoteCmCommand(VibesHost host, VibesCm cm) { return(host.IndClustered ? $"echo '{host.SshPassword}' | sudo -S /usr/sbin/crm resource stop {cm.CmResourceName}" : $"echo '{host.SshPassword}' | sudo -Su vibes sh {cm.CmCorePath}/bin/cm_stop -i {cm.CmResourceName}"); }