diff --git a/extensions/big-data-cluster/package.nls.json b/extensions/big-data-cluster/package.nls.json
index 2106fe603b..f69a8e2d89 100644
--- a/extensions/big-data-cluster/package.nls.json
+++ b/extensions/big-data-cluster/package.nls.json
@@ -4,5 +4,6 @@
"text.sqlServerBigDataClustersEnabledConfig.description":"Whether the SQL Server big data clusters data explorer view is enabled",
"command.addController.title": "Connect to Controller",
"command.deleteController.title" : "Delete",
- "command.refreshController.title" : "Refresh"
+ "command.refreshController.title" : "Refresh",
+ "command.manageController.title" : "Manage",
}
diff --git a/extensions/big-data-cluster/resources/dark/copy_inverse.svg b/extensions/big-data-cluster/resources/dark/copy_inverse.svg
new file mode 100644
index 0000000000..fa65571127
--- /dev/null
+++ b/extensions/big-data-cluster/resources/dark/copy_inverse.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extensions/big-data-cluster/resources/light/copy.svg b/extensions/big-data-cluster/resources/light/copy.svg
new file mode 100644
index 0000000000..91692de258
--- /dev/null
+++ b/extensions/big-data-cluster/resources/light/copy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts b/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts
index 3fedafb0fd..b959384122 100644
--- a/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts
+++ b/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts
@@ -4,8 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import * as request from 'request';
-import { ClusterRouterApi, Authentication } from './apiGenerated';
+import { ClusterRouterApi, Authentication, DefaultApi, EndpointModel } from './apiGenerated';
+import * as nls from 'vscode-nls';
+const localize = nls.loadMessageBundle();
class AuthConfiguration implements Authentication {
public username: string = '';
@@ -43,40 +45,47 @@ export async function getEndPoints(
url = adjustUrl(url);
let endPointApi = new ClusterApiWrapper(username, password, url, !!ignoreSslVerification);
- let controllerResponse: IEndPointsResponse = undefined;
- let controllerError: IControllerError = undefined;
- let request = {
- url: url,
- username: username,
- password: password,
- method: 'endPointsGet'
- };
-
try {
let result = await endPointApi.endpointsGet(clusterName);
- controllerResponse = {
+ return {
response: result.response as IHttpResponse,
- endPoints: result.body as IEndPoint[],
- request
+ endPoints: result.body as EndpointModel[]
};
- return controllerResponse;
} catch (error) {
- if ('response' in error) {
- let err: IEndPointsResponse = error as IEndPointsResponse;
- let errCode = `${err.response.statusCode || ''}`;
- let errMessage = err.response.statusMessage;
- let errUrl = err.response.url;
- controllerError = {
- address: errUrl,
- code: errCode,
- errno: errCode,
- message: errMessage,
- name: undefined
- };
- } else {
- controllerError = error as IControllerError;
- }
- throw Object.assign(controllerError, { request }) as IControllerError;
+ throw new ControllerError(error, localize('bdc.error.getEndPoints', "Error retrieving endpoints from {0}", url));
+ }
+}
+
+class DefaultApiWrapper extends DefaultApi {
+ constructor(basePathOrUsername: string, password?: string, basePath?: string, ignoreSslVerification?: boolean) {
+ super(basePathOrUsername, password, basePath);
+ this.authentications.default = new AuthConfiguration(!!ignoreSslVerification);
+ }
+}
+
+export async function getClusterStatus(
+ clusterName: string,
+ url: string,
+ username: string,
+ password: string,
+ ignoreSslVerification?: boolean
+): Promise {
+
+ if (!url) {
+ return undefined;
+ }
+
+ url = adjustUrl(url);
+ const defaultApi = new DefaultApiWrapper(username, password, url, ignoreSslVerification);
+
+ try {
+ const clusterStatus = await defaultApi.getClusterStatus('', '', clusterName);
+ return {
+ response: clusterStatus.response,
+ clusterStatus: clusterStatus.body
+ };
+ } catch (error) {
+ throw new ControllerError(error, localize('bdc.error.getClusterStatus', "Error retrieving cluster status from {0}", url));
}
}
@@ -95,7 +104,7 @@ function adjustUrl(url: string): string {
return url;
}
-export interface IEndPointsRequest {
+export interface IClusterRequest {
url: string;
username: string;
password?: string;
@@ -104,7 +113,12 @@ export interface IEndPointsRequest {
export interface IEndPointsResponse {
response: IHttpResponse;
- endPoints: IEndPoint[];
+ endPoints: EndpointModel[];
+}
+
+export interface IClusterStatusResponse {
+ response: IHttpResponse;
+ clusterStatus: IBdcStatus;
}
export interface IHttpResponse {
@@ -114,17 +128,65 @@ export interface IHttpResponse {
statusMessage?: string;
}
-export interface IEndPoint {
- name?: string;
- description?: string;
- endpoint?: string;
- ip?: string;
- port?: number;
+export interface IBdcStatus {
+ name: string;
+ status: IStatus;
+ services: IServiceStatus[];
}
-export interface IControllerError extends Error {
- code?: string;
- errno?: string;
- message: string;
- request?: any;
+export interface IServiceStatus {
+ serviceName: string;
+ status: IStatus;
+ resources: IResourceStatus[];
+}
+
+export interface IResourceStatus {
+ resourceName: string;
+ status: IStatus;
+ instances?: IInstanceStatus[];
+}
+
+export interface IInstanceStatus {
+ instanceName: string;
+ status: IStatus;
+ dashboards: IDashboard[];
+}
+
+export interface IDashboard {
+ nodeMetricsUrl: string;
+ sqlMetricsUrl: string;
+ logsUrl: string;
+}
+
+export interface IStatus {
+ state: string;
+ healthStatus: string;
+ details?: string;
+}
+
+export class ControllerError extends Error {
+ public code?: string;
+ public errno?: string;
+ public reason?: string;
+ public address?: string;
+
+ /**
+ *
+ * @param error The original error to wrap
+ * @param messagePrefix Optional text to prefix the error message with
+ */
+ constructor(error: any, messagePrefix?: string) {
+ super(messagePrefix);
+ // Pull out the response information containing details about the failure
+ if (error.response) {
+ this.code = error.response.statusCode || '';
+ this.message += `${error.response.statusMessage ? ` - ${error.response.statusMessage}` : ''}` || '';
+ this.address = error.response.url || '';
+ }
+
+ // The body message contains more specific information about the failure
+ if (error.body && error.body.reason) {
+ this.message += ` - ${error.body.reason}`;
+ }
+ }
}
diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts
index 16421ebefc..e4cc3a7f8f 100644
--- a/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts
+++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts
@@ -7,10 +7,11 @@
import * as azdata from 'azdata';
import * as nls from 'vscode-nls';
-import { IEndPoint, IControllerError, getEndPoints } from '../controller/clusterControllerApi';
+import { ControllerError, getEndPoints } from '../controller/clusterControllerApi';
import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider';
import { TreeNode } from '../tree/treeNode';
import { showErrorMessage } from '../utils';
+import { EndpointModel } from '../controller/apiGenerated';
const localize = nls.loadMessageBundle();
@@ -39,7 +40,7 @@ export class AddControllerDialogModel {
// We pre-fetch the endpoints here to verify that the information entered is correct (the user is able to connect)
let response = await getEndPoints(clusterName, url, username, password, true);
if (response && response.endPoints) {
- let masterInstance: IEndPoint = undefined;
+ let masterInstance: EndpointModel = undefined;
if (response.endPoints) {
masterInstance = response.endPoints.find(e => e.name && e.name === 'sql-server-master');
}
@@ -55,10 +56,9 @@ export class AddControllerDialogModel {
throw error;
}
}
-
}
- public async onError(error: IControllerError): Promise {
+ public async onError(error: ControllerError): Promise {
// implement
}
@@ -171,7 +171,7 @@ export class AddControllerDialog {
} catch (error) {
showErrorMessage(error);
if (this.model && this.model.onError) {
- await this.model.onError(error as IControllerError);
+ await this.model.onError(error as ControllerError);
}
return false;
}
diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts
new file mode 100644
index 0000000000..d76d32ee43
--- /dev/null
+++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts
@@ -0,0 +1,223 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import * as vscode from 'vscode';
+import * as azdata from 'azdata';
+import * as nls from 'vscode-nls';
+import { BdcDashboardModel } from './bdcDashboardModel';
+
+const localize = nls.loadMessageBundle();
+
+export class BdcDashboard {
+
+ private dashboard: azdata.workspace.ModelViewEditor;
+
+ private copyIconPath: { light: string, dark: string };
+ private refreshIconPath: { light: string, dark: string };
+
+ constructor(private title: string, private model: BdcDashboardModel, context: vscode.ExtensionContext) {
+ this.copyIconPath = {
+ light: context.asAbsolutePath('resources/light/copy.svg'),
+ dark: context.asAbsolutePath('resources/dark/copy_inverse.svg')
+ };
+
+ this.refreshIconPath = {
+ light: context.asAbsolutePath('resources/light/refresh.svg'),
+ dark: context.asAbsolutePath('resources/dark/refresh_inverse.svg')
+ };
+ }
+
+ public showDashboard(): void {
+ this.createDashboard();
+ this.dashboard.openEditor();
+ }
+
+ private createDashboard(): void {
+ this.dashboard = azdata.workspace.createModelViewEditor(this.title, { retainContextWhenHidden: true, supportsSave: false });
+ this.dashboard.registerContent(async (view: azdata.ModelView) => {
+
+ const dashboardRootContainer = view.modelBuilder.flexContainer().withLayout(
+ {
+ flexFlow: 'column',
+ width: '100%',
+ height: '100%',
+ alignItems: 'left'
+ }).component();
+
+ // ###########
+ // # TOOLBAR #
+ // ###########
+
+ // Refresh button
+ const refreshButton = view.modelBuilder.button()
+ .withProperties({
+ label: localize('bdc.dashboard.refreshButton', "Refresh"),
+ iconPath: this.refreshIconPath,
+ height: '50'
+ }).component();
+
+ refreshButton.onDidClick(() => this.model.refresh());
+
+ const toolbarContainer = view.modelBuilder.toolbarContainer().withToolbarItems([{ component: refreshButton }]).component();
+
+ dashboardRootContainer.addItem(toolbarContainer);
+
+ // ################
+ // # CONTENT AREA #
+ // ################
+
+ const contentContainer = view.modelBuilder.flexContainer().withLayout(
+ {
+ flexFlow: 'column',
+ width: '100%',
+ height: '100%',
+ alignItems: 'left'
+ }).component();
+
+ dashboardRootContainer.addItem(contentContainer, { flex: '0 0 100%' });
+
+ // ##############
+ // # PROPERTIES #
+ // ##############
+
+ const propertiesLabel = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.propertiesHeader', "Cluster Properties") }).component();
+ contentContainer.addItem(propertiesLabel, { CSSStyles: { 'margin-top': '15px', 'font-size': '20px', 'font-weight': 'bold', 'padding-left': '10px' } });
+
+ // Row 1
+ const row1 = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '30px', alignItems: 'center' }).component();
+ // Cluster Name
+ const clusterNameLabel = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.clusterName', "Cluster Name :") }).component();
+ const clusterNameValue = view.modelBuilder.text().withProperties({ value: this.model.clusterName }).component();
+ row1.addItem(clusterNameLabel, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+ row1.addItem(clusterNameValue, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+
+ contentContainer.addItem(row1, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
+
+ // Row 2
+ const row2 = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '30px', alignItems: 'center' }).component();
+
+ // Cluster State
+ const clusterStateLabel = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.clusterState', "Cluster State :") }).component();
+ const clusterStateValue = view.modelBuilder.text().withProperties({ value: this.model.clusterStatus.state }).component();
+ row2.addItem(clusterStateLabel, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+ row2.addItem(clusterStateValue, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+
+ // Health Status
+ const healthStatusLabel = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.healthStatus', "Health Status :") }).component();
+ const healthStatusValue = view.modelBuilder.text().withProperties({ value: this.model.clusterStatus.healthStatus }).component();
+ row2.addItem(healthStatusLabel, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+ row2.addItem(healthStatusValue, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+
+ contentContainer.addItem(row2, { CSSStyles: { 'padding-left': '10px', 'border-bottom': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
+
+ // ############
+ // # OVERVIEW #
+ // ############
+
+ const overviewLabel = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.overviewHeader', "Cluster Overview") }).component();
+ contentContainer.addItem(overviewLabel, { CSSStyles: { 'margin-top': '15px', 'font-size': '20px', 'font-weight': 'bold', 'padding-left': '10px' } });
+
+ const overviewContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component();
+
+ // Service Status header row
+ const serviceStatusHeaderRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
+ const nameCell = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.serviceNameHeader', "Service Name") }).component();
+ serviceStatusHeaderRow.addItem(nameCell, { CSSStyles: { 'width': '25%', 'font-weight': 'bold', 'user-select': 'text' } });
+ const stateCell = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.stateHeader', "State") }).component();
+ serviceStatusHeaderRow.addItem(stateCell, { CSSStyles: { 'width': '15%', 'font-weight': 'bold', 'user-select': 'text' } });
+ const healthStatusCell = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.healthStatusHeader', "Health Status") }).component();
+ serviceStatusHeaderRow.addItem(healthStatusCell, { CSSStyles: { 'width': '15%', 'font-weight': 'bold', 'user-select': 'text' } });
+ overviewContainer.addItem(serviceStatusHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } });
+
+ // Service Status rows
+ this.model.serviceStatus.forEach(serviceStatus => {
+ const serviceStatusRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center', height: '20px' }).component();
+ const nameCell = view.modelBuilder.text().withProperties({ value: serviceStatus.serviceName }).component();
+ serviceStatusRow.addItem(nameCell, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+ const stateCell = view.modelBuilder.text().withProperties({ value: serviceStatus.status.state }).component();
+ serviceStatusRow.addItem(stateCell, { CSSStyles: { 'width': '15%', 'user-select': 'text' } });
+ const healthStatusCell = view.modelBuilder.text().withProperties({ value: serviceStatus.status.healthStatus }).component();
+ serviceStatusRow.addItem(healthStatusCell, { CSSStyles: { 'width': '15%', 'user-select': 'text' } });
+
+ overviewContainer.addItem(serviceStatusRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
+ });
+
+ contentContainer.addItem(overviewContainer);
+
+ // #####################
+ // # SERVICE ENDPOINTS #
+ // #####################
+
+ const endpointsLabel = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.endpointsLabel', "Service Endpoints") }).component();
+ contentContainer.addItem(endpointsLabel, { CSSStyles: { 'font-size': '20px', 'font-weight': 'bold', 'padding-left': '10px' } });
+
+ const endpointsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component();
+
+ // Service endpoints header row
+ const endpointsHeaderRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
+ const endpointsServiceNameHeaderCell = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.serviceHeader', "Service") }).component();
+ endpointsHeaderRow.addItem(endpointsServiceNameHeaderCell, { CSSStyles: { 'width': '25%', 'font-weight': 'bold', 'user-select': 'text' } });
+ const endpointsEndpointHeaderCell = view.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.endpointHeader', "Endpoint") }).component();
+ endpointsHeaderRow.addItem(endpointsEndpointHeaderCell, { CSSStyles: { 'width': '15%', 'font-weight': 'bold', 'user-select': 'text' } });
+ endpointsContainer.addItem(endpointsHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } });
+
+ // Service endpoints rows
+ this.model.serviceEndpoints.forEach(endpointInfo => {
+ const endPointRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center', height: '20px' }).component();
+ const nameCell = view.modelBuilder.text().withProperties({ value: getFriendlyEndpointNames(endpointInfo.serviceName) }).component();
+ endPointRow.addItem(nameCell, { CSSStyles: { 'width': '25%', 'user-select': 'text' } });
+ if (endpointInfo.isHyperlink) {
+ const linkCell = view.modelBuilder.hyperlink().withProperties({ label: endpointInfo.hyperlink, url: endpointInfo.hyperlink }).component();
+ endPointRow.addItem(linkCell, { CSSStyles: { 'width': '35%', 'color': '#0078d4', 'text-decoration': 'underline', 'overflow': 'hidden' } });
+ }
+ else {
+ const endpointCell = view.modelBuilder.text().withProperties({ value: endpointInfo.ipAddress + ':' + endpointInfo.port }).component();
+ endPointRow.addItem(endpointCell, { CSSStyles: { 'width': '35%', 'user-select': 'text', 'overflow': 'hidden' } });
+ }
+ const copyValueCell = view.modelBuilder.button().component();
+ copyValueCell.iconPath = this.copyIconPath;
+ copyValueCell.onDidClick(() => {
+ vscode.env.clipboard.writeText(endpointInfo.hyperlink);
+ });
+ copyValueCell.title = localize("bdc.dashboard.copyTitle", "Copy");
+ copyValueCell.iconHeight = '14px';
+ copyValueCell.iconWidth = '14px';
+ endPointRow.addItem(copyValueCell, { CSSStyles: { 'width': '5%', 'padding-left': '10px' } });
+
+ endpointsContainer.addItem(endPointRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
+ });
+
+ contentContainer.addItem(endpointsContainer);
+
+ await view.initializeModel(dashboardRootContainer);
+ });
+ }
+}
+
+function getFriendlyEndpointNames(name: string): string {
+ let friendlyName: string = name;
+ switch (name) {
+ case 'app-proxy':
+ friendlyName = localize('bdc.dashboard.appproxy', "Application Proxy");
+ break;
+ case 'controller':
+ friendlyName = localize('bdc.dashboard.controller', "Cluster Management Service");
+ break;
+ case 'gateway':
+ friendlyName = localize('bdc.dashboard.gateway', "HDFS and Spark");
+ break;
+ case 'management-proxy':
+ friendlyName = localize('bdc.dashboard.managementproxy', "Management Proxy");
+ break;
+ case 'mgmtproxy':
+ friendlyName = localize('bdc.dashboard.mgmtproxy', "Management Proxy");
+ break;
+ default:
+ break;
+ }
+ return friendlyName;
+}
diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts
new file mode 100644
index 0000000000..4dfa2b47d5
--- /dev/null
+++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+'use strict';
+
+import { IBdcStatus, IStatus, IServiceStatus } from '../controller/clusterControllerApi';
+
+export class BdcDashboardModel {
+
+ private _clusterStatus: IBdcStatus;
+
+ constructor(public clusterName: string, url: string, username: string, password: string) {
+
+ }
+
+ public get clusterStatus(): IStatus {
+ return { healthStatus: 'Warning detected', state: 'ready' };
+ }
+
+ public get serviceStatus(): IServiceStatus[] {
+ return [
+ { serviceName: 'SQL Server', status: { state: 'Ready', healthStatus: 'Warning' }, resources: undefined },
+ { serviceName: 'HDFS', status: { state: 'Ready', healthStatus: 'Healthy' }, resources: undefined },
+ { serviceName: 'Spark', status: { state: 'Ready', healthStatus: 'Healthy' }, resources: undefined },
+ { serviceName: 'Control', status: { state: 'Ready', healthStatus: 'Healthy' }, resources: undefined },
+ { serviceName: 'Gateway', status: { state: 'Ready', healthStatus: 'Healthy' }, resources: undefined },
+ { serviceName: 'App', status: { state: 'Ready', healthStatus: 'Healthy' }, resources: undefined }
+ ];
+ }
+
+ public get serviceEndpoints(): { serviceName: string, hyperlink?: string, isHyperlink: boolean, ipAddress?: string, port?: string }[] {
+ return [
+ { serviceName: 'SQL Server Master Instance', ipAddress: '10.91.134.112', port: '31433', isHyperlink: false },
+ { serviceName: 'Controller', ipAddress: '10.91.134.112', port: '31433', isHyperlink: false },
+ { serviceName: 'HDFS/Spark Gateway', ipAddress: '10.91.134.112', port: '31433', isHyperlink: false },
+ { serviceName: 'Spark Job Management', hyperlink: 'https://10.91.134.112:30443/gateway/default/yarn', isHyperlink: true },
+ { serviceName: 'Grafana Dashboard', hyperlink: 'https://10.91.134.112/grafana/d/wZx3OUdmz', isHyperlink: true },
+ { serviceName: 'Kibana Dashboard', hyperlink: 'https://10.91.134.112/kibana/app/kibana#/discover', isHyperlink: true },
+ ];
+ }
+
+ public refresh(): void {
+
+ }
+}
diff --git a/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts
index 10c9784d88..88878d022f 100644
--- a/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts
+++ b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts
@@ -11,9 +11,9 @@ import { TreeNode } from './treeNode';
import { IControllerTreeChangeHandler } from './controllerTreeChangeHandler';
import { AddControllerNode } from './addControllerNode';
import { ControllerRootNode, ControllerNode } from './controllerTreeNode';
-import { IEndPoint } from '../controller/clusterControllerApi';
import { showErrorMessage } from '../utils';
import { LoadingControllerNode } from './loadingControllerNode';
+import { EndpointModel } from '../controller/apiGenerated';
const CredentialNamespace = 'clusterControllerCredentials';
@@ -63,7 +63,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider {
runThrottledAction(AddControllerCommand, () => addBdcController(treeDataProvider, node));
});
@@ -51,6 +54,12 @@ function registerCommands(treeDataProvider: ControllerTreeDataProvider): void {
}
treeDataProvider.notifyNodeChanged(node);
});
+
+ vscode.commands.registerCommand(ManageControllerCommand, async (node: ControllerNode) => {
+ const title: string = `${localize('bdc.dashboard.title', "Big Data Cluster Dashboard -")} ${ControllerNode.toIpAndPort(node.url)} ${localize('bdc.Dash', "-")} ${node.clusterName}`;
+ const dashboard: BdcDashboard = new BdcDashboard(title, new BdcDashboardModel(node.clusterName, node.url, node.username, node.password), context);
+ dashboard.showDashboard();
+ });
}
function addBdcController(treeDataProvider: ControllerTreeDataProvider, node?: TreeNode): void {
@@ -90,8 +99,6 @@ function deleteControllerInternal(treeDataProvider: ControllerTreeDataProvider,
}
}
-
-
/**
* Throttles actions to avoid bug where on clicking in tree, action gets called twice
* instead of once. Any right-click action is safe, just the default on-click action in a tree
@@ -106,4 +113,4 @@ function runThrottledAction(id: string, action: () => void) {
}, 150);
}
// else ignore this as we got an identical action in the last 150ms
-}
\ No newline at end of file
+}