Skip to content

Commit

Permalink
[#153] Improve quotas.html to use datatables
Browse files Browse the repository at this point in the history
  • Loading branch information
pjeli authored Oct 31, 2018
1 parent b5a51dc commit 07e2769
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 115 deletions.
28 changes: 28 additions & 0 deletions docs/REST_Endpoints/Quotas.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@ It takes an optional parameter `?user=<user>` to specify looking at cached direc
It also takes a required parameter `&sum=<nsQuotaRatioUsed|dsQuotaRatioUsed>` to specify which quota you wish to look at; either namespace or diskspace.
Directories can be added to NNA for quota scanning via `/addDirectory` and `/removeDirectory` ADMIN endpoints.

If you make the call with `?all` as the parameter then the JSON dump will contain information of all directory quotas.

Example response:
```json
{
nsQuota: {
user1: {
/dir1/dir1: 10,
/dir1/dir2: 20
},
user2: {
/dir2/dir2: 30,
/dir3/dir3: 40
}
},
dsQuota: {
user1: {
/dir1/dir1: 50,
/dir1/dir2: 60
},
user2: {
/dir2/dir2: 70,
/dir3/dir3: 80
}
}
}
```

Response code is 200 and a JSON dump of directories and a mapping to a percentage (0-100) of their quota used.

Response code of 403 means you are not authorized to view this endpoint.
11 changes: 8 additions & 3 deletions src/main/java/com/paypal/namenode/WebServerMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -1627,9 +1627,14 @@ public void init(
(req, res) -> {
res.header("Access-Control-Allow-Origin", "*");
res.header("Content-Type", "application/json");
String user = req.queryMap("user").value();
String sum = req.queryMap("sum").value();
return nameNodeLoader.getSuggestionsEngine().getQuotaAsJson(user, sum);
boolean allJson = req.queryMap().toMap().containsKey("all");
if (allJson) {
return nameNodeLoader.getSuggestionsEngine().getAllQuotasAsJson();
} else {
String user = req.queryMap("user").value();
String sum = req.queryMap("sum").value();
return nameNodeLoader.getSuggestionsEngine().getQuotaAsJson(user, sum);
}
});

/* USERS endpoint is an admin-level endpoint meant to dump the cached set of detected users by NNA. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,20 @@ public Set<String> getDirectoriesForAnalysis() {
return cachedDirs;
}

/**
* Get all quota information from cache as a JSON String.
*
* @return quota info returned as JSON string
*/
public String getAllQuotasAsJson() {
Map<String, Map<String, Map<String, Long>>> allQuotaRatios = new HashMap<>();
Map<String, Map<String, Long>> nsQuotas = cachedUserNsQuotas;
Map<String, Map<String, Long>> dsQuotas = cachedUserDsQuotas;
allQuotaRatios.put("nsQuotas", nsQuotas);
allQuotaRatios.put("dsQuotas", dsQuotas);
return Histograms.toJson(allQuotaRatios);
}

/**
* Get quota information from cache as a JSON String.
*
Expand Down
110 changes: 0 additions & 110 deletions src/main/resources/public/qoutas.html

This file was deleted.

170 changes: 170 additions & 0 deletions src/main/resources/public/quotas.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!DOCTYPE html>
<html lang="en">
<script src="./js/jquery.min.js"></script>
<script src="./js/nna.utility.js"></script>
<script type="text/javascript">checkIfAuthenticated();</script>
<head>
<!-- Bootstrap core CSS -->
<link href="./css/bootstrap.min.css" rel="stylesheet">

<!-- Sweetalert core CSS -->
<link href="./css/sweetalert.css" rel="stylesheet">

<!-- Custom styles for this template -->
<link href="./css/nna.css" rel="stylesheet">
<link href="./css/datatables.min.css" rel="stylesheet">
<link href="./css/buttons.datatables.min.css" rel="stylesheet">
</head>

<body>

<div class="container">

<div id="navbar"></div>

<!-- Main component for a primary marketing message or call to action -->
<div class="jumbotron">
<h1>Welcome to NNAnalytics</h1>
<div id="connection"></div>
<p>
View directories that have quotas set on them and how filled those quotas are.<br />
</p>
<br />
<br />
</div>
<div id="quotaMetrics">
<table id="quotaDataTable" class="table table-bordered" style="width: 100%;">
<thead>
<tr>
<th>Path</th>
<th>Owner</th>
<th>Namespace Quota % Used</th>
<th>Diskspace Quota % Used</th>
</tr>
</thead>
</table>
</div>

</div> <!-- /container -->


<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="./js/bootstrap.min.js"></script>
<script src="./js/sweetalert.min.js"></script>
<script src="./js/Chart.bundle.min.js"></script>
<script src="./js/cycle.js"></script>
<script src="./js/datatables.min.js"></script>
<script src="./js/dataTables.buttons.min.js"></script>
<script src="./js/jszip.min.js"></script>
<script src="./js/buttons.html5.min.js"></script>
<script type="text/javascript">$(document).ready(function(){ $("head").append("header.html");});</script>
<script type="text/javascript">getClusterName();</script>

<script>
$(document).ready(function() {
$('#navbar').load('navbar.html', function() {
checkIfAdmin();
});

function union(...iterables) {
const set = new Set();
for (let iterable of iterables) {
for (let item of iterable) {
set.add(item);
}
}
return set;
}

function flattenObject(obj) {
var array = [];
var dsQuotas = obj.dsQuotas;
var nsQuotas = obj.nsQuotas;

var users = union(Object.keys(dsQuotas), Object.keys(nsQuotas));
var paths = union(Object.keys(Object.values(dsQuotas)[0]), Object.keys(Object.values(nsQuotas)[0]));

for(var user of users) {
var paths = union(Object.keys(dsQuotas[user]), Object.keys(nsQuotas[user]));
for(var path of paths) {
var innerArray = [];
innerArray.push(path);
innerArray.push(user);
var dsQuota = dsQuotas[user][path];
if(dsQuota != null) {
innerArray.push(dsQuotas[user][path]);
} else {
innerArray.push(-1);
}
var nsQuota = nsQuotas[user][path];
if(nsQuota != null) {
innerArray.push(nsQuotas[user][path]);
} else {
innerArray.push(-1);
}
array.push(innerArray);
}
}
return array
}

let table = $('#quotaDataTable').DataTable( {
"ajax": {
"url": "./quotas?all",
"type": "GET",
"dataSrc": function(json) {
console.log(json);
return flattenObject(json);
}
},
"iDisplayLength": 100,
"aLengthMenu": [[10, 50, 100, -1], [10, 50, 100, "All"]],
"order": [[3, "desc"], [2, "desc"]],
"dom": 'Bfrtipl',
"buttons": [
'copyHtml5', 'csvHtml5', 'excelHtml5'
],
"columns": [
{ title: "Path" },
{ title: "Username",
"render": function(data, type, row, meta) {
data = "<a target='_blank' href='./suggestion.html?username="+data+"'>" + data + "</a>";
return data;
}
},
{ title: "Namespace Quota % Used" },
{ title: "Diskspace Quota % Used" }
],
"columnDefs": [
{ width: "20%", targets: [1,2,3] }
]
});
table.draw();
});
</script>

</body>
</html>

11 changes: 9 additions & 2 deletions src/test/java/com/paypal/nnanalytics/TestNNAnalyticsBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ public void testUsersSuggestions() throws IOException {
assertThat(res.getStatusLine().getStatusCode(), is(200));
}

@Test
public void testAllQuotas() throws IOException {
HttpGet get = new HttpGet("http://localhost:4567/quotas?all");
HttpResponse res = client.execute(hostPort, get);
assertThat(res.getStatusLine().getStatusCode(), is(200));
}

@Test
public void testDsQuotas() throws IOException {
HttpGet get = new HttpGet("http://localhost:4567/quotas?sum=dsQuotaRatioUsed");
Expand Down Expand Up @@ -304,7 +311,7 @@ public void testModDateFilterGt() throws IOException {
}

@Test
public void testHasQouta() throws IOException {
public void testHasQuota() throws IOException {
HttpGet get =
new HttpGet("http://localhost:4567/filter?set=dirs&filters=hasQuota:eq:true&sum=count");
HttpResponse res = client.execute(hostPort, get);
Expand All @@ -315,7 +322,7 @@ public void testHasQouta() throws IOException {
}

@Test
public void testHasQoutaList() throws IOException {
public void testHasQuotaList() throws IOException {
HttpGet get = new HttpGet("http://localhost:4567/filter?set=dirs&filters=hasQuota:eq:true");
HttpResponse res = client.execute(hostPort, get);
List<String> result = IOUtils.readLines(res.getEntity().getContent());
Expand Down

0 comments on commit 07e2769

Please sign in to comment.