Skip to content

Commit 18fbdb5

Browse files
MB-12222: logs section
Change-Id: I8fd4daaa6c05a40baa281140401447c2ba2c1db2 Reviewed-on: http://review.couchbase.org/46632 Reviewed-by: Pavel Blagodov <[email protected]> Tested-by: Pavel Blagodov <[email protected]>
1 parent 0a2b40a commit 18fbdb5

19 files changed

+422
-29
lines changed

priv/public/angular/app/app.css

+18-23
Original file line numberDiff line numberDiff line change
@@ -735,19 +735,18 @@ body.init_bucket_dialog #init_bucket_dialog { display: block; }
735735

736736
.btn_x {margin: 4px 0 0;width:25px;height:24px;background:url(/images/btn_x.png) no-repeat}
737737

738-
.server_row .dynamic_healthy,
739-
.server_row .dynamic_unhealthy,
740-
.server_row .dynamic_inactiveFailed,
741-
.server_row .dynamic_warmup,
742-
.server_row .pending_add {width: 65px; padding: 0 !important; background-image: url("/images/server_status_icons_solid_bg-v2.png"); background-repeat: no-repeat;}
743-
.server_row .dynamic_healthy {background-position: 0 19px;}
744-
.server_row .dynamic_unhealthy,
745-
.server_row .dynamic_unhealthy.dynamic_inactiveFailed {background-position: 0 -160px;}
746-
.server_row .dynamic_inactiveFailed,
747-
.server_row .dynamic_warmup,
748-
.server_row .pending_add {background-position: 0 -310px;}
749-
.server_row.dynamic_inactiveFailed .dynamic_inactiveFailed {background-position: 0 -303px;}
750-
.server_row.dynamic_inactiveFailed .dynamic_unhealthy.dynamic_inactiveFailed {background-position: 0 -154px;}
738+
.dynamic_healthy,
739+
.dynamic_unhealthy,
740+
.dynamic_inactiveFailed,
741+
.dynamic_warmup,
742+
.pending_add {width: 65px; padding: 0 !important; background-image: url("/images/server_status_icons_solid_bg-v2.png"); background-repeat: no-repeat;}
743+
.dynamic_healthy {background-position: 0 19px;}
744+
.dynamic_unhealthy {background-position: 0 -160px;}
745+
.dynamic_inactiveFailed,
746+
.dynamic_warmup,
747+
.pending_add {background-position: 0 -310px;}
748+
.dynamic_inactiveFailed {background-position: 0 -303px;}
749+
.dynamic_unhealthy.dynamic_inactiveFailed {background-position: 0 -154px;}
751750

752751
.servers h2 { height: 25px;}
753752
.servers_list ul {*margin:-1px 0 0;*position:relative;*top:1px;}
@@ -831,7 +830,7 @@ body.init_bucket_dialog #init_bucket_dialog { display: block; }
831830
.add_back_btn span {display: block; padding-top: 2px; padding-left: 31px; font-size: 13px; font-weight: bold; color: #fff; line-height: 29px; background: url('/images/green_btn_plus_icon.png') no-repeat 11px 50%;}
832831

833832
.list {border-right:1px solid #d9dce0;background:none #eee;}
834-
.list td {background:none #eee;border-bottom:1px solid #ced5d9; padding:10px;border-right:0px; text-align:center;vertical-align:middle;}
833+
.list td {border-bottom:1px solid #ced5d9; padding:10px;border-right:0px; text-align:center;vertical-align:middle;}
835834
.align_text td {text-align:left}
836835

837836
.buckets .properties_table td {color:#404042}
@@ -1417,16 +1416,10 @@ input[type=number].size {width:4em;}
14171416
.collect_information_dialog h2 {margin-top: 15px;clear:both;font-size:20px;font-weight:normal;padding-bottom:6px;margin-bottom:10px;border-bottom:1px solid #d4d9dc;}
14181417
.collect_information_dialog .collect_from {padding-left: 20px; padding-top: 5px; line-height: 30px;}
14191418
.collect_information_dialog .collect_from input {vertical-align: middle; margin-right: 5px;}
1420-
.collect_information_dialog .collect_from label {vertical-align: middle;}
1419+
.collect_information_dialog .collect_from label {vertical-align: middle; display: inline-block; width: 170px;}
1420+
.collect_information_dialog .selected_nodes .icon {display: inline-block; height: 58px; vertical-align: middle; background-image: url("/images/server_status_icons_solid_bg-v2.png"); background-repeat: no-repeat; margin: -14px 0;}
14211421
.collect_information_dialog .selected_nodes {padding: 9px 0 9px 60px; margin: 0; display: inline-block; background: no-repeat url('/images/server_icon.png') 0 50%; }
1422-
.collect_information_dialog .selected_nodes li {background-image: url("/images/server_status_icons_solid_bg-v2.png"); background-repeat: no-repeat; padding-right: 150px;}
1423-
.collect_information_dialog .dynamic_server_up {background-position: 100% 8px;}
1424-
.collect_information_dialog .dynamic_server_down,
1425-
.collect_information_dialog .dynamic_server_down.dynamic_failed_over {background-position: 100% -170px;}
1426-
.collect_information_dialog .dynamic_server_warmup,
1427-
.collect_information_dialog .dynamic_pending_add {background-position: 100% -320px;}
1428-
.collect_information_dialog .dynamic_failed_over {background-position: 100% -534px;}
1429-
.collect_information_dialog .dynamic_server_down.dynamic_failed_over {background-position: 100% -170px;}
1422+
.collect_information_dialog .selected_nodes li { padding-right: 150px; width: 300px;}
14301423
.collect_information_dialog .save_cancel button {color: #fff;}
14311424
.collect_information_dialog .js_error_container {display: none; color: red; display: inline-block; margin-left: 10px;}
14321425
.collect_information_dialog .dynamic_input_error {border-color: red;}
@@ -1458,3 +1451,5 @@ input[type=number].size {width:4em;}
14581451
.for_upload label {display: inline-block; width: 200px;}
14591452

14601453
.global_alerts_container {width: 1060px; margin: 0 auto;}
1454+
1455+
#js_collect_information .controls {text-align: right; height: 30px; padding-right: 10px; margin-top: -40px; padding-bottom: 10px; margin-left: 300px;}

priv/public/angular/app/app.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ angular.module('mnXDCRService', [
157157
'mnTasksDetails'
158158
]);
159159

160+
angular.module('mnLogs', [
161+
'mnLogsService'
162+
]);
163+
angular.module('mnLogsCollectInfoService', [
164+
'mnHttp'
165+
]);
166+
angular.module('mnLogsService', [
167+
'mnHttp'
168+
]);
169+
160170

161171
angular.module('app', [
162172
'mnFilters',
@@ -191,7 +201,9 @@ angular.module('app', [
191201
'mnBucketsDetailsDialogService',
192202
'mnViews',
193203
'mnXDCR',
194-
'mnXDCRService'
204+
'mnXDCRService',
205+
'mnLogs',
206+
'mnLogsCollectInfoService'
195207

196208

197209
]).run(function ($rootScope, $state, $urlRouter, mnPools) {

priv/public/angular/app/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@
7070
<script src="mn_admin/mn_xdcr/delete_dialog/mn_xdcr_delete_dialog_controller.js"></script>
7171
<script src="mn_admin/mn_xdcr/edit_dialog/mn_xdcr_edit_dialog_controller.js"></script>
7272

73+
<script src="mn_admin/mn_logs/mn_logs_service.js"></script>
74+
<script src="mn_admin/mn_logs/mn_logs_controller.js"></script>
75+
<script src="mn_admin/mn_logs/collect_info/mn_logs_collect_info_service.js"></script>
76+
<script src="mn_admin/mn_logs/collect_info/mn_logs_collect_info_controller.js"></script>
77+
<script src="mn_admin/mn_logs/list/mn_logs_list_controller.js"></script>
78+
7379
<script src="mn_wizard/mn_wizard_config.js"></script>
7480

7581
<script src="mn_wizard/step1/mn_wizard_step1_controller.js"></script>

priv/public/angular/app/mn_admin/mn_admin.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
<li class="line" ui-sref-active="currentNav">
4949
<a class="switch_replications" ui-sref="app.admin.replications">XDCR</a>
5050
</li>
51-
<li class="line" data-section="log">
52-
<a class="switch_log">Log</a>
51+
<li class="line" ng-class="{currentNav: ('app.admin.logs' | includedByState)}">
52+
<a class="switch_log" ui-sref="app.admin.logs.list">Log</a>
5353
</li>
5454
<li class="line" ui-sref-active="currentNav" id="nav_settings_container">
5555
<a href="#?section=settings" ui-sref="app.admin.settings.cluster" class="switch_settings">Settings</a>

priv/public/angular/app/mn_admin/mn_admin_config.js

+34
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,40 @@ angular.module('mnAdmin').config(function ($stateProvider, $urlRouterProvider) {
9090
}
9191
}
9292
})
93+
.state('app.admin.logs', {
94+
url: '/logs',
95+
abstract: true,
96+
templateUrl: 'mn_admin/mn_logs/mn_logs.html'
97+
})
98+
.state('app.admin.logs.list', {
99+
url: '',
100+
controller: 'mnLogsListController',
101+
templateUrl: 'mn_admin/mn_logs/list/mn_logs_list.html',
102+
resolve: {
103+
logs: function (mnLogsService) {
104+
return mnLogsService.getLogs();
105+
}
106+
}
107+
})
108+
.state('app.admin.logs.collectInfo', {
109+
url: '/collectInfo',
110+
abstract: true,
111+
controller: 'mnLogsCollectInfoController',
112+
templateUrl: 'mn_admin/mn_logs/collect_info/mn_logs_collect_info.html',
113+
resolve: {
114+
state: function (mnLogsCollectInfoService) {
115+
return mnLogsCollectInfoService.getState();
116+
}
117+
}
118+
})
119+
.state('app.admin.logs.collectInfo.result', {
120+
url: '/result',
121+
templateUrl: 'mn_admin/mn_logs/collect_info/mn_logs_collect_info_result.html'
122+
})
123+
.state('app.admin.logs.collectInfo.form', {
124+
url: '/form',
125+
templateUrl: 'mn_admin/mn_logs/collect_info/mn_logs_collect_info_form.html'
126+
})
93127
.state('app.admin.settings', {
94128
url: '/settings',
95129
abstract: true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<div id="js_collect_information" class="when-roadmin-avoid-me">
2+
<div class="controls">
3+
<a id="js_start_new_info" class="btn_1" ng-show="!('app.admin.logs.collectInfo.form' | isState)" ui-sref="app.admin.logs.collectInfo.form"><span>Start New Collection</span></a>
4+
<a id="js_cancel_collect_info" class="btn_1" ng-show="state.status === 'running'" ng-click="stopCollection()" ng-disabled="disabledStopCollect"><span>Stop Collection</span></a>
5+
<a id="js_previous_result_btn" class="btn_1" ng-show="state.status !== 'idle' && !('app.admin.logs.collectInfo.result' | isState)" ui-sref="app.admin.logs.collectInfo.result"><span>Show Collection Progress</span></a>
6+
</div>
7+
<div ui-view></div>
8+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
angular.module('mnLogs').controller('mnLogsCollectInfoController',
2+
function ($scope, mnHelper, mnLogsCollectInfoService, state, $state, $modal) {
3+
function applyState(state) {
4+
$scope.loadingResult = false;
5+
$scope.state = state;
6+
}
7+
$scope.stopCollection = function () {
8+
$modal.open({
9+
templateUrl: '/angular/app/mn_admin/mn_logs/collect_info/mn_logs_collect_info_stop_dialog.html'
10+
}).result.then(function () {
11+
$scope.disabledStopCollect = true;
12+
mnLogsCollectInfoService.cancelLogsCollection()['finally'](function () {
13+
$scope.disabledStopCollect = false;
14+
})
15+
});
16+
};
17+
$scope.collect = {
18+
nodes: {},
19+
from: '*'
20+
// uploadHost: 's3.amazonaws.com/cb-customers'
21+
};
22+
$scope.submit = function () {
23+
var collect = _.clone($scope.collect);
24+
collect.nodes = !collect.from ? mnHelper.checkboxesToList(collect.nodes).join(',') : '*';
25+
if (!collect.upload) {
26+
delete collect.uploadHost;
27+
delete collect.customer;
28+
delete collect.ticket;
29+
}
30+
var promise = mnLogsCollectInfoService.startLogsCollection(collect);
31+
mnHelper
32+
.promiseHelper($scope, promise)
33+
.showSpinner()
34+
.catchErrors()
35+
.reloadState()
36+
.getPromise()
37+
.then(function () {
38+
$scope.loadingResult = true;
39+
$state.go('app.admin.logs.collectInfo.result');
40+
});
41+
};
42+
applyState(state);
43+
44+
mnHelper.setupLongPolling({
45+
methodToCall: mnLogsCollectInfoService.getState,
46+
scope: $scope,
47+
onUpdate: applyState
48+
});
49+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<div class="collect_information_dialog" id="js_collect_info_start_new_view" mn-spinner="viewLoading">
2+
<form id="js_collect_info_form" ng-submit="submit()" name="collectForm">
3+
<div class="content">
4+
<h2>Select Nodes</h2>
5+
<p>Collect logs and diagnostic information from:</p>
6+
<ul class="collect_from">
7+
<li>
8+
<input type="radio" name="from" value="*" ng-model="collect.from" id="all_nodes_radio" checked="checked">
9+
<label for="all_nodes_radio">All nodes</label>
10+
</li>
11+
<li>
12+
<input type="radio" name="from" value="" ng-model="collect.from" id="selected_nodes_radio">
13+
<label for="selected_nodes_radio">Selected nodes:</label>
14+
</li>
15+
<li>
16+
<ul id="js_select_nodes_list_container" class="selected_nodes">
17+
<li ng-repeat="node in state.nodes track by node.otpNode">
18+
<input type="checkbox"
19+
name="js-selected-nodes"
20+
ng-model="collect.nodes[node.otpNode]"
21+
ng-init="collect.nodes[node.otpNode] = true"
22+
value="{{node.otpNode}}"
23+
id="collected_node_list_{{node.otpNode | mnMakeSafeForCSS }}"
24+
ng-checked="node.status !== 'unhealthy'"
25+
ng-disabled="node.status === 'unhealthy' || collect.from">
26+
<label for="collected_node_list_{{node.otpNode | mnMakeSafeForCSS }}">{{ node.hostname | mnStripPortHTML:state.nodes }}</label>
27+
<span class="icon {{'dynamic_' + node.status + ' dynamic_' + node.clusterMembership}}"></span>
28+
</li>
29+
</ul>
30+
<div id="js_nodes_error" class="js_error_container" style="display:block"></div>
31+
</li>
32+
</ul>
33+
<h2>Upload Options</h2>
34+
<div class="for_upload" id="js_upload_conf">
35+
<div id="js_generalCollectInfo_error" class="js_error_container" style="display:block;margin:0 0 10px 0; line-height: 13px;" ng-show="errors._">{{errors._}}</div>
36+
<div>
37+
<label for="js_upload_to_cb">Upload to Couchbase:</label>
38+
<input id="js_upload_to_cb" name="upload" ng-model="collect.upload" value="true" type="checkbox">
39+
<div id="js_upload_error" class="js_error_container" ng-show="errors.upload">{{errors.upload}}</div>
40+
</div>
41+
<div>
42+
<label for="js_uploadHost_input">Upload to host:</label>
43+
<input id="js_uploadHost_input" name="uploadHost" ng-model="collect.uploadHost" type="text" ng-disabled="!collect.upload" required>
44+
<div id="js_uploadHost_error" class="js_error_container" ng-show="errors.uploadHost">{{errors.uploadHost}}</div>
45+
<div class="js_error_container" ng-show="collect.upload && collectForm.uploadHost.$error.required">upload host field must be given if upload is selected</div>
46+
</div>
47+
<div>
48+
<label for="js_customer_input">Customer name:</label>
49+
<input id="js_customer_input" name="customer" ng-model="collect.customer" type="text" ng-disabled="!collect.upload" required>
50+
<div id="js_customer_error" class="js_error_container" ng-show="errors.customer">{{errors.customer}}</div>
51+
<div class="js_error_container" ng-show="collect.upload && collectForm.customer.$error.required">customer field must be given if upload is selected</div>
52+
</div>
53+
<div>
54+
<label for="js_ticket_input">Ticket Number (optional):</label>
55+
<input id="js_ticket_input" name="ticket" ng-model="collect.ticket" type="text" ng-disabled="!collect.upload">
56+
<div id="js_ticket_error" class="js_error_container" ng-show="errors.ticket">{{errors.ticket}}</div>
57+
</div>
58+
</div>
59+
</div>
60+
<div class="save_cancel">
61+
<button class="save_button float_right js_save_button" type="submit" ng-disabled="collect.nodes.length">Collect</button>
62+
</div>
63+
</form>
64+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<div class="collect_result_dialog collect_information_dialog" id="js_collect_result_view" mn-spinner="loadingResult">
2+
<div class="content" id="js_collect_progress_container">
3+
<div id="js_collection_status" class="collection_status dynamic_{{state.status}}">
4+
<span>Collection {{state.status}} {{state.status === 'running' ? '...' : '.'}}</span>
5+
</div>
6+
<div ng-if="state.nodesByStatus.started">
7+
<p>In progress:</p>
8+
<div class="js_result result">
9+
<div ng-repeat="node in state.nodesByStatus.started">{{node.nodeName}}</div>
10+
</div>
11+
</div>
12+
<div ng-if="state.nodesByStatus.starting">
13+
<p>Pending:</p>
14+
<div class="js_result result">
15+
<div ng-repeat="node in state.nodesByStatus.starting">{{node.nodeName}}</div>
16+
</div>
17+
</div>
18+
<div ng-if="state.nodesByStatus.collected">
19+
<p class="green">Logs were successfully collected to the following paths:</p>
20+
<div class="js_result result">
21+
<div ng-repeat="node in state.nodesByStatus.collected"><strong>{{node.nodeName}}</strong> {{node.path}}</div>
22+
</div>
23+
</div>
24+
<div ng-if="state.nodesByStatus.startedUpload">
25+
<p class="green">Logs are being uploaded from this paths:</p>
26+
<div class="js_result result">
27+
<div ng-repeat="node in state.nodesByStatus.startedUpload"><strong>{{node.nodeName}}</strong> {{node.path}}</div>
28+
</div>
29+
</div>
30+
<div ng-if="state.nodesByStatus.startingUpload">
31+
<p class="green">Logs are pending upload from this paths:</p>
32+
<div class="js_result result">
33+
<div ng-repeat="node in state.nodesByStatus.startingUpload"><strong>{{node.nodeName}}</strong> {{node.path}}</div>
34+
</div>
35+
</div>
36+
<div ng-if="state.nodesByStatus.uploaded">
37+
<p class="green">Logs were successfully uploaded to the following URLs:</p>
38+
<div class="js_result result">
39+
<div ng-repeat="node in state.nodesByStatus.uploaded"><a href="{{node.url}}" target="_blank">{{node.url}}</a></div>
40+
</div>
41+
</div>
42+
<div ng-if="state.nodesByStatus.failedUpload">
43+
<p class="red">Warning: The following logs were successfully collected but failed to upload. Please manually upload from the following locations:</p>
44+
<div class="js_result result">
45+
<div ng-repeat="node in state.nodesByStatus.failedUpload"><strong>{{node.nodeName}}</strong> {{node.path}}</div>
46+
</div>
47+
</div>
48+
<div ng-if="state.nodesByStatus.failed">
49+
<p class="red">Error: Unable to collect logs from the following nodes:</p>
50+
<div class="js_result result">
51+
<div ng-repeat="node in state.nodesByStatus.failed">{{node.nodeName}}</div>
52+
</div>
53+
</div>
54+
<div ng-if="state.nodesByStatus.cancelled">
55+
<p class="red">Cancelled Nodes:</p>
56+
<div class="js_result result">
57+
<div ng-repeat="node in state.nodesByStatus.cancelled">{{node.nodeName}}</div>
58+
</div>
59+
</div>
60+
<div ng-if="state.nodeErrors.length">
61+
<p class="red">Node Errors:</p>
62+
<div class="js_result result">
63+
<div ng-repeat="node in state.nodeErrors"><strong>{{node.nodeName}}</strong> {{node.error}}</div></div>
64+
</div>
65+
</div>
66+
</div>
67+
</div>

0 commit comments

Comments
 (0)