Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended queue and partition with ID's #377

Merged
merged 5 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Unicorn History Server Components provides a remote components that are consumed by the [YuniKorn Web](https://github.com/G-Research/yunikorn-web/)

## Development environment setup

### Dependencies

- [Node.js](https://nodejs.org/en/)
Expand All @@ -14,7 +15,26 @@ For managing node packages you can use `npm`, `yarn` or `pnpm`. Run `npm install

### Development Server

Run `npm start` for a dev server. Remote components will be served from this path `http://localhost:3100/remoteEntry.js`. The application will automatically reload if you change any of the source files.
#### Setting up the environment

To establish a connection between the web UI and UHS, you need to configure the `src/assets/config/envconfig.json` file with the following values:

```json
{
"localUhsComponentsWebAddress": "http://localhost:3100",
"externalLogsURL": "https://logs.example.com?token=abc123&applicationId=",
"yunikornApiURL": "http://localhost:30001",
"uhsApiURL": "http://localhost:8989"
}
```

#### Running the development server

Follow these steps to run the development server:

1. Follow the instructions in the UHS to run the server.
2. Run `pnpm start` for a dev server. Remote components will be served from this path `http://localhost:3100/remoteEntry.js`. The application will automatically reload if you change any of the source files.
3. Follow the instructions in the [YuniKorn Web](https://github.com/G-Research/yunikorn-web/) repository to set up the development environment. This is required to run the web UI.

### JSON Server

Expand All @@ -23,13 +43,14 @@ To run a mock server for local development, follow these steps:
**Start the JSON Server**:

- **Using Makefile**: you can start the server by running:

```sh
make mock-server
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but II think this is make json-server, maybe we can fix in another PR

```

- **Using npm**: If you are in the `./web` directory, you can run the JSON Server directly with npm by using:
- **Using pnpm**: If you are in the `./web` directory, you can run the JSON Server directly with pnpm by using:
```sh
npm run start:json-server
pnpm run start:json-server
```

This will start the JSON Server and serve mock data. You can access it at `http://localhost:3000`.
Expand All @@ -42,12 +63,14 @@ Some endpoints that can be tested with ID's are:

### Build

Run `make web-build` from the project root or `npm run build`. Build output is set to `/assets` folder in project root as it will be served from the UHS server.
Run `make web-build` from the project root or `pnpm run build`. Build output is set to `/assets` folder in project root as it will be served from the UHS server.

## Further help

To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

## Code scaffolding

Run `ng generate component component-name` to generate a new component.

You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
117 changes: 86 additions & 31 deletions web/src/app/allocations-drawer/allocations-drawer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,75 @@
<mat-drawer #matDrawer mode="over" position="end">
<mat-drawer-content>
<div class="header">
<span>{{ selectedRow?.applicationId }} ({{ selectedRow?.allocations?.length }} allocations)</span>
<span class="far fa-clipboard copy-btn" (click)="copyLinkToClipboard()"
matTooltip="Click to copy the URL to this view" matTooltipShowDelay="500"></span>
<span
>{{ selectedRow?.applicationId }} ({{
selectedRow?.allocations?.length
}}
allocations)</span
>
<span
class="far fa-clipboard copy-btn"
(click)="copyLinkToClipboard()"
matTooltip="Click to copy the URL to this view"
matTooltipShowDelay="500"
></span>
<span class="far fa-solid fa-xmark close-btn" (click)="closeDrawer()"></span>
</div>
<div class="content">

<mat-table [dataSource]="allocDataSource" matSort #allocSort="matSort">
<ng-container [matColumnDef]="columnDef.colId" *ngFor="let columnDef of allocColumnDef">
<mat-header-cell *matHeaderCellDef mat-sort-header [style.flex]="columnDef?.colWidth || 1">{{
columnDef.colName }}</mat-header-cell>
<mat-header-cell
*matHeaderCellDef
mat-sort-header
[style.flex]="columnDef?.colWidth || 1"
>{{ columnDef.colName }}</mat-header-cell
>

<ng-container *ngIf="columnDef.colId === 'priority'; else renderNext_3">
<mat-cell class="small" *matCellDef="let element" [style.flex]="columnDef?.colWidth || 1"
[title]="element[columnDef.colId]">{{
element['priority'] }} </mat-cell>
<mat-cell
class="small"
*matCellDef="let element"
[style.flex]="columnDef?.colWidth || 1"
[title]="element[columnDef.colId]"
>{{ element['priority'] }}
</mat-cell>
</ng-container>

<ng-container *ngIf="columnDef.colId === 'log'; else renderNext_3">
<mat-cell class="small" *matCellDef="let element" [style.flex]="columnDef?.colWidth || 1"><a
href="{{ externalLogsBaseUrl }}{{ element['applicationId'] }}" target="_blank"
(click)="logClick($event)">Logs</a> </mat-cell>
<mat-cell
class="small"
*matCellDef="let element"
[style.flex]="columnDef?.colWidth || 1"
><a
href="{{ externalLogsBaseUrl }}{{ element['applicationId'] }}"
target="_blank"
(click)="logClick($event)"
>Logs</a
>
</mat-cell>
</ng-container>

<ng-container *ngIf="columnDef.colId === 'resource'; else renderNext_3">
<mat-cell *matCellDef="let element" class="allocations-data" [style.flex]="columnDef?.colWidth || 1"
<mat-cell
*matCellDef="let element"
class="allocations-data"
[style.flex]="columnDef?.colWidth || 1"
matTooltip="
{{element[columnDef.colId]}}" matTooltipShowDelay="500">
<ng-container *ngIf="columnDef.colFormatter; else showAllocRowData;">
<ng-container *ngIf="columnDef.colFormatter(element[columnDef.colId]) as colValue">
{{ element[columnDef.colId] }}"
matTooltipShowDelay="500"
>
<ng-container *ngIf="columnDef.colFormatter; else showAllocRowData">
<ng-container
*ngIf="columnDef.colFormatter(element[columnDef.colId]) as colValue"
>
<ul class="mat-res-ul">
<ng-container *ngFor="let resource of formatResources(colValue); let i = index">
<li class="mat-res-li" *ngIf="i<1">
<ng-container
*ngFor="let resource of formatResources(colValue); let i = index"
>
<li class="mat-res-li" *ngIf="i < 1">
{{ resource }}
</li>
<li class="mat-res-li" *ngIf="i>=1 && element['expanded']">
<li class="mat-res-li" *ngIf="i >= 1 && element['expanded']">
{{ resource }}
</li>
</ng-container>
Expand All @@ -50,11 +83,24 @@
</mat-cell>
</ng-container>

<ng-container *ngIf="columnDef.colId === 'elapsedTime'; else renderNext_3">
<mat-cell
*matCellDef="let element"
[style.flex]="columnDef?.colWidth || 1"
[title]="element[columnDef.colId]"
>{{ calculateElapsedTime(element['requestTime'], element['allocationTime']) }}
</mat-cell>
</ng-container>

<ng-template #renderNext_3>
<mat-cell *matCellDef="let element" [class]="element['expanded'] ? '' : 'ellipsis'"
[style.flex]="columnDef?.colWidth || 1" matTooltip="{{element[columnDef.colId]}}"
matTooltipShowDelay="500">{{ element[columnDef.colId] || 'n/a'
}}</mat-cell>
<mat-cell
*matCellDef="let element"
[class]="element['expanded'] ? '' : 'ellipsis'"
[style.flex]="columnDef?.colWidth || 1"
matTooltip="{{ element[columnDef.colId] }}"
matTooltipShowDelay="500"
>{{ element[columnDef.colId] || 'n/a' }}</mat-cell
>
</ng-template>
</ng-container>

Expand All @@ -66,17 +112,26 @@

<mat-header-row *matHeaderRowDef="allocColumnIds"></mat-header-row>

<mat-row *matRowDef="let row; columns: allocColumnIds; let i = index" (click)="allocationsDetailToggle(i)"
[ngClass]="{'even-row': i % 2 === 0, 'row': true}"></mat-row>
<mat-row
*matRowDef="let row; columns: allocColumnIds; let i = index"
(click)="allocationsDetailToggle(i)"
[ngClass]="{ 'even-row': i % 2 === 0, row: true }"
></mat-row>

<mat-footer-row *matFooterRowDef="['noRecord']"
[ngStyle]="{ display: isAllocDataSourceEmpty() ? '' : 'none' }"></mat-footer-row>
<mat-footer-row
*matFooterRowDef="['noRecord']"
[ngStyle]="{ display: isAllocDataSourceEmpty() ? '' : 'none' }"
></mat-footer-row>
</mat-table>

<mat-paginator #allocationMatPaginator [pageSizeOptions]="[10, 20, 50, 100]" [pageSize]="50"
[ngStyle]="{ display: isAllocDataSourceEmpty() ? 'none' : '' }" showFirstLastButtons></mat-paginator>

<mat-paginator
#allocationMatPaginator
[pageSizeOptions]="[10, 20, 50, 100]"
[pageSize]="50"
[ngStyle]="{ display: isAllocDataSourceEmpty() ? 'none' : '' }"
showFirstLastButtons
></mat-paginator>
</div>
</mat-drawer-content>
</mat-drawer>
</mat-drawer-container>
</mat-drawer-container>
97 changes: 64 additions & 33 deletions web/src/app/allocations-drawer/allocations-drawer.component.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
import { Component, EventEmitter, Injectable, Input, OnInit, Output, ViewChild } from "@angular/core";
import { MatPaginator } from "@angular/material/paginator";
import { MatDrawer } from "@angular/material/sidenav";
import { MatSort } from "@angular/material/sort";
import { MatTableDataSource } from "@angular/material/table";
import { AllocationInfo } from "@app/models/alloc-info.model";
import { AppInfo } from "@app/models/app-info.model";
import { ColumnDef } from "@app/models/column-def.model";
import { EnvConfigService } from "@app/services/envconfig/envconfig.service";
import { CommonUtil } from "@app/utils/common.util";
import {
Component,
EventEmitter,
Injectable,
Input,
OnInit,
Output,
ViewChild,
} from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatDrawer } from '@angular/material/sidenav';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { AllocationInfo } from '@app/models/alloc-info.model';
import { AppInfo } from '@app/models/app-info.model';
import { ColumnDef } from '@app/models/column-def.model';
import { EnvConfigService } from '@app/services/envconfig/envconfig.service';
import { CommonUtil } from '@app/utils/common.util';

@Injectable()
@Component({
selector: "app-allocations-drawer",
templateUrl: "./allocations-drawer.component.html",
styleUrls: ["./allocations-drawer.component.scss"],
selector: 'app-allocations-drawer',
templateUrl: './allocations-drawer.component.html',
styleUrls: ['./allocations-drawer.component.scss'],
})
export class AllocationsDrawerComponent implements OnInit {
@ViewChild("matDrawer", { static: false }) matDrawer!: MatDrawer;
@ViewChild("allocationMatPaginator", { static: true }) allocPaginator!: MatPaginator;
@ViewChild("allocSort", { static: true }) allocSort!: MatSort;
@ViewChild('matDrawer', { static: false }) matDrawer!: MatDrawer;
@ViewChild('allocationMatPaginator', { static: true }) allocPaginator!: MatPaginator;
@ViewChild('allocSort', { static: true }) allocSort!: MatSort;
@Input() allocDataSource!: MatTableDataSource<AllocationInfo & { expanded: boolean }>;
@Input() selectedRow!: AppInfo | null;
@Input() externalLogsBaseUrl!: string | null;
Expand All @@ -41,41 +49,50 @@ export class AllocationsDrawerComponent implements OnInit {

ngOnInit(): void {
this.allocColumnDef = [
{ colId: "displayName", colName: "Display Name", colWidth: 1 },
{ colId: "allocationKey", colName: "Allocation Key", colWidth: 1 },
{ colId: "nodeId", colName: "Node ID", colWidth: 1 },
{ colId: 'displayName', colName: 'Display Name', colWidth: 1 },
{ colId: 'allocationKey', colName: 'Allocation Key', colWidth: 1 },
{ colId: 'nodeId', colName: 'Node ID', colWidth: 1 },
{
colId: "log",
colName: "Log Link",
colId: 'log',
colName: 'Log Link',
colWidth: 1,
},
{
colId: "resource",
colName: "Resource",
colId: 'resource',
colName: 'Resource',
colFormatter: CommonUtil.resourceColumnFormatter,
colWidth: 1,
},
{ colId: "priority", colName: "Priority", colWidth: 0.5 },
{ colId: 'priority', colName: 'Priority', colWidth: 0.5 },
{
colId: 'elapsedTime',
colName: 'Time to Allocation',
colWidth: 0.8,
},
];
this.allocColumnIds = this.allocColumnDef.map((col) => col.colId);
this.externalLogsBaseUrl = this.envConfig.getExternalLogsBaseUrl();
}

formatResources(colValue: string): string[] {
const arr: string[] = colValue.split("<br/>");
const arr: string[] = colValue.split('<br/>');
// Check if there are "cpu" or "Memory" elements in the array
const hasCpu = arr.some((item) => item.toLowerCase().includes("cpu"));
const hasMemory = arr.some((item) => item.toLowerCase().includes("memory"));
const hasCpu = arr.some((item) => item.toLowerCase().includes('cpu'));
const hasMemory = arr.some((item) => item.toLowerCase().includes('memory'));
if (!hasCpu) {
arr.unshift("CPU: n/a");
arr.unshift('CPU: n/a');
}
if (!hasMemory) {
arr.unshift("Memory: n/a");
arr.unshift('Memory: n/a');
}

// Concatenate the two arrays, with "cpu" and "Memory" elements first
const cpuAndMemoryElements = arr.filter((item) => item.toLowerCase().includes("CPU") || item.toLowerCase().includes("Memory"));
const otherElements = arr.filter((item) => !item.toLowerCase().includes("CPU") && !item.toLowerCase().includes("Memory"));
const cpuAndMemoryElements = arr.filter(
(item) => item.toLowerCase().includes('CPU') || item.toLowerCase().includes('Memory')
);
const otherElements = arr.filter(
(item) => !item.toLowerCase().includes('CPU') && !item.toLowerCase().includes('Memory')
);
const result = cpuAndMemoryElements.concat(otherElements);

return result;
Expand Down Expand Up @@ -116,8 +133,22 @@ export class AllocationsDrawerComponent implements OnInit {
}

copyLinkToClipboard() {
const url = window.location.href.split("?")[0];
const url = window.location.href.split('?')[0];
const copyString = `${url}?partition=${this.partitionSelected}&queue=${this.leafQueueSelected}&applicationId=${this?.selectedRow?.applicationId}`;
navigator.clipboard.writeText(copyString).catch((error) => console.error("Writing to the clipboard is not allowed. ", error));
navigator.clipboard
.writeText(copyString)
.catch((error) => console.error('Writing to the clipboard is not allowed. ', error));
}

calculateElapsedTime(requestTime: string, allocationTime: string): string {
if (!requestTime) {
return 'n/a';
}

const request = parseInt(requestTime) / 1_000_000; // nanoseconds to milliseconds
const allocation = parseInt(allocationTime) / 1_000_000; // nanoseconds to milliseconds
const diffInSeconds = Math.floor((allocation - request) / 1000);

return `${diffInSeconds}s`;
}
}
4 changes: 3 additions & 1 deletion web/src/app/models/alloc-info.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export class AllocationInfo {
public queueName: string,
public nodeId: string,
public applicationId: string,
public partition: string
public partition: string,
public requestTime: string,
public allocationTime: string
) {}
}
Loading
Loading