view ServerMonitor/Forms/CheckForm.cs @ 1:9e92780ebc0f

Additional validation for SSH check
author Brad Greco <brad@bgreco.net>
date Tue, 01 Jan 2019 21:14:47 -0500
parents 3e1a2131f897
children 3142e52cbe69
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace ServerMonitorApp
{
    public partial class CheckForm : Form
    {
        //private static readonly Dictionary<Type, CheckInfo> checkInfo = new Dictionary<Type, CheckInfo>();
        private readonly List<CheckControl> checkControls = new List<CheckControl>();
        private bool helpShown;
        private CancellationTokenSource cancellationTokenSource;
        private Check workCheck;
        private CheckControl checkControl;
        private QuickHelpForm helpForm;
        private Server server;
        private ServerMonitor monitor;

        public event EventHandler<HelpLocationChangedEventArgs> HelpLocationChanged;

        public Check Check { get; private set; }

        public int CheckId { get; private set; }

        public Point HelpLocation { get { return TypeHelpPictureBox.PointToScreen(Point.Empty); } }

        public CheckForm(ServerMonitor monitor, Check check)
        {
            InitializeComponent();

            Check = check;
            CheckId = Check.Id;
            server = Check.Server;
            this.monitor = monitor;
        }

        public CheckForm(ServerMonitor monitor, Server server)
        {
            InitializeComponent();
            this.server = server;
            this.monitor = monitor;
        }

        private void CheckForm_Load(object sender, EventArgs e)
        {
            CheckTypeComboBox.Items.AddRange(Check.CheckTypes);
            FrequencyUnitsComboBox.DataSource = Enum.GetValues(typeof(FrequencyUnits));
            Helpers.FormatImageButton(RunButton);
            Helpers.FormatImageButton(CancelRunButton);

            Move += CheckForm_Move;
            CheckTypePanel.LocationChanged += CheckTypePanel_LocationChanged;
            GeneralGroupBox.Click += Control_Click;
            CheckSettingsPanel.Click += Control_Click;

            CheckTypeComboBox.SelectedItem = Check?.GetType();
            if (Check != null)
                LoadCheck(Check);
        }

        private void CheckTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ShowCheckControl();
            workCheck = (Check)Activator.CreateInstance((Type)CheckTypeComboBox.SelectedItem);
        }

        private void CheckTypeComboBox_Format(object sender, ListControlConvertEventArgs e)
        {
            e.Value = Helpers.GetDisplayName((Type)e.ListItem);
        }

        /*private void showGroupBox(GroupBox groupBox)
        {

        }*/

        /*private Type GetCheckType()
        {
            return (Type)CheckTypeComboBox.SelectedItem;
        }*/

        private void ShowCheckControl()
        {
            IEnumerable<CheckControl> checkControls = CheckSettingsPanel.Controls.OfType<CheckControl>();
            foreach (CheckControl control in checkControls)
                control.Hide();
            Type type = (Type)CheckTypeComboBox.SelectedItem;
            checkControl = checkControls.FirstOrDefault(c => c.CheckType == type);
            if (checkControl == null)
            {
                checkControl = CheckControl.Create(type);
                if (checkControl != null)
                {
                    checkControl.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
                    checkControl.Click += Control_Click;
                    CheckSettingsPanel.Controls.Add(checkControl);
                }
            }
            if (checkControl != null)
            {
                CheckSettingsPanel.Height = checkControl.Height;
                //Height = 230 + checkControl.Height;
                checkControl.Show();
            }
        }

        private void LoadCheck(Check check)
        {
            NameTextBox.Text = Check.Name;
            EnabledCheckBox.Checked = check.Enabled;
            TimeoutInput.Value = check.Timeout;
            FrequencyUnitsComboBox.SelectedItem = check.Schedule.Units;
            FrequencyUpDown.Value = check.Schedule.Frequency;
            StartTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.StartTime;
            EndTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.EndTime;
            checkControl?.LoadCheck(check);
        }

        private bool UpdateCheck(Check check, bool saving = true)
        {
            string result;
            if (CheckTypeComboBox.SelectedIndex == -1)
            {
                result = "Check type cannot be blank.";
            }
            else
            {
                check.Id = CheckId;
                check.Server = server;
                check.Name = NameTextBox.Text;
                check.Enabled = EnabledCheckBox.Checked;
                check.Timeout = (int)TimeoutInput.Value;
                check.Schedule = new Schedule((FrequencyUnits)FrequencyUnitsComboBox.SelectedItem, (int)FrequencyUpDown.Value, StartTimePicker.Value.TimeOfDay, EndTimePicker.Value.TimeOfDay);
                try
                {
                    checkControl?.UpdateCheck(check);
                    result = check.Validate(saving);
                }
                catch (UpdateCheckException e)
                {
                    result = e.Message;
                }
            }
            if (!result.IsNullOrEmpty())
            {
                MessageBox.Show(result, "Error validating check", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            return true;
        }

        private void OkButton_Click(object sender, EventArgs e)
        {
            if (!UpdateCheck(workCheck))
                return;
            Check = workCheck;
            server.UpdateCheck(Check);
            monitor.SaveServers();
            Close();
        }

        private void CancelCheckButton_Click(object sender, EventArgs e)
        {
            Close();
        }

        private async void RunButton_Click(object sender, EventArgs e)
        {
            if (!UpdateCheck(workCheck, false))
                return;

            RunButton.Enabled = false;
            RunButton.Text = "Running";
            ResultLabel.Visible = ResultIconPictureBox.Visible = false;
            CancelRunButton.Visible = true;

            // Create a CancellationTokenSource for this execution, and set it to both variables.
            // localCancellationTokenSource will mantain a reference to the token for this execution,
            // while cancellationTokenSource might end up referencing a different token if the Cancel
            // button is clicked (but the Task itself is unable to be cancelled), then Run is clicked
            // again.
            CancellationTokenSource localCancellationTokenSource = new CancellationTokenSource();
            cancellationTokenSource = localCancellationTokenSource;
            CheckResult result = await workCheck.ExecuteAsync(cancellationTokenSource.Token, false);
            if (!localCancellationTokenSource.IsCancellationRequested)
                OnRunFinished(result);
            localCancellationTokenSource.Dispose();
            localCancellationTokenSource = null;
        }

        //private void RunButton_Click(object sender, EventArgs e)
        //{
        //    if (!Check.Server.SshClient.IsConnected)
        //        Check.Server.SshClient.Connect();
        //    using (Renci.SshNet.SshCommand command = Check.Server.SshClient.CreateCommand("ls"))
        //    {
        //        //token.Register(command.CancelAsync);
        //        //command.BeginExecute(asyncResult =>
        //        //{
        //        //    string result = command.EndExecute(asyncResult);
        //        //    //tcs.SetResult(new CheckResult(this, CheckStatus.Success, result));
        //        //});
        //        IAsyncResult iar = command.BeginExecute(z);
        //        command.EndExecute(iar);
        //        //string result = command.Execute();
        //        //System.Console.Write(result);
        //    }
        //}

        //private void z(IAsyncResult ar)
        //{
        //    throw new NotImplementedException();
        //}

        private void CancelRunButton_Click(object sender, EventArgs e)
        {
            cancellationTokenSource.Cancel();
            OnRunFinished();
        }

        /// <summary>Updates the UI after a check is finished running.</summary>
        /// <param name="result">Result of the check execution. If null, the check was cancelled before it completed.</param>
        private void OnRunFinished(CheckResult result = null)
        {
            RunButton.Enabled = true;
            RunButton.Text = "Run";
            CancelRunButton.Visible = false;
            if (result != null)
            {
                ResultLabel.Text = result.Message;
                //ResultLabel.ForeColor = result.CheckStatus == CheckStatus.Success ? Color.Green : Color.Red;
                ResultIconPictureBox.Image = result.CheckStatus.GetIcon();
                ResultLabel.Visible = ResultIconPictureBox.Visible = true;
            }
        }

        /*private class CheckInfo
        {
            public GroupBox GroupBox { get; private set; }

            public Func<string> ValidateFunction { get; private set; }

            public CheckInfo(GroupBox groupBox, Func<string> validateFunction)
            {
                GroupBox = groupBox;
                ValidateFunction = validateFunction;
            }
        }*/

        private string BuildHelpText()
        {
            StringBuilder rtf = new StringBuilder(@"{\rtf1\ansi ");
            foreach (Type checkType in Check.CheckTypes)
            {
                // Arguments to AppendLine() must end with a \ for the RichTextBox to render the newline character
                rtf.Append(@"\b ").Append(Helpers.GetDisplayName(checkType)).AppendLine(@"\b0 \")
                    .Append(checkType.GetAttribute<DescriptionAttribute>()?.Description ?? "No description provided.").AppendLine(@"\").AppendLine(@"\");
            }
            return rtf.ToString().TrimEnd(' ', '\r', '\n', '\\');
        }

        private void TypeHelpPictureBox_Click(object sender, EventArgs e)
        {
            if (helpShown)
            {
                helpForm.Close();
            }
            else
            {
                helpForm = new QuickHelpForm(BuildHelpText());
                helpForm.FormClosed += QuickHelpForm_FormClosed;
                helpForm.Show(this);
                Activate();
                helpShown = true;
                OnHelpLocationChanged();
            }
        }

        private void QuickHelpForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            helpShown = false;
            helpForm.FormClosed -= QuickHelpForm_FormClosed;
            helpForm.Dispose();
        }

        private void OnHelpLocationChanged()
        {
            HelpLocationChanged?.Invoke(this, new HelpLocationChangedEventArgs(HelpLocation));
        }

        private void CheckTypePanel_LocationChanged(object sender, EventArgs e)
        {
            OnHelpLocationChanged();
        }

        private void CheckForm_Move(object sender, EventArgs e)
        {
            OnHelpLocationChanged();
        }

        private void Control_Click(object sender, EventArgs e)
        {
            if (helpShown)
                helpForm.Close();
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == Keys.Escape && helpShown)
            {
                helpForm.Close();
                return true;
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }

        private void FrequencyUnitsComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ScheduleBetweenPanel.Visible = !(ScheduleAtPanel.Visible = FrequencyUnitsComboBox.SelectedIndex == 3);
        }

        private void FrequencyUnitsComboBox_Format(object sender, ListControlConvertEventArgs e)
        {
            e.Value = e.Value.ToString().ToLower() + 's';
        }
    }

    public class HelpLocationChangedEventArgs : EventArgs
    {
        public Point HelpLocation { get; private set; }

        public HelpLocationChangedEventArgs(Point helpLocation)
        {
            HelpLocation = helpLocation;
        }
    }
}