Dryad/JobBrowser/JobBrowser/ClusterBrowser.cs

1007 lines
38 KiB
C#

/*
Copyright (c) Microsoft Corporation
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License
at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using System.Diagnostics;
using Microsoft.Research.JobObjectModel;
using Microsoft.Research.Tools;
using Microsoft.Research.UsefulForms;
namespace Microsoft.Research.DryadAnalysis
{
/// <summary>
/// Class to browse jobs on cluster, copy, summarize and start visualization.
/// </summary>
public partial class ClusterBrowser : Form
{
/// <summary>
/// Configuration for the currently selected cluster.
/// </summary>
ClusterConfiguration Configuration { get; set; }
/// <summary>
/// Configuration for current cluster to analyze.
/// </summary>
ClusterStatus clusterStatus;
/// <summary>
/// Cluster selected by user.
/// </summary>
string cluster;
/// <summary>
/// Complete list of jobs on the cluster.
/// </summary>
List<ClusterJobInformation> completeJobsList;
/// <summary>
/// Used to display status messages.
/// </summary>
StatusWriter status;
/// <summary>
/// Timer invoked to refresh automatically.
/// </summary>
Timer refreshTimer;
/// <summary>
/// Settings persisting after form closure.
/// </summary>
ClusterBrowserSettings formSettings;
/// <summary>
/// File logging detailed errors.
/// </summary>
private TextWriterTraceListener LogFile;
private BackgroundWorkQueue queue;
/// <summary>
/// Jobs from the cluster.
/// </summary>
DGVData<ClusterJobInformation> clusterJobs;
/// <summary>
/// Virtual cluster selected by the user.
/// </summary>
string SelectedVirtualCluster { get { return this.comboBox_virtualCluster.Text; } }
/// <summary>
/// Create a cluster browser object which stores the databases in the specified directory.
/// </summary>
public ClusterBrowser()
{
this.InitializeComponent();
this.status = new StatusWriter(this.statuslabel, this.statusStrip, this.Status);
BackgroundWorker queueWorker = new BackgroundWorker();
this.queue = new BackgroundWorkQueue(queueWorker, null, null);
this.completeJobsList = new List<ClusterJobInformation>();
this.refreshTimer = new Timer();
this.refreshTimer.Interval = 30000; // 30 seconds
this.refreshTimer.Tick += this.refreshTimer_Tick;
this.toolStripMenuItem_job.Enabled = false;
this.clusterJobs = new DGVData<ClusterJobInformation>();
this.filteredDataGridView.SetDataSource(this.clusterJobs);
this.filteredDataGridView.DataGridView.Columns["IsUnavailable"].Visible = false;
this.filteredDataGridView.DataGridView.Columns["Cluster"].Visible = false;
this.filteredDataGridView.DataGridView.Columns["Name"].FillWeight = 50;
this.filteredDataGridView.DataGridView.Columns["Status"].FillWeight = 10;
this.filteredDataGridView.ViewChanged += this.filteredDataGridView_ViewChanged;
#region TOOLTIPS
//ToolTip help = new ToolTip();
//help.SetToolTip(this.combo_cluster, "Cluster whose jobs are visualized (or a \"Cache\" cluster with previously seen data).");
//help.SetToolTip(this.autoRefreshToolStripMenuItem, "Select to refresh the cluster view every 30 seconds.");
#endregion
this.Status("Please pick a cluster.", StatusKind.OK);
}
/// <summary>
/// Display the current status.
/// </summary>
/// <param name="msg">Message to display.</param>
/// <param name="kind">Kind of status message.</param>
public void Status(string msg, StatusKind kind)
{
this.status.Status(msg, kind);
}
/// <summary>
/// The used has selected a new cluster.
/// </summary>
/// <param name="clusterName">Cluster selected by the user.</param>
// ReSharper disable once UnusedMethodReturnValue.Local
private void ClusterSelected(string clusterName)
{
this.cluster = clusterName;
NetworkCredential credential = null;
try
{
ClusterConfiguration config = ClusterConfiguration.KnownClusterByName(clusterName);
this.Configuration = config;
}
catch (Exception ex)
{
this.Status("Could not connect to cluster " + clusterName + ": " + ex.Message, StatusKind.Error);
if (ex.Message.Contains("Unauthorized"))
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (credential != null)
// ReSharper disable once HeuristicUnreachableCode
InvalidateCredentials(credential.Domain);
}
return;
}
this.Text = "Cluster: " + clusterName;
this.clusterJobs.Clear();
{
this.comboBox_virtualCluster.Enabled = false;
this.label_vc.Enabled = false;
this.RefreshClusterJobList();
}
}
/// <summary>
/// The timer to refresh the view has ticked.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
void refreshTimer_Tick(object sender, EventArgs e)
{
this.RefreshClusterJobList();
}
/// <summary>
/// Cache here network credentials, mapping from domain to credential.
/// </summary>
static Dictionary<string, NetworkCredential> clusterCredentials = new Dictionary<string, NetworkCredential>();
/// <summary>
/// Probably password was mistyped; forget these credentials.
/// </summary>
/// <param name="domain">Domain whose password we have to forget.</param>
static void InvalidateCredentials(string domain)
{
clusterCredentials.Remove(domain);
}
/// <summary>
/// Queue a work item.
/// </summary>
/// <param name="item">Item to queue.</param>
/// <param name="cancellationPolicy">Policy which describes which queued items to cancel.</param>
private void Queue(IBackgroundWorkItem item, Func<IBackgroundWorkItem, bool> cancellationPolicy = null)
{
if (cancellationPolicy == null)
cancellationPolicy = i => item.Description == i.Description;
item.Queue(this.queue, this.Status, this.UpdateProgress, cancellationPolicy);
}
/// <summary>
/// Query the cluster again for the list of jobs.
/// </summary>
/// <returns>True if the cluster could be found.</returns>
private void RefreshClusterJobList()
{
if (this.cluster == null) return;
string clus = this.cluster;
if (!string.IsNullOrEmpty(this.SelectedVirtualCluster))
clus += "/" + this.SelectedVirtualCluster;
this.Status("Querying cluster " + clus, StatusKind.LongOp);
try
{
this.openFromURLToolStripMenuItem.Visible = false;
if (
this.Configuration is CacheClusterConfiguration)
{
this.filteredDataGridView.DataGridView.Columns["VirtualCluster"].Visible = true;
}
else
{
this.filteredDataGridView.DataGridView.Columns["VirtualCluster"].Visible = false;
}
this.clusterStatus = this.Configuration.CreateClusterStatus();
this.toolStripMenuItem_job.Enabled = true;
var item = new BackgroundWorkItem<List<ClusterJobInformation>>(
m => BuildClusterJobList(m, this.clusterStatus, this.SelectedVirtualCluster),
this.JobListRetrieved,
"getJobs");
this.Queue(item);
}
catch (Exception ex)
{
this.Status("Cannot retrieve information from cluster " + cluster + ": " + ex.Message, StatusKind.Error);
Trace.TraceInformation(ex.ToString());
this.comboBox_virtualCluster.Text = "";
}
}
private void JobListRetrieved(bool cancelled, List<ClusterJobInformation> jobs)
{
if (cancelled) return;
this.filteredDataGridView.DataGridView.ClearSelection();
this.completeJobsList = jobs;
this.clusterJobs.SetItems(this.completeJobsList);
if (!this.filteredDataGridView.RedoSort())
// ReSharper disable once AssignNullToNotNullAttribute
this.filteredDataGridView.DataGridView.Sort(this.filteredDataGridView.DataGridView.Columns["Date"], ListSortDirection.Descending);
this.filteredDataGridView.DataGridView.Focus();
this.filteredDataGridView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
}
/// <summary>
/// The view of the data grid has been changed.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
void filteredDataGridView_ViewChanged(object sender, EventArgs e)
{
this.Status("Showing " + this.clusterJobs.VisibleItemCount + "/" + this.completeJobsList.Count + " jobs", StatusKind.OK);
}
/// <summary>
/// Talk to the web server and build the list of clustr jobs; used it to populate the upper panel.
/// </summary>
/// <param name="virtualCluster">Virtual cluster selected; defined only for Scope clusters.</param>
/// <param name="manager">Communication manager.</param>
/// <param name="status">Cluster to scan.</param>
private static List<ClusterJobInformation> BuildClusterJobList(CommManager manager, ClusterStatus status, string virtualCluster)
{
return status.GetClusterJobList(virtualCluster, manager).ToList();
}
/// <summary>
/// Find cluster jobs selected in the upper pane.
/// </summary>
/// <returns>A reference to the cluster job information.</returns>
private IEnumerable<ClusterJobInformation> SelectedJobs()
{
DataGridViewSelectedRowCollection sel = this.filteredDataGridView.DataGridView.SelectedRows;
if (sel.Count < 1)
{
Status("You must select exactly some rows in the upper pane.", StatusKind.Error);
yield break;
}
for (int i = 0; i < sel.Count; i++)
{
ClusterJobInformation ti = sel[i].DataBoundItem as ClusterJobInformation;
yield return ti;
}
}
/// <summary>
/// Start the job browser from a job summary.
/// </summary>
/// <param name="js">Job summary to browse.</param>
private void browseFromJobSummary(DryadLinqJobSummary js)
{
if (js == null)
return;
// TODO: this should run in the background
CommManager manager = new CommManager(this.Status, delegate { }, new System.Threading.CancellationTokenSource().Token);
DryadLinqJobInfo job = DryadLinqJobInfo.CreateDryadLinqJobInfo(this.clusterStatus.Config, js, false, manager);
if (job != null)
{
JobBrowser browser = new JobBrowser(job);
browser.Show();
this.Status("OK", StatusKind.OK);
}
else
{
this.Status("Could not find information about job", StatusKind.Error);
}
}
/// <summary>
/// The user has double-clicked a row in the clusterJobTable. Browse the job.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Event describing position.</param>
private void filteredDataGridView_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex < 0)
return;
ClusterJobInformation task = (ClusterJobInformation)this.filteredDataGridView.DataGridView.Rows[e.RowIndex].DataBoundItem;
DryadLinqJobSummary js = task.DiscoverDryadLinqJob(this.clusterStatus, this.Status);
if (js == null)
{
this.Status("Error discovering job information on cluster.", StatusKind.Error);
}
else
{
this.Status("Starting job browser...", StatusKind.LongOp);
this.browseFromJobSummary(js);
}
}
/// <summary>
/// Exit application.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void exitToolStripMenuItem2_Click(object sender, EventArgs e)
{
this.Close();
}
/// <summary>
/// Start a job browser on the specified job.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void jobBrowserToolStripMenuItem_Click(object sender, EventArgs e)
{
IEnumerable<ClusterJobInformation> ti = this.SelectedJobs();
this.Status("Starting job browser...", StatusKind.LongOp);
IEnumerable<DryadLinqJobSummary> jobs = ti.Select(t => t.DiscoverDryadLinqJob(this.clusterStatus, this.Status)).ToList();
CommManager manager = new CommManager(this.Status, delegate { }, new System.Threading.CancellationTokenSource().Token);
IEnumerable<DryadLinqJobInfo> detailed = jobs.Select(j => DryadLinqJobInfo.CreateDryadLinqJobInfo(this.clusterStatus.Config, j, false, manager));
foreach (DryadLinqJobInfo j in detailed)
{
if (j == null) continue;
JobBrowser jb = new JobBrowser(j);
jb.Show();
}
this.Status("OK", StatusKind.OK);
}
/// <summary>
/// User clicked the refresh menu item.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void refreshToolStripMenuItem1_Click(object sender, EventArgs e)
{
this.RefreshClusterJobList();
}
/// <summary>
/// User opens a job by typing a job url.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void openFromURLToolStripMenuItem_Click(object sender, EventArgs e)
{
var dialog = new CustomDialog("Job URL:");
DialogResult result = dialog.ShowDialog();
switch (result)
{
case DialogResult.Cancel:
default:
return;
case DialogResult.OK:
{
string url = dialog.UserInput;
if (url == "")
return;
DryadLinqJobSummary summary = null;
try
{
summary = this.clusterStatus.DiscoverDryadLinqJobFromURL(url, this.Status);
}
catch (Exception ex)
{
this.Status("Could not find job associated to url " + url + ": exception " + ex.Message, StatusKind.Error);
}
if (summary == null)
{
this.Status("Could not locate job associated to url " + url, StatusKind.Error);
}
this.browseFromJobSummary(summary);
break;
}
}
}
/// <summary>
/// User intends to terminate the selected job.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void terminateToolStripMenuItem_Click(object sender, EventArgs e)
{
var todo = this.SelectedJobs().ToList();
if (todo.Count() != 1)
{
this.Status("You have to select exactly one job to terminate", StatusKind.Error);
return;
}
IEnumerable<DryadLinqJobSummary> jobs = todo.Select(j => j.DiscoverDryadLinqJob(this.clusterStatus, this.Status)).Where(j => j != null);
var item = new BackgroundWorkItem<bool>(
m => ClusterWork.CancelJobs(jobs, this.clusterStatus, m),
(c, b) => { },
"cancel");
this.Queue(item);
}
/// <summary>
/// Update the progress bar.
/// </summary>
/// <param name="value">Value to set the progress bar to.</param>
public void UpdateProgress(int value)
{
if (this.InvokeRequired)
{
this.Invoke(new Action<int>(this.UpdateProgress), value);
}
else
{
if (this.toolStripProgressBar != null)
// there could be overflows here
this.toolStripProgressBar.Value = Math.Min(value, 100);
}
}
/// <summary>
/// The user is closing the form; save application settings.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void ClusterBrowser_FormClosing(object sender, FormClosingEventArgs e)
{
this.refreshTimer.Stop();
this.queue.Stop();
this.formSettings.Location = this.Location;
this.formSettings.Size = this.Size;
this.formSettings.MaximizeWindow = this.WindowState == FormWindowState.Maximized;
this.formSettings.AutoRefresh = this.autoRefreshToolStripMenuItem.Checked;
this.formSettings.KnownClusters = ClusterConfiguration.KnownClustersSerialization();
this.formSettings.Save();
}
/// <summary>
/// Form is being loaded; restore settings.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void ClusterBrowser_Load(object sender, EventArgs e)
{
this.formSettings = new ClusterBrowserSettings();
Rectangle rect = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
// set location only if it is inside
if (rect.Contains(this.formSettings.Location))
this.Location = this.formSettings.Location;
bool maximized = this.formSettings.MaximizeWindow;
if (maximized)
this.WindowState = FormWindowState.Maximized;
else
// then we care about the size
this.Size = this.formSettings.Size;
this.autoRefreshToolStripMenuItem.Checked = this.formSettings.AutoRefresh;
this.AddClusterNameToMenu("<add>");
this.AddClusterNameToMenu("<scan>");
ClusterConfiguration.ReconstructKnownCluster(this.formSettings.KnownClusters);
int found = 0;
IEnumerable<string> clusters = ClusterConfiguration.GetKnownClusterNames();
foreach (string c in clusters)
{
this.AddClusterNameToMenu(c);
var config = ClusterConfiguration.KnownClusterByName(c);
if (config is CacheClusterConfiguration)
{
(config as CacheClusterConfiguration).StartCaching();
}
found++;
}
if (found == 0)
// try to find them by scanning
this.ScanClusters();
}
/// <summary>
/// Add a new menu item with a cluster name, if not already there.
/// </summary>
/// <param name="clusterName">Name to add to the cluster menu.</param>
private void AddClusterNameToMenu(string clusterName)
{
int count = this.clusterToolStripMenuItem.DropDownItems.Count;
for (int i = 0; i < count; i++)
{
var item = this.clusterToolStripMenuItem.DropDownItems[i];
if (item.Text == clusterName) return;
}
ToolStripMenuItem newItem = new ToolStripMenuItem(clusterName);
this.clusterToolStripMenuItem.DropDownItems.Add(newItem);
if (clusterName == "<add>")
{
newItem.Click += this.AddNewCluster;
return;
}
if (clusterName == "<scan>")
{
newItem.Click += this.ScanClusters;
return;
}
var selItem = newItem.DropDownItems.Add("Select");
var delItem = newItem.DropDownItems.Add("Delete");
var editItem = newItem.DropDownItems.Add("Edit");
delItem.Click += delItem_Click;
selItem.Click += selItem_Click;
editItem.Click += editItem_Click;
}
/// <summary>
/// Scan the clusters we are subscribed to and add them to the list of known clusters.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void ScanClusters(object sender, EventArgs e)
{
this.ScanClusters();
}
/// <summary>
/// Scan the clusters we are subscribed to and add them to the list of known clusters.
/// </summary>
private void ScanClusters()
{
this.Status("Scanning for known clusters", StatusKind.LongOp);
foreach (var conf in ClusterConfiguration.EnumerateSubscribedClusters())
{
ClusterConfiguration.AddKnownCluster(conf);
this.AddClusterNameToMenu(conf.Name);
this.Status("Adding cluster " + conf.Name, StatusKind.OK);
}
this.Status("Scan completed", StatusKind.OK);
}
/// <summary>
/// Edit a cluster.
/// </summary>
/// <param name="sender">MenuItem that is clicked.</param>
/// <param name="e">Unused.</param>
void editItem_Click(object sender, EventArgs e)
{
ToolStripItem item = sender as ToolStripItem;
if (item == null) return;
ToolStripItem strip = item.OwnerItem;
string clus = strip.Text;
var config = this.EditCluster(clus);
if (config != null)
{
ClusterConfiguration.AddKnownCluster(config);
this.AddClusterNameToMenu(config.Name);
}
}
void selItem_Click(object sender, EventArgs e)
{
ToolStripItem item = sender as ToolStripItem;
if (item == null) return;
ToolStripItem strip = item.OwnerItem;
string clus = strip.Text;
this.ClusterSelected(clus);
}
void AddNewCluster(object sender, EventArgs e)
{
ClusterConfiguration conf = this.EditCluster(null);
if (conf == null) return;
// you cannot have two cache clusters at the same time
if (conf is CacheClusterConfiguration)
{
foreach (var name in ClusterConfiguration.GetKnownClusterNames())
{
var config = ClusterConfiguration.KnownClusterByName(name);
if (config is CacheClusterConfiguration)
{
DialogResult res = MessageBox.Show("You cannot have two cache clusters at once: " + conf.Name + " and " + config.Name + "\nPress OK to use " + conf.Name + " instead of " + config.Name);
if (res == System.Windows.Forms.DialogResult.OK)
{
ClusterConfiguration.RemoveKnownCluster(config.Name);
(config as CacheClusterConfiguration).StopCaching();
(conf as CacheClusterConfiguration).StartCaching();
}
else
{
return;
}
}
}
}
ClusterConfiguration.AddKnownCluster(conf);
this.AddClusterNameToMenu(conf.Name);
}
/// <summary>
/// Edit the information about the specified cluster. If null create a new cluster.
/// </summary>
/// <param name="clusterName">Cluster that is being edited.</param>
/// <returns>The name of the edited cluster, or null if the operation is cancelled.</returns>
private ClusterConfiguration EditCluster(string clusterName)
{
ClusterConfigEditor editor = new ClusterConfigEditor();
if (clusterName != null)
{
var config = ClusterConfiguration.KnownClusterByName(clusterName);
editor.SetConfigToEdit(config);
}
DialogResult res = editor.ShowDialog();
if (res == System.Windows.Forms.DialogResult.OK)
{
var config = editor.GetConfiguration();
return config;
}
return null;
}
/// <summary>
/// Delete menu item clicked.
/// </summary>
/// <param name="sender">Menu item clicked.</param>
/// <param name="e">Unused.</param>
void delItem_Click(object sender, EventArgs e)
{
ToolStripItem item = sender as ToolStripItem;
if (item == null) return;
ToolStripItem strip = item.OwnerItem;
string clus = strip.Text;
this.RemoveClusterName(clus);
}
/// <summary>
/// Remove a menu item from the cluster menu.
/// </summary>
/// <param name="clusterName">Name of cluster to remove.</param>
private void RemoveClusterName(string clusterName)
{
var config = ClusterConfiguration.RemoveKnownCluster(clusterName);
if (config is CacheClusterConfiguration)
{
(config as CacheClusterConfiguration).StopCaching();
}
for (int i = 0; i < this.clusterToolStripMenuItem.DropDownItems.Count; i++)
{
var item = this.clusterToolStripMenuItem.DropDownItems[i];
if (item.Text == clusterName)
{
this.clusterToolStripMenuItem.DropDownItems.RemoveAt(i);
return;
}
}
//throw new ArgumentException("Menu does not contain cluster " + clusterName);
}
/// <summary>
/// Diagnose the failures of the selected jobs.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void diagnoseToolStripMenuItem_Click(object sender, EventArgs e)
{
IEnumerable<ClusterJobInformation> todo = this.SelectedJobs();
IEnumerable<DryadLinqJobSummary> jobs = todo.Select(j => j.DiscoverDryadLinqJob(this.clusterStatus, this.Status)).Where(j => j != null);
var item = new BackgroundWorkItem<List<DiagnosisLog>>(
m => ClusterWork.DiagnoseJobs(jobs, this.clusterStatus.Config, m),
DiagnosisResult.ShowDiagnosisResult,
"cancel");
this.Queue(item);
}
private void filteredDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
ClusterJobInformation t = (ClusterJobInformation)this.filteredDataGridView.DataGridView.Rows[e.RowIndex].DataBoundItem;
switch (t.Status)
{
case ClusterJobInformation.ClusterJobStatus.Cancelled:
e.CellStyle.BackColor = Color.Yellow;
break;
case ClusterJobInformation.ClusterJobStatus.Succeeded:
e.CellStyle.BackColor = Color.LightGreen;
break;
case ClusterJobInformation.ClusterJobStatus.Failed:
e.CellStyle.BackColor = Color.Tomato;
break;
case ClusterJobInformation.ClusterJobStatus.Unknown:
e.CellStyle.BackColor = Color.White;
break;
case ClusterJobInformation.ClusterJobStatus.Running:
e.CellStyle.BackColor = Color.Cyan;
break;
}
if (t.IsUnavailable)
e.CellStyle.BackColor = Color.Gray;
}
/// <summary>
/// Open a new window of the cluster browser.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void newWindowToolStripMenuItem_Click(object sender, EventArgs e)
{
ClusterBrowser browser = new ClusterBrowser();
browser.Show();
}
/// <summary>
/// User changed the log file.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void logFileToolStripMenuItem_Click(object sender, EventArgs e)
{
FileDialog file = new OpenFileDialog();
file.CheckFileExists = false;
DialogResult res = file.ShowDialog();
if (res != DialogResult.OK)
this.SetLogFile(this.LogFile != null ? this.LogFile.Name : "", false);
else
this.SetLogFile(file.FileName, true);
}
/// <summary>
/// Change the log file and the status of logging.
/// </summary>
/// <param name="filename">File to log info to.</param>
/// <param name="enabled">If true logging is enabled.</param>
private void SetLogFile(string filename, bool enabled)
{
if (string.IsNullOrEmpty(filename) && enabled)
{
enabled = false;
}
// remove the current listener
if (this.LogFile != null && Trace.Listeners.Contains(this.LogFile))
{
this.LogFile.Flush();
Trace.Listeners.Remove(this.LogFile);
}
if (enabled)
{
this.LogFile = new TextWriterTraceListener(filename);
Trace.Listeners.Add(this.LogFile);
}
this.logFileToolStripMenuItem.Checked = enabled;
}
/// <summary>
/// The user has selected a virtual cluster.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void comboBox_virtualCluster_SelectedIndexChanged(object sender, EventArgs e)
{
this.RefreshClusterJobList();
}
/// <summary>
/// User pressed enter in virtual cluster combo box.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Key description.</param>
private void comboBox_virtualCluster_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == '\r')
{
this.RefreshClusterJobList();
}
}
/// <summary>
/// Change auto-refresh status.
/// </summary>
/// <param name="sender">Unused.</param>
/// <param name="e">Unused.</param>
private void autoRefreshToolStripMenuItem_Click(object sender, EventArgs e)
{
this.autoRefreshToolStripMenuItem.Checked = !this.autoRefreshToolStripMenuItem.Checked;
if (this.autoRefreshToolStripMenuItem.Checked)
this.refreshTimer.Start();
else
this.refreshTimer.Stop();
}
}
/// <summary>
/// Persistent settings for the job browser.
/// </summary>
public class ClusterBrowserSettings : ApplicationSettingsBase
{
/// <summary>
/// Form location on screen.
/// </summary>
[UserScopedSetting]
[DefaultSettingValue("0,0")]
public Point Location
{
get { return (Point)this["Location"]; }
set { this["Location"] = value; }
}
/// <summary>
/// Form size.
/// </summary>
[UserScopedSetting]
[DefaultSettingValue("1186, 620")]
public System.Drawing.Size Size
{
get { return (System.Drawing.Size)this["Size"]; }
set { this["Size"] = value; }
}
/// <summary>
/// Size of window.
/// </summary>
[UserScopedSetting]
[DefaultSettingValue("false")]
public bool MaximizeWindow
{
get { return (bool)this["MaximizeWindow"]; }
set { this["MaximizeWindow"] = value; }
}
/// <summary>
/// Does the window auto-refresh.
/// </summary>
[UserScopedSetting]
[DefaultSettingValue("false")]
public bool AutoRefresh
{
get { return (bool)this["AutoRefresh"]; }
set { this["AutoRefresh"] = value; }
}
/// <summary>
/// Does the window auto-refresh.
/// </summary>
[UserScopedSetting]
[DefaultSettingValue("true")]
public bool ReducedFunctionality
{
get { return (bool)this["ReducedFunctionality"]; }
set { this["ReducedFunctionality"] = value; }
}
/// <summary>
/// List of known clusters.
/// </summary>
[UserScopedSetting]
public List<ClusterConfigurationSerialization> KnownClusters
{
get {
var kc = this["KnownClusters"];
if (kc != null)
return (List<ClusterConfigurationSerialization>)kc;
else
return null;
}
set { this["KnownClusters"] = value; }
}
}
/// <summary>
/// This class contains a set of static methods which are invoked in the background to perform actions on the cluster.
/// </summary>
internal static class ClusterWork
{
/// <summary>
/// Cancel a job.
/// </summary>
/// <param name="jobs">Jobs to cancel.</param>
/// <param name="cluster">Cluster where the jobs are running.</param>
/// <returns>True if all cancellations succeed.</returns>
/// <param name="manager">Communicatoni manager.</param>
// ReSharper disable once UnusedParameter.Global
public static bool CancelJobs(IEnumerable<DryadLinqJobSummary> jobs, ClusterStatus cluster, CommManager manager)
{
bool done = true;
foreach (DryadLinqJobSummary job in jobs)
{
manager.Token.ThrowIfCancellationRequested();
if (job.Status != ClusterJobInformation.ClusterJobStatus.Running)
{
manager.Status("Job " + job.Name + " does not appear to be running; will still try to cancel", StatusKind.Error);
}
bool success;
string reason = "";
try
{
success = cluster.CancelJob(job);
}
catch (Exception ex)
{
success = false;
reason = ex.Message;
Trace.TraceInformation(ex.ToString());
}
if (success)
manager.Status("Job " + job.Name + " cancelled", StatusKind.OK);
else
manager.Status("Cancellation of " + job.Name + " failed " + reason, StatusKind.Error);
done &= success;
}
return done;
}
/// <summary>
/// Diagnose a list of jobs.
/// </summary>
/// <param name="jobs">Jobs to diagnose.</param>
/// <param name="config">Cluster configuration.</param>
/// <param name="manager">Communicatino manager.</param>
public static List<DiagnosisLog> DiagnoseJobs(IEnumerable<DryadLinqJobSummary> jobs, ClusterConfiguration config, CommManager manager)
{
var dryadLinqJobSummaries = jobs as DryadLinqJobSummary[] ?? jobs.ToArray();
int jobCount = dryadLinqJobSummaries.Count();
List<DiagnosisLog> result = new List<DiagnosisLog>();
int done = 0;
foreach (DryadLinqJobSummary summary in dryadLinqJobSummaries)
{
if (summary == null) continue;
manager.Token.ThrowIfCancellationRequested();
JobFailureDiagnosis diagnosis = JobFailureDiagnosis.CreateJobFailureDiagnosis(config, summary, manager);
manager.Status("Diagnosing " + summary.ShortName(), StatusKind.LongOp);
DiagnosisLog log = diagnosis.Diagnose();
result.Add(log);
done++;
manager.Progress(done * 100 / jobCount);
}
manager.Status("Diagnosis complete", StatusKind.OK);
return result;
}
}
}