diff --git a/LifeSciences/AzureBlast/AzureBlast.Web/AzureBlast.Web.csproj b/LifeSciences/AzureBlast/AzureBlast.Web/AzureBlast.Web.csproj index 401dd57..9c42eab 100644 --- a/LifeSciences/AzureBlast/AzureBlast.Web/AzureBlast.Web.csproj +++ b/LifeSciences/AzureBlast/AzureBlast.Web/AzureBlast.Web.csproj @@ -307,6 +307,7 @@ + @@ -423,6 +424,7 @@ + diff --git a/LifeSciences/AzureBlast/AzureBlast.Web/Controllers/PoolController.cs b/LifeSciences/AzureBlast/AzureBlast.Web/Controllers/PoolController.cs index 9743b53..1715648 100644 --- a/LifeSciences/AzureBlast/AzureBlast.Web/Controllers/PoolController.cs +++ b/LifeSciences/AzureBlast/AzureBlast.Web/Controllers/PoolController.cs @@ -1,7 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.IO; +using System.Linq; +using System.Net.Mime; +using System.Text; +using System.Web; using System.Web.Mvc; +using Microsoft.Azure.Batch; using Microsoft.Azure.Batch.Blast.Configuration; using Microsoft.Azure.Blast.Web.Models; @@ -10,10 +16,12 @@ namespace Microsoft.Azure.Blast.Web.Controllers public class PoolController : AuthorizedController { private readonly BlastConfiguration _configuration; + private readonly BatchClient _batchClient; public PoolController(BlastConfiguration configuration) { _configuration = configuration; + _batchClient = configuration.BatchClient; } public ActionResult Index() @@ -29,5 +37,67 @@ namespace Microsoft.Azure.Blast.Web.Controllers }; return View(model); } + + [Route("Pool/{poolId}")] + public ActionResult Show(string poolId) + { + var pool = _batchClient.PoolOperations.GetPool(poolId); + + if (pool == null) + { + return new HttpNotFoundResult("No such pool"); + } + + var model = new PoolDetailsModel + { + Pool = pool, + ComputeNodes = _batchClient.PoolOperations.ListComputeNodes(poolId).ToList(), + }; + + return View(model); + } + + [Route("Pool/{poolId}/computenodes/{computeNodeId}/files/{fileName}/{fileExtension}")] + public ActionResult DownloadStartTaskFile(string poolId, string computeNodeId, string fileName, string fileExtension) + { + var pool = _batchClient.PoolOperations.GetPool(poolId); + + if (pool == null) + { + return new HttpNotFoundResult("No such pool"); + } + + var node = _batchClient.PoolOperations.GetComputeNode(poolId, computeNodeId); + + if (node == null) + { + return new HttpNotFoundResult("No such node"); + } + + fileName = fileName + "." + fileExtension; + var filePath = "startup/" + fileName; + + var nodeFile = _batchClient.PoolOperations.GetNodeFile(poolId, computeNodeId, filePath); + + if (nodeFile == null) + { + return new HttpNotFoundResult("No such node file"); + } + + var content = nodeFile.ReadAsString(); + + string filename = Path.GetFileName(fileName); + byte[] filedata = Encoding.UTF8.GetBytes(content); + + var cd = new ContentDisposition + { + FileName = filename, + Inline = true, + }; + + Response.AppendHeader("Content-Disposition", cd.ToString()); + + return File(filedata, MediaTypeNames.Text.Plain, fileName); + } } } \ No newline at end of file diff --git a/LifeSciences/AzureBlast/AzureBlast.Web/Models/PoolDetailsModel.cs b/LifeSciences/AzureBlast/AzureBlast.Web/Models/PoolDetailsModel.cs new file mode 100644 index 0000000..7898c5c --- /dev/null +++ b/LifeSciences/AzureBlast/AzureBlast.Web/Models/PoolDetailsModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Microsoft.Azure.Batch; + +namespace Microsoft.Azure.Blast.Web.Models +{ + public class PoolDetailsModel + { + public CloudPool Pool { get; set; } + public List ComputeNodes { get; set; } + } +} \ No newline at end of file diff --git a/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Index.cshtml b/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Index.cshtml index 0813167..ffd5260 100644 --- a/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Index.cshtml +++ b/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Index.cshtml @@ -40,23 +40,25 @@ $(function () { $('#pooltblbody').on('click', 'tr td a', function () { - if (confirm("Are you sure you want to delete the pool: " + $(this).attr('id') + "?")) { - $.ajax({ - type: 'DELETE', - url: '/api/pools/' + $(this).attr('id'), - contentType: false, - processData: false, - success: function (response) { - window.location.href = "/Pool"; - }, - error: function (data) { - $('#btnCreatePool').removeClass("disabled"); - console.log(data); - } - }); + if ($(this).text() === 'Delete') { + if (confirm("Are you sure you want to delete the pool: " + $(this).attr('id') + "?")) { + $.ajax({ + type: 'DELETE', + url: '/api/pools/' + $(this).attr('id'), + contentType: false, + processData: false, + success: function (response) { + window.location.href = "/Pool"; + }, + error: function (data) { + $('#btnCreatePool').removeClass("disabled"); + console.log(data); + } + }); + } } }); - + $.get('/api/pools', null, function(pools) { $('progress').hide(); @@ -68,7 +70,7 @@ var tr = "\ \ - " + pool.id + "\ + "+pool.id+"\ \ \ " + displayName + "\ @@ -88,9 +90,7 @@ \ " + pool.targetDedicated + "\ \ - \ - Delete\ - \ + Delete\ ;"; $('#pooltblbody').append(tr); diff --git a/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Show.cshtml b/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Show.cshtml new file mode 100644 index 0000000..7b67756 --- /dev/null +++ b/LifeSciences/AzureBlast/AzureBlast.Web/Views/Pool/Show.cshtml @@ -0,0 +1,109 @@ +@model Microsoft.Azure.Blast.Web.Models.PoolDetailsModel +@{ + ViewBag.Title = "Pool - " + @Model.Pool.Id; +} + +

 

+ + + +
+
+

Pool '@Model.Pool.Id' Details

+
+ Delete +
+
+
+
+
+ +
+

@Model.Pool.Id

+
+
+
+ +
+

@Model.Pool.DisplayName

+
+
+
+ +
+

@Model.Pool.State

+
+
+
+ +
+

@Model.Pool.AllocationState

+
+
+
+ +
+

@Model.Pool.VirtualMachineSize

+
+
+
+ +
+

@Model.Pool.CurrentDedicated

+
+
+
+ +
+

@Model.Pool.TargetDedicated

+
+
+
+ +
+

@(Model.Pool.ResizeError == null ? "" : string.Format("{0} - {1}", Model.Pool.ResizeError.Code, Model.Pool.ResizeError.Message))

+
+
+
+ +
+

@Model.Pool.CreationTime

+
+
+
+
+
+ +
+
Compute Nodes
+ + + + + + + + + + + + + + @foreach (var computeNode in Model.ComputeNodes) + { + + + + + + + + + + } + +
IdStateStart Task StateStart Task Exit CodeStart Task Scheduling ErrorsStart Task StdOutStart Task StdErr
@computeNode.Id@computeNode.State@(computeNode.StartTaskInformation == null ? "" : computeNode.StartTaskInformation.State.ToString())@(computeNode.StartTaskInformation == null || computeNode.StartTaskInformation.ExitCode == null ? "" : computeNode.StartTaskInformation.ExitCode.ToString())@(computeNode.StartTaskInformation == null || computeNode.StartTaskInformation.SchedulingError == null ? "" : string.Format("Category: {0}Code: {1}Details: {2}", computeNode.StartTaskInformation.SchedulingError.Category, computeNode.StartTaskInformation.SchedulingError.Code, string.Join(",", computeNode.StartTaskInformation.SchedulingError.Details.Select(detail => detail.Name + ": " + detail.Value))))@Html.ActionLink("stdout.txt", "DownloadStartTaskFile", "Pool", new { poolId = Model.Pool.Id, computeNodeId = computeNode.Id, fileName = "stdout", fileExtension = "txt" }, null)@Html.ActionLink("stderr.txt", "DownloadStartTaskFile", "Pool", new { poolId = Model.Pool.Id, computeNodeId = computeNode.Id, fileName = "stderr" , fileExtension = "txt" }, null)
+