mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Cluster management/newdashboard task (#6060)
* initial commit: added cluster status notebook and dashboard task * following the previous naming conventions * endpoint widget changes to accomodatw naming changes * management-proxy/mgmtproxy chnages * updates to address the comments and added the new copy image with hover text. * added user select for making the table selectable * localize changes * added the final documented notebook * reset execution_count to 0 for all cells * style changes * updated the url to point to private repo
This commit is contained in:
@@ -64,6 +64,7 @@ export enum MssqlClusterItemsSubType {
|
||||
// SPARK JOB SUBMISSION //////////////////////////////////////////////////////////
|
||||
export const mssqlClusterNewNotebookTask = 'mssqlCluster.task.newNotebook';
|
||||
export const mssqlClusterOpenNotebookTask = 'mssqlCluster.task.openNotebook';
|
||||
export const mssqlopenClusterStatusNotebook = 'mssqlCluster.task.openClusterStatusNotebook';
|
||||
export const mssqlClusterLivySubmitSparkJobCommand = 'mssqlCluster.livy.cmd.submitSparkJob';
|
||||
export const mssqlClusterLivySubmitSparkJobFromFileCommand = 'mssqlCluster.livy.cmd.submitFileToSparkJob';
|
||||
export const mssqlClusterLivySubmitSparkJobTask = 'mssqlCluster.livy.task.submitSparkJob';
|
||||
|
||||
@@ -137,36 +137,51 @@ export async function activate(context: vscode.ExtensionContext): Promise<MssqlE
|
||||
azdata.ui.registerModelViewProvider('bdc-endpoints', async (view) => {
|
||||
|
||||
const endpointsArray: Array<Utils.IEndpoint> = Object.assign([], view.serverInfo.options['clusterEndpoints']);
|
||||
endpointsArray.forEach(endpointInfo => {
|
||||
endpointInfo.isHyperlink = true;
|
||||
endpointInfo.hyperlink = 'https://' + endpointInfo.ipAddress + ':' + endpointInfo.port;
|
||||
|
||||
});
|
||||
if (endpointsArray.length > 0) {
|
||||
const managementProxyEp = endpointsArray.find(e => e.serviceName === 'management-proxy');
|
||||
const managementProxyEp = endpointsArray.find(e => e.serviceName === 'management-proxy' || e.serviceName === 'mgmtproxy');
|
||||
if (managementProxyEp) {
|
||||
endpointsArray.push(getCustomEndpoint(managementProxyEp, 'Grafana Dashboard', '/grafana'));
|
||||
endpointsArray.push(getCustomEndpoint(managementProxyEp, 'Kibana Dashboard', '/kibana'));
|
||||
endpointsArray.push(getCustomEndpoint(managementProxyEp, localize("grafana", "Metrics Dashboard"), '/grafana/d/wZx3OUdmz'));
|
||||
endpointsArray.push(getCustomEndpoint(managementProxyEp, localize("kibana", "Log Search Dashboard"), '/kibana/app/kibana#/discover'));
|
||||
}
|
||||
|
||||
const gatewayEp = endpointsArray.find(e => e.serviceName === 'gateway');
|
||||
if (gatewayEp) {
|
||||
endpointsArray.push(getCustomEndpoint(gatewayEp, 'Spark History', '/gateway/default/sparkhistory'));
|
||||
endpointsArray.push(getCustomEndpoint(gatewayEp, 'Yarn History', '/gateway/default/yarn'));
|
||||
endpointsArray.push(getCustomEndpoint(gatewayEp, localize("sparkHostory", "Spark Job Monitoring"), '/gateway/default/sparkhistory'));
|
||||
endpointsArray.push(getCustomEndpoint(gatewayEp, localize("yarnHistory", "Spark Resource Management"), '/gateway/default/yarn'));
|
||||
}
|
||||
|
||||
const container = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component();
|
||||
endpointsArray.forEach(endpointInfo => {
|
||||
const endPointRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
|
||||
const nameCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: endpointInfo.serviceName }).component();
|
||||
endPointRow.addItem(nameCell, { CSSStyles: { 'width': '30%', 'font-weight': '600' } });
|
||||
const nameCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: getFriendlyEndpointNames(endpointInfo.serviceName) }).component();
|
||||
endPointRow.addItem(nameCell, { CSSStyles: { 'width': '35%', 'font-weight': '600', 'user-select': 'text' } });
|
||||
if (endpointInfo.isHyperlink) {
|
||||
const linkCell = view.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({ label: endpointInfo.hyperlink, url: endpointInfo.hyperlink, position: '' }).component();
|
||||
endPointRow.addItem(linkCell, { CSSStyles: { 'width': '70%', 'color': 'blue', 'text-decoration': 'underline', 'padding-top': '10px' } });
|
||||
const linkCell = view.modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({ label: endpointInfo.hyperlink, url: endpointInfo.hyperlink }).component();
|
||||
endPointRow.addItem(linkCell, { CSSStyles: { 'width': '62%', 'color': '#0078d4', 'text-decoration': 'underline', 'padding-top': '10px' } });
|
||||
}
|
||||
else {
|
||||
const endpointCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: endpointInfo.ipAddress + ':' + endpointInfo.port }).component();
|
||||
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': '70%' } });
|
||||
endPointRow.addItem(endpointCell, { CSSStyles: { 'width': '62%', 'user-select': 'text' } });
|
||||
}
|
||||
container.addItem(endPointRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box' } });
|
||||
const copyValueCell = view.modelBuilder.button().component();
|
||||
copyValueCell.iconPath = { light: context.asAbsolutePath('resources/light/copy.png'), dark: context.asAbsolutePath('resources/dark/copy_inverse.png') };
|
||||
copyValueCell.onDidClick(() => {
|
||||
vscode.env.clipboard.writeText(endpointInfo.hyperlink);
|
||||
});
|
||||
copyValueCell.title = localize("copyText", "Copy");
|
||||
copyValueCell.iconHeight = '14px';
|
||||
copyValueCell.iconWidth = '14px';
|
||||
endPointRow.addItem(copyValueCell, { CSSStyles: { 'width': '3%', 'padding-top': '10px' } });
|
||||
|
||||
container.addItem(endPointRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
});
|
||||
const endpointsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%', alignItems: 'left' }).component();
|
||||
endpointsContainer.addItem(container, { CSSStyles: { 'padding-top': '25px' } });
|
||||
const endpointsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '540px', height: '100%', alignItems: 'left' }).component();
|
||||
endpointsContainer.addItem(container, { CSSStyles: { 'padding-top': '25px', 'padding-left': '5px' } });
|
||||
|
||||
await view.initializeModel(endpointsContainer);
|
||||
}
|
||||
@@ -187,6 +202,30 @@ export async function activate(context: vscode.ExtensionContext): Promise<MssqlE
|
||||
return null;
|
||||
}
|
||||
|
||||
function getFriendlyEndpointNames(name: string): string {
|
||||
let friendlyName: string = name;
|
||||
switch (name) {
|
||||
case 'app-proxy':
|
||||
friendlyName = localize("appproxy", "Application Proxy");
|
||||
break;
|
||||
case 'controller':
|
||||
friendlyName = localize("controller", "Cluster Management Service");
|
||||
break;
|
||||
case 'gateway':
|
||||
friendlyName = localize("gateway", "HDFS and Spark");
|
||||
break;
|
||||
case 'management-proxy':
|
||||
friendlyName = localize("managementproxy", "Management Proxy");
|
||||
break;
|
||||
case 'mgmtproxy':
|
||||
friendlyName = localize("mgmtproxy", "Management Proxy");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return friendlyName;
|
||||
}
|
||||
|
||||
let api: MssqlExtensionApi = {
|
||||
getMssqlObjectExplorerBrowser(): MssqlObjectExplorerBrowser {
|
||||
return {
|
||||
@@ -228,6 +267,9 @@ function activateNotebookTask(appContext: AppContext): void {
|
||||
apiWrapper.registerTaskHandler(Constants.mssqlClusterOpenNotebookTask, (profile: azdata.IConnectionProfile) => {
|
||||
return handleOpenNotebookTask(profile);
|
||||
});
|
||||
apiWrapper.registerTaskHandler(Constants.mssqlopenClusterStatusNotebook, (profile: azdata.IConnectionProfile) => {
|
||||
return handleOpenClusterStatusNotebookTask(profile, appContext);
|
||||
});
|
||||
}
|
||||
|
||||
function saveProfileAndCreateNotebook(profile: azdata.IConnectionProfile): Promise<void> {
|
||||
@@ -297,6 +339,21 @@ async function handleOpenNotebookTask(profile: azdata.IConnectionProfile): Promi
|
||||
}
|
||||
}
|
||||
|
||||
async function handleOpenClusterStatusNotebookTask(profile: azdata.IConnectionProfile, appContext: AppContext): Promise<void> {
|
||||
const notebookRelativePath = 'notebooks/tsg/cluster-status.ipynb';
|
||||
const notebookFullPath = path.join(appContext.extensionContext.extensionPath, notebookRelativePath);
|
||||
if (!Utils.fileExists(notebookFullPath)) {
|
||||
vscode.window.showErrorMessage(localize("fileNotFound", "Unable to find the file specified"));
|
||||
} else {
|
||||
const targetFile = Utils.getTargetFileName(notebookFullPath);
|
||||
Utils.copyFile(notebookFullPath, targetFile);
|
||||
let fileUri = vscode.Uri.file(targetFile);
|
||||
await azdata.nb.showNotebookDocument(fileUri, {
|
||||
connectionProfile: profile,
|
||||
preview: false
|
||||
});
|
||||
}
|
||||
}
|
||||
function generateServerOptions(executablePath: string): ServerOptions {
|
||||
let launchArgs = Utils.getCommonLaunchArgsAndCleanupOldLogFiles('sqltools', executablePath);
|
||||
return { command: executablePath, args: launchArgs, transport: TransportKind.stdio };
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as crypto from 'crypto';
|
||||
import * as os from 'os';
|
||||
import * as findRemoveSync from 'find-remove';
|
||||
import * as constants from './constants';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const configTracingLevel = 'tracingLevel';
|
||||
const configLogRetentionMinutes = 'logRetentionMinutes';
|
||||
@@ -29,6 +30,34 @@ export function getAppDataPath() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file name that is not already used in the target directory
|
||||
* @param filePath source notebook file name
|
||||
* @param fileExtension file type
|
||||
*/
|
||||
export function getTargetFileName(filePath: string): string {
|
||||
const targetDirectory = os.homedir();
|
||||
const fileExtension = path.extname(filePath);
|
||||
const baseName = path.basename(filePath, fileExtension);
|
||||
let targetFileName;
|
||||
let idx = 0;
|
||||
do {
|
||||
const suffix = idx === 0 ? '' : `-${idx}`;
|
||||
targetFileName = path.join(targetDirectory, `${baseName}${suffix}${fileExtension}`);
|
||||
idx++;
|
||||
} while (fs.existsSync(targetFileName));
|
||||
|
||||
return targetFileName;
|
||||
}
|
||||
|
||||
export function fileExists(file: string): boolean {
|
||||
return fs.existsSync(file);
|
||||
}
|
||||
|
||||
export function copyFile(source: string, target: string): void {
|
||||
fs.copyFileSync(source, target);
|
||||
}
|
||||
|
||||
export function removeOldLogFiles(prefix: string): JSON {
|
||||
return findRemoveSync(getDefaultLogDir(), { prefix: `${prefix}_`, age: { seconds: getConfigLogRetentionSeconds() }, limit: getConfigLogFilesRemovalLimit() });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user