Przeglądaj źródła

HDFS-12759. Ozone: web: integrate configuration reader page to the SCM/KSM web ui. Contributed by Elek, Marton.

Anu Engineer 7 lat temu
rodzic
commit
4deb845d7c

+ 6 - 10
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/ksm/index.html

@@ -30,14 +30,14 @@
     <link href="/static/hadoop.css" rel="stylesheet">
     <link href="/static/nvd3-1.8.5.min.css" rel="stylesheet">
 
-    <link href="/main.css" rel="stylesheet">
+    <link href="/static/ozone.css" rel="stylesheet">
 
 </head>
 
 <body ng-app="ksm">
 
 <header class="navbar navbar-inverse navbar-fixed-top bs-docs-nav">
-    <div class="container">
+    <div class="container-fluid">
         <div class="navbar-header">
             <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                     aria-expanded="false" aria-controls="navbar">
@@ -48,17 +48,13 @@
             </button>
             <a class="navbar-brand" href="#">HDFS KSM</a>
         </div>
-        <common-tools></common-tools>
+        <navmenu
+                metrics="{ 'Ksm metrics' : '/#!/metrics/ksm', 'Rpc metrics' : '/#!/metrics/rpc'}"></navmenu>
     </div>
 </header>
 
-<div class="container">
-
-    <overview></overview>
-    <ksm-metrics></ksm-metrics>
-    <rpc-metrics></rpc-metrics>
-
-
+<div class="container-fluid">
+    <ng-view></ng-view>
 </div><!-- /.container -->
 
 <script src="/static/jquery-1.10.2.min.js"></script>

+ 6 - 1
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/ksm/ksm.js

@@ -23,7 +23,12 @@
     };
 
     angular.module('ksm', ['ozone', 'nvd3']);
-
+    angular.module('ksm').config(function ($routeProvider) {
+        $routeProvider
+            .when("/metrics/ksm", {
+                template: "<ksm-metrics></ksm-metrics>"
+            });
+    });
     angular.module('ksm').component('ksmMetrics', {
         templateUrl: 'ksm-metrics.html',
         controller: function ($http) {

+ 18 - 0
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/ksm/main.html

@@ -0,0 +1,18 @@
+<!--
+    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.
+  -->
+<overview>
+</overview>

+ 10 - 10
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/scm/index.html

@@ -30,14 +30,14 @@
     <link href="/static/hadoop.css" rel="stylesheet">
     <link href="/static/nvd3-1.8.5.min.css" rel="stylesheet">
 
-    <link href="/main.css" rel="stylesheet">
+    <link href="/static/ozone.css" rel="stylesheet">
 
 </head>
 
 <body ng-app="scm">
 
 <header class="navbar navbar-inverse navbar-fixed-top bs-docs-nav">
-    <div class="container">
+    <div class="container-fluid">
         <div class="navbar-header">
             <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                     aria-expanded="false" aria-controls="navbar">
@@ -48,18 +48,18 @@
             </button>
             <a class="navbar-brand" href="#">HDFS SCM</a>
         </div>
-        <common-tools></common-tools>
+
+
+        <navmenu
+                metrics="{ 'Rpc metrics' : '/#!/metrics/rpc'}"></navmenu>
+
+
     </div>
 </header>
 
-<div class="container">
-
-    <overview>
-        <scm-overview>
-        </scm-overview>
-    </overview>
-    <rpc-metrics></rpc-metrics>
+<div class="container-fluid" style="margin: 12pt">
 
+    <ng-view></ng-view>
 
 </div><!-- /.container -->
 

+ 20 - 0
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/scm/main.html

@@ -0,0 +1,20 @@
+<!--
+    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.
+  -->
+<overview>
+    <scm-overview>
+    </scm-overview>
+</overview>

+ 0 - 62
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/css/ozone-conf.css

@@ -1,62 +0,0 @@
-/*
-* 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.
-*/
-
-.btn {
-    border: 0 none;
-    font-weight: 700;
-    letter-spacing: 1px;
-    text-transform: uppercase;
-}
-
-.btn:focus, .btn:active:focus, .btn.active:focus {
-    outline: 0 none;
-}
-
-.btn-primary {
-    background: #F8F9F9;
-    color: #237676;
-}
-
-.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary {
-    background: #33a6cc;
-}
-
-.btn-primary:active, .btn-primary.active {
-    background: #007299;
-    box-shadow: none;
-}
-
-.top-buffer { margin-top:4px; }
-
-.tagPanel tr:nth-child(odd) {
-  background-color: #fff;
-}
-.tagPanel tr:nth-child(even) {
-  background-color: #d9ffcc;
-}
-
-.sortorder:after {
-  content: '\25b2';   // BLACK UP-POINTING TRIANGLE
-}
-.sortorder.reverse:after {
-  content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
-}
-
-.wrap-table{
-  word-wrap: break-word;
-  table-layout: fixed;
-}

+ 0 - 102
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/js/ozone-conf.js

@@ -1,102 +0,0 @@
-/**
- * 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.
- */
-var app = angular.module('app', []);
-app.controller('tagController', function($scope, $http,$filter) {
-    $scope.configArray = [];
-    $scope.selectedTags = [];
-
-    $http.get("/conf?cmd=getOzoneTags&group=ozone")
-        .then(function(response) {
-            $scope.tags = response.data;
-
-      var idx = $scope.tags.indexOf('CBLOCK');
-        // Remove CBLOCK related properties
-        if (idx > -1) {
-            console.log('Removing cblock configs')
-            $scope.tags.splice(idx, 1);
-        }
-      $scope.loadAll();
-        });
-
-    $scope.convertToArray = function(configAr) {
-        $scope.configArray = configAr;
-    }
-
-    $scope.loadAll = function() {
-      console.log("Displaying all configs");
-        $http.get("/conf?cmd=getPropertyByTag&tags=" + $scope.tags + "&group=ozone").then(function(response) {
-            $scope.configs = response.data;
-            $scope.convertToArray($scope.configs);
-            $scope.sortBy('name');
-        });
-    };
-
-    $scope.selected = function(tag) {
-        var idx = $scope.selectedTags.indexOf(tag);
-        // Is currently selected
-        if (idx > -1) {
-            $scope.selectedTags.splice(idx, 1);
-        } else {
-            $scope.selectedTags.push(tag);
-        }
-        console.log("Tags selected:" + $scope.selectedTags);
-        $scope.reloadConfig();
-    };
-
-    $scope.reloadConfig = function() {
-        if ($scope.selectedTags.length > 0) {
-            console.log("Displaying configs for:" + $scope.selectedTags);
-            $http.get("/conf?cmd=getPropertyByTag&tags=" + $scope.selectedTags + "&group=ozone").then(function(response) {
-                $scope.configs = response.data;
-                $scope.convertToArray($scope.configs);
-            });
-        }
-      else {
-            $scope.loadAll();
-      }
-
-    };
-
-    $scope.filterTags = function(tag) {
-        return (!['KSM', 'SCM', 'OZONE','CBLOCK'].includes(tag));
-    };
-
-    $scope.filterConfig = function(filter) {
-        $http.get("/conf?cmd=getPropertyByTag&tags=" + filter + "&group=ozone").then(function(response) {
-          var tmpConfig = response.data;
-          console.log('filtering config for tag:'+filter);
-          array3 = [];
-
-          for(var i1 in tmpConfig) {
-
-             for(var i2 in  $scope.configs) {
-              if(tmpConfig[i1].name == $scope.configs[i2].name){
-                array3.push( tmpConfig[i1]);
-              }
-            }
-          }
-        $scope.convertToArray(array3);
-        });
-    };
-
-  $scope.sortBy = function(propertyName) {
-    $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
-    $scope.propertyName = propertyName;
-  };
-
-});

+ 41 - 4
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/scm/main.css → hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/ozone.css

@@ -15,9 +15,46 @@
  *  limitations under the License.
 */
 body {
-  padding-top: 50px;
+    padding: 40px;
+    padding-top: 60px;
 }
 .starter-template {
-  padding: 40px 15px;
-  text-align: center;
-}
+    padding: 40px 15px;
+    text-align: center;
+}
+
+
+.btn {
+    border: 0 none;
+    font-weight: 700;
+    letter-spacing: 1px;
+    text-transform: uppercase;
+}
+
+.btn:focus, .btn:active:focus, .btn.active:focus {
+    outline: 0 none;
+}
+
+.table-striped > tbody > tr:nth-child(2n+1).selectedtag > td:hover {
+    background-color: #3276b1;
+}
+.table-striped > tbody > tr:nth-child(2n+1).selectedtag > td {
+    background-color: #3276b1;
+}
+.tagPanel tr.selectedtag td {
+    background-color: #3276b1;
+}
+.top-buffer { margin-top:4px; }
+
+
+.sortorder:after {
+    content: '\25b2';   // BLACK UP-POINTING TRIANGLE
+}
+.sortorder.reverse:after {
+    content: '\25bc';   // BLACK DOWN-POINTING TRIANGLE
+}
+
+.wrap-table{
+    word-wrap: break-word;
+    table-layout: fixed;
+}

+ 116 - 6
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/ozone.js

@@ -22,7 +22,19 @@
         return key == 'name' || key == 'modelerType' || key == "$$hashKey" ||
             key.match(/tag.*/);
     };
-    angular.module('ozone', ['nvd3'])
+    angular.module('ozone', ['nvd3', 'ngRoute']);
+    angular.module('ozone').config(function ($routeProvider) {
+        $routeProvider
+            .when("/", {
+                templateUrl: "main.html"
+            })
+            .when("/metrics/rpc", {
+                template: "<rpc-metrics></rpc-metrics>"
+            })
+            .when("/config", {
+                template: "<config></config>"
+            })
+    });
     angular.module('ozone').component('overview', {
         templateUrl: 'static/templates/overview.html',
         transclude: true,
@@ -37,13 +49,13 @@
     angular.module('ozone').component('jvmParameters', {
         templateUrl: 'static/templates/jvm.html',
         controller: function ($http) {
-            var ctrl = this
+            var ctrl = this;
             $http.get("/jmx?qry=java.lang:type=Runtime")
                 .then(function (result) {
                     ctrl.jmx = result.data.beans[0];
 
                     //convert array to a map
-                    var systemProperties = {}
+                    var systemProperties = {};
                     for (var idx in ctrl.jmx.SystemProperties) {
                         var item = ctrl.jmx.SystemProperties[idx];
                         systemProperties[item.key.replace(/\./g, "_")] = item.value;
@@ -208,7 +220,8 @@
                     ctrl.select(pane);
                 }
             },
-            template: '<div class="nav navtabs"><div class="container"><ul class="nav nav-pills">' +
+            template: '<div class="nav navtabs"><div class="row"><ul' +
+            ' class="nav nav-pills">' +
             '<li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}">' +
             '<a href="" ng-click="$ctrl.click(pane)">{{pane.title}}</a> ' +
             '</li> </ul></div><br/><div class="tab-content" ng-transclude></div> </div>'
@@ -229,8 +242,11 @@
             template: '<div class="tab-pane" ng-if="$ctrl.selected" ng-transclude></div>'
         });
 
-    angular.module('ozone').component('commonTools', {
-        templateUrl: '/static/templates/tools.html',
+    angular.module('ozone').component('navmenu', {
+        bindings: {
+            metrics: '<'
+        },
+        templateUrl: '/static/templates/menu.html',
         controller: function ($http) {
             var ctrl = this;
             ctrl.docs = false;
@@ -242,4 +258,98 @@
                 });
         }
     });
+
+    angular.module('ozone').component('config', {
+        templateUrl: '/static/templates/config.html',
+        controller: function ($scope, $http) {
+            var ctrl = this;
+            ctrl.selectedTags = [];
+
+            $http.get("/conf?cmd=getOzoneTags&group=ozone")
+                .then(function (response) {
+                    ctrl.tags = response.data;
+
+                    var excludedTags = ['CBLOCK', 'KSM', 'SCM'];
+                    for (var i = 0; i < excludedTags.length; i++) {
+                        var idx = ctrl.tags.indexOf(excludedTags[i]);
+                        // Remove CBLOCK related properties
+                        if (idx > -1) {
+                            ctrl.tags.splice(idx, 1);
+                        }
+                    }
+                    ctrl.loadAll();
+                });
+
+
+
+            ctrl.loadAll = function () {
+                console.log("Displaying all configs");
+                $http.get("/conf?cmd=getPropertyByTag&tags=" + ctrl.tags + "&group=ozone").then(function (response) {
+                    ctrl.configs = response.data;
+                    console.log(ctrl.configs)
+                    for (var idx in ctrl.configs) {
+                        var tags = []
+                        var parsedTags = ctrl.configs[idx].tag.split(",");
+                        for (var t in parsedTags) {
+                            tags.push(parsedTags[t].trim())
+                        }
+                        ctrl.configs[idx].tag = tags;
+
+                    };
+                    ctrl.sortBy('name');
+                });
+            };
+
+            ctrl.tagFilter = function (value, index, array) {
+                if (!ctrl.selectedTags) {
+                    return true;
+                }
+                var selected = true;
+                for (var idx in ctrl.selectedTags) {
+                    selected = selected && (value.tag.indexOf(ctrl.selectedTags[idx]) > -1);
+                }
+                return selected;
+            };
+            ctrl.configFilter = function (config) {
+                return false;
+            };
+            ctrl.selected = function (tag) {
+                return ctrl.selectedTags.includes(tag);
+            };
+
+            ctrl.allSelected = function () {
+                return ctrl.selectedTags.indexOf('SCM') == -1
+                    && ctrl.selectedTags.indexOf('KSM') == -1
+            };
+
+            ctrl.switchto = function (tag) {
+                var tags = ctrl.selectedTags.filter(function (item) {
+                    return item != 'KSM' && item != 'SCM';
+                });
+                if (tag) {
+                    tags.push(tag);
+                }
+                ctrl.selectedTags = tags;
+            };
+
+            ctrl.select = function (tag) {
+                var tagIdx = ctrl.selectedTags.indexOf(tag);
+                if (tagIdx > -1) {
+                    ctrl.selectedTags = ctrl.selectedTags.filter(function (item) {
+                        return item != tag;
+                    });
+                } else {
+                    ctrl.selectedTags.push(tag);
+                }
+                console.log("Tags selected:" + ctrl.selectedTags);
+            };
+
+            ctrl.sortBy = function (propertyName) {
+                ctrl.reverse = (ctrl.propertyName === propertyName) ? !ctrl.reverse : false;
+                ctrl.propertyName = propertyName;
+            };
+
+        }
+    });
+
 })();

+ 30 - 47
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/templates/ozone-config.html → hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/templates/config.html

@@ -1,5 +1,3 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <!--
     Licensed to the Apache Software Foundation (ASF) under one or more
     contributor license agreements.  See the NOTICE file distributed with
@@ -16,94 +14,79 @@
     See the License for the specific language governing permissions and
     limitations under the License.
   -->
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Ozone Config</title>
-  <meta name="viewport" content="width=device-width, initial-scale=1">
-  <link href="/static/bootstrap-3.0.2/css/bootstrap.min.css" rel="stylesheet">
-  <link rel="stylesheet" type="text/css" href="/static/css/ozone-conf.css">
-  <script src="/static/angular-1.6.4.min.js"></script>
-  <script src="/static/angular-route-1.6.4.min.js"></script>
-  <script src="/static/js/ozone-conf.js"></script>
-</head>
 
-<body class="htmlNoPages" ng-app="app" ng-controller="tagController">
-<div class="row btn-success">
-  <div class="col-md-12 text-center">
-    <h2>Ozone Configuration</h2>
-  </div>
-</div>
 <div class="row top-buffer">
-  <div class="col-md-3">
+  <div class="col-md-2">
     <input type="text" class="form-control" placeholder="Search Properties"
            name="search" ng-model="search.$">
   </div>
-  <div class="col-md-9">
+  <div class="col-md-10">
     <div class="btn-group btn-group-justified">
-      <a class="btn btn-primary" ng-click="loadAll()">All</a>
-      <a class="btn btn-primary" ng-click="filterConfig('KSM')">KSM</a>
-      <a class="btn btn-primary" ng-click="filterConfig('SCM')">SCM</a>
+      <a class="btn"
+         ng-class="$ctrl.allSelected() ? 'btn-primary' :'btn-secondary'"
+         ng-click="$ctrl.switchto('')">All
+      </a>
+      <a class="btn"
+         ng-class="$ctrl.selected('KSM') ? 'btn-primary' :'btn-secondary'"
+         ng-click="$ctrl.switchto('KSM')">KSM</a>
+      <a class="btn"
+         ng-class="$ctrl.selected('SCM') ? 'btn-primary' :'btn-secondary'"
+         ng-click="$ctrl.switchto('SCM')">SCM</a>
     </div>
   </div>
 </div>
 <div class="row">
-  <div class="col-md-3">
-    <table class="table table-striped table-condensed table-hover tagPanel">
+  <div class="col-md-2">
+
+    <table class="table table-striped table-condensed tagPanel">
       <colgroup>
-        <col class="col-md-2">
-        <col class="col-md-10">
+        <col class="col-md-12">
       </colgroup>
       <thead>
       <tr>
-        <th></th>
         <th>Tag</th>
       </tr>
       </thead>
       <tbody>
-      <tr ng-repeat="tag in tags |  filter:filterTags">
-        <td>
-          <input type="checkbox" name="{{tag}}" value="{{tag}}"
-                 ng-click="selected(tag)">
-        </td>
+      <tr ng-click="$ctrl.select(tag)"
+          ng-class="$ctrl.selected(tag) ? 'selectedtag':''"
+          ng-repeat="tag in $ctrl.tags">
         <td>{{tag}}</td>
       </tr>
       </tbody>
     </table>
   </div>
-  <div class="col-md-9">
+  <div class="col-md-10">
     <table class="table table-striped table-condensed table-hover wrap-table">
       <thead>
       <tr>
-        <th class="col-md-3">
-          <a href="#" ng-click="sortBy('name')">Property</a>
+        <th class="col-md-3" >
+          <a href="#" ng-click="$ctrl.sortBy('name')">Property</a>
           <span class="sortorder" ng-show="propertyName === 'name'"
                 ng-class="{reverse: reverse}">
 
               </span>
         </th>
-        <th class="col-md-2">
-          <a ng-click="sortBy('value')">Value</a>
+        <th class="col-md-2" style="word-wrap: break-word;">
+          <a ng-click="$ctrl.sortBy('value')">Value</a>
           <span class="sortorder" ng-show="propertyName === 'value'"
                 ng-class="{reverse: reverse}"></span>
         </th>
         <th class="col-md-7">
-          <a ng-click="sortBy('description')">Description</a>
+          <a href="#" ng-click="$ctrl.sortBy('description')">Description</a>
           <span class="sortorder" ng-show="propertyName === 'description'"
                 ng-class="{reverse: reverse}"></span>
         </th>
       </tr>
       </thead>
       <tbody>
-      <tr ng-repeat="config in configArray | filter:search | orderBy:propertyName:reverse">
-        <td>{{config.name}}</td>
-        <td>{{config.value}}</td>
-        <td>{{config.description}}</td>
+      <tr
+              ng-repeat="config in $ctrl.configs | filter:$ctrl.tagFilter | filter:search | orderBy:propertyName:reverse">
+        <td style="word-wrap: break-word;">{{config.name}}</td>
+        <td style="word-wrap: break-word;">{{config.value}}</td>
+        <td style="word-wrap: break-word;">{{config.description}}</td>
       </tr>
       </tbody>
     </table>
   </div>
 </div>
-</body>
-
-</html>

+ 26 - 5
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/templates/tools.html → hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/templates/menu.html

@@ -17,16 +17,38 @@
 <div id="navbar" class="collapse navbar-collapse">
     <ul class="nav navbar-nav" id="ui-tabs">
         <li>
-            <a class="dropdown-toggle" id="toolsMenu"
-               data-toggle="dropdown" aria-haspopup="true"
-               aria-expanded="true" href="#">
+            <a class="dropdown-toggle"
+               id="metricsMenu"
+               data-toggle="dropdown"
+               aria-haspopup="true"
+               aria-expanded="true">
+                Metrics
+                <span class="caret"></span>
+            </a>
+            <ul
+                class="dropdown-menu"
+                aria-labelledby="metricsMenu">
+                <li ng-repeat="(name, url) in $ctrl.metrics">
+                    <a ng-href="{{url}}">{{name}}<span
+                        aria-hidden="true"></span></a></li>
+            </ul>
+        </li>
+        <li><a href="/#!/config">Configuration</a></li>
+        <li ng-show="$ctrl.docs"><a href="/docs">Documentation</a></li>
+        <li>
+            <a class="dropdown-toggle"
+               id="toolsMenu"
+               data-toggle="dropdown"
+               aria-haspopup="true"
+               aria-expanded="true"
+               >
                 Common tools
                 <span class="caret"></span>
             </a>
             <ul class="dropdown-menu" aria-labelledby="toolsMenu">
                 <li><a href="/jmx">JMX <span
                         aria-hidden="true"></span></a></li>
-                <li><a href="/static/templates/ozone-config.html">Config <span
+                <li><a href="/conf">Config <span
                         aria-hidden="true"></a></li>
                 <li><a href="/stacks">Stacks <span
                         aria-hidden="true"></a></li>
@@ -34,6 +56,5 @@
                         aria-hidden="true"></a></li>
             </ul>
         </li>
-        <li ng-show="$ctrl.docs"><a href="/docs">Documentation</a></li>
     </ul>
 </div><!--/.nav-collapse -->

+ 3 - 2
hadoop-hdfs-project/hadoop-hdfs/src/main/webapps/static/templates/rpc-metrics.html

@@ -25,8 +25,9 @@
     <p>Quantiles based on a fixed {{window}} window. Calculated once at every
         {{window}}</p>
 
-    <div class="container">
-        <div class="col-md-6" ng-repeat="(metric,percentiles) in windowed">
+    <div class="row">
+        <div class="col-md-6 col-lg-4"
+             ng-repeat="(metric,percentiles) in windowed">
             <h3>{{metric}}</h3>
             <p>{{percentiles.numOps}} sample</p>
             <nvd3 options="$ctrl.percentileGraphOptions"