From 1caeacac98a0faa809405aedf2bffece7ef92665 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Thu, 28 Nov 2024 05:25:49 +0500 Subject: [PATCH 1/7] NAS-132372: Add `/dev/zvol` root for explorer --- .../cloud-backup-form.component.html | 1 + .../cloud-backup-form.component.ts | 5 ++- src/app/services/filesystem.service.ts | 35 +++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.html b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.html index bc6dcc81d94..b48964ea304 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.html +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.html @@ -13,6 +13,7 @@ [tooltip]="helptext.source_path_tooltip | translate" [required]="true" [multiple]="false" + [root]="'/'" [nodeProvider]="fileNodeProvider" > diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts index 39d53afe19b..434ca56fb76 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts @@ -331,7 +331,10 @@ export class CloudBackupFormComponent implements OnInit { } private setFileNodeProvider(): void { - this.fileNodeProvider = this.filesystemService.getFilesystemNodeProvider({ directoriesOnly: true }); + this.fileNodeProvider = this.filesystemService.getFilesystemNodeProvider({ + dsAndZvols: true, + directoriesOnly: true, + }); } private setBucketNodeProvider(): void { diff --git a/src/app/services/filesystem.service.ts b/src/app/services/filesystem.service.ts index 806e17672a7..8fbb848a3a7 100644 --- a/src/app/services/filesystem.service.ts +++ b/src/app/services/filesystem.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { map } from 'rxjs'; +import { map, of } from 'rxjs'; import { ExplorerNodeType } from 'app/enums/explorer-type.enum'; import { FileAttribute } from 'app/enums/file-attribute.enum'; import { FileType } from 'app/enums/file-type.enum'; @@ -9,6 +9,12 @@ import { ExplorerNodeData, TreeNode } from 'app/interfaces/tree-node.interface'; import { TreeNodeProvider } from 'app/modules/forms/ix-forms/components/ix-explorer/tree-node-provider.interface'; import { ApiService } from 'app/services/websocket/api.service'; +export interface ProviderOptions { + directoriesOnly?: boolean; + showHiddenFiles?: boolean; + includeSnapshots?: boolean; + dsAndZvols?: boolean; +} @Injectable({ providedIn: 'root' }) export class FilesystemService { constructor( @@ -18,19 +24,34 @@ export class FilesystemService { /** * Returns a pre-configured node provider for files and directories. */ - getFilesystemNodeProvider(providerOptions?: { - directoriesOnly?: boolean; - showHiddenFiles?: boolean; - includeSnapshots?: boolean; - }): TreeNodeProvider { - const options = { + getFilesystemNodeProvider(providerOptions?: ProviderOptions): TreeNodeProvider { + const options: ProviderOptions = { directoriesOnly: false, showHiddenFiles: false, includeSnapshots: true, + dsAndZvols: false, ...providerOptions, }; return (node: TreeNode) => { + if (options.dsAndZvols) { + if (node.data.path.trim() === '/') { + return of([ + { + path: '/mnt', + name: '/mnt', + hasChildren: true, + type: ExplorerNodeType.Directory, + }, + { + path: '/dev/zvol', + name: '/dev/zvol', + hasChildren: true, + type: ExplorerNodeType.Directory, + }, + ] as ExplorerNodeData[]); + } + } const typeFilter: [QueryFilter?] = []; if (options.directoriesOnly) { typeFilter.push(['type', '=', FileType.Directory]); From 0abeded22f7ce3e3fb23903e1983e72a21852179 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Fri, 29 Nov 2024 04:31:00 +0500 Subject: [PATCH 2/7] NAS-132372: Remove directories only condition and symlink check --- .../cloud-backup-form/cloud-backup-form.component.ts | 1 - src/app/services/filesystem.service.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts index 434ca56fb76..7074976ab76 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts @@ -333,7 +333,6 @@ export class CloudBackupFormComponent implements OnInit { private setFileNodeProvider(): void { this.fileNodeProvider = this.filesystemService.getFilesystemNodeProvider({ dsAndZvols: true, - directoriesOnly: true, }); } diff --git a/src/app/services/filesystem.service.ts b/src/app/services/filesystem.service.ts index 8fbb848a3a7..2f2b6413515 100644 --- a/src/app/services/filesystem.service.ts +++ b/src/app/services/filesystem.service.ts @@ -71,7 +71,7 @@ export class FilesystemService { map((files) => { const children: ExplorerNodeData[] = []; files.forEach((file) => { - if (file.type === FileType.Symlink || !file.hasOwnProperty('name')) { + if ((!options.dsAndZvols && file.type === FileType.Symlink) || !file.hasOwnProperty('name')) { return; } From 5a24804bc99b34b0bebfc4993b072347111af104 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Sat, 30 Nov 2024 04:31:57 +0500 Subject: [PATCH 3/7] NAS-132372: Update filesystem.service.ts --- src/app/services/filesystem.service.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app/services/filesystem.service.ts b/src/app/services/filesystem.service.ts index 2f2b6413515..e65d607c444 100644 --- a/src/app/services/filesystem.service.ts +++ b/src/app/services/filesystem.service.ts @@ -1,8 +1,11 @@ import { Injectable } from '@angular/core'; -import { map, of } from 'rxjs'; +import { + catchError, map, of, throwError, +} from 'rxjs'; import { ExplorerNodeType } from 'app/enums/explorer-type.enum'; import { FileAttribute } from 'app/enums/file-attribute.enum'; import { FileType } from 'app/enums/file-type.enum'; +import { ApiError } from 'app/interfaces/api-error.interface'; import { FileRecord } from 'app/interfaces/file-record.interface'; import { QueryFilter, QueryOptions } from 'app/interfaces/query-api.interface'; import { ExplorerNodeData, TreeNode } from 'app/interfaces/tree-node.interface'; @@ -68,6 +71,7 @@ export class FilesystemService { }; return this.api.call('filesystem.listdir', [node.data.path, typeFilter, queryOptions]).pipe( + map((files) => { const children: ExplorerNodeData[] = []; files.forEach((file) => { @@ -91,6 +95,12 @@ export class FilesystemService { return children; }), + catchError((error: ApiError) => { + if (error.reason === '[ENOENT] Directory /dev/zvol does not exist') { + return of([]); + } + return throwError(() => (error)); + }), ); }; } From 33f1853f5bd156efec15c06bf19dc075ac893276 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Sat, 30 Nov 2024 04:34:09 +0500 Subject: [PATCH 4/7] NAS-132372: Fix name variable --- .../cloud-backup-form/cloud-backup-form.component.ts | 2 +- src/app/services/filesystem.service.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts index 7074976ab76..59f0a8767df 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts @@ -332,7 +332,7 @@ export class CloudBackupFormComponent implements OnInit { private setFileNodeProvider(): void { this.fileNodeProvider = this.filesystemService.getFilesystemNodeProvider({ - dsAndZvols: true, + datasetsAndZvols: true, }); } diff --git a/src/app/services/filesystem.service.ts b/src/app/services/filesystem.service.ts index e65d607c444..38e7c2b565c 100644 --- a/src/app/services/filesystem.service.ts +++ b/src/app/services/filesystem.service.ts @@ -16,7 +16,7 @@ export interface ProviderOptions { directoriesOnly?: boolean; showHiddenFiles?: boolean; includeSnapshots?: boolean; - dsAndZvols?: boolean; + datasetsAndZvols?: boolean; } @Injectable({ providedIn: 'root' }) export class FilesystemService { @@ -32,12 +32,12 @@ export class FilesystemService { directoriesOnly: false, showHiddenFiles: false, includeSnapshots: true, - dsAndZvols: false, + datasetsAndZvols: false, ...providerOptions, }; return (node: TreeNode) => { - if (options.dsAndZvols) { + if (options.datasetsAndZvols) { if (node.data.path.trim() === '/') { return of([ { @@ -75,7 +75,7 @@ export class FilesystemService { map((files) => { const children: ExplorerNodeData[] = []; files.forEach((file) => { - if ((!options.dsAndZvols && file.type === FileType.Symlink) || !file.hasOwnProperty('name')) { + if ((!options.datasetsAndZvols && file.type === FileType.Symlink) || !file.hasOwnProperty('name')) { return; } From a012ab19fc3a175ed182a6b6bf5001f047f410b5 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Wed, 4 Dec 2024 05:41:03 +0500 Subject: [PATCH 5/7] NAS-132372: Added new icon for link-file --- src/app/enums/explorer-type.enum.ts | 1 + .../ix-explorer/ix-explorer.component.html | 2 ++ src/app/services/filesystem.service.ts | 14 +++++++++++++- src/assets/icons/custom/file-link.svg | 1 + src/assets/icons/sprite-config.json | 2 +- src/assets/icons/sprite.svg | 2 +- 6 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 src/assets/icons/custom/file-link.svg diff --git a/src/app/enums/explorer-type.enum.ts b/src/app/enums/explorer-type.enum.ts index 23d14f6881b..00d28c46c8d 100644 --- a/src/app/enums/explorer-type.enum.ts +++ b/src/app/enums/explorer-type.enum.ts @@ -1,4 +1,5 @@ export enum ExplorerNodeType { Directory = 'directory', File = 'file', + Symlink = 'symlink', } diff --git a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html index 9be4b292f32..e222d0ed2bf 100644 --- a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html +++ b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html @@ -60,6 +60,8 @@ > @if (node.data.type === ExplorerNodeType.File) { + } @else if (node.data.type === ExplorerNodeType.Symlink) { + } @else { @if (node.data.isLock) { diff --git a/src/app/services/filesystem.service.ts b/src/app/services/filesystem.service.ts index 38e7c2b565c..3f7c899e93d 100644 --- a/src/app/services/filesystem.service.ts +++ b/src/app/services/filesystem.service.ts @@ -83,12 +83,24 @@ export class FilesystemService { return; } + let fileType: ExplorerNodeType; + switch (file.type) { + case FileType.Directory: + fileType = ExplorerNodeType.Directory; + break; + case FileType.Symlink: + fileType = ExplorerNodeType.Symlink; + break; + default: + fileType = ExplorerNodeType.File; + break; + } children.push({ path: file.path, name: file.name, isMountpoint: file.attributes.includes(FileAttribute.MountRoot), isLock: file.attributes.includes(FileAttribute.Immutable), - type: file.type === FileType.Directory ? ExplorerNodeType.Directory : ExplorerNodeType.File, + type: fileType, hasChildren: file.type === FileType.Directory, }); }); diff --git a/src/assets/icons/custom/file-link.svg b/src/assets/icons/custom/file-link.svg new file mode 100644 index 00000000000..354e1faee13 --- /dev/null +++ b/src/assets/icons/custom/file-link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/sprite-config.json b/src/assets/icons/sprite-config.json index a9f7ea8ed38..ac4d3fd659c 100644 --- a/src/assets/icons/sprite-config.json +++ b/src/assets/icons/sprite-config.json @@ -1,3 +1,3 @@ { - "iconUrl": "assets/icons/sprite.svg?v=1bbe5d8faa" + "iconUrl": "assets/icons/sprite.svg?v=c891ca0d0a" } \ No newline at end of file diff --git a/src/assets/icons/sprite.svg b/src/assets/icons/sprite.svg index 1c648ac2eb5..f8b0e6663fb 100644 --- a/src/assets/icons/sprite.svg +++ b/src/assets/icons/sprite.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 52405c3aef21ddbac02b3a0da431504bfe026072 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Thu, 5 Dec 2024 03:09:30 +0500 Subject: [PATCH 6/7] NAS-132372: Change zvol icon to database --- .../ix-forms/components/ix-explorer/ix-explorer.component.html | 2 +- src/assets/icons/sprite-config.json | 2 +- src/assets/icons/sprite.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html index 34362b55690..e480bdf5067 100644 --- a/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html +++ b/src/app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component.html @@ -61,7 +61,7 @@ @if (node.data.type === ExplorerNodeType.File) { } @else if (node.data.type === ExplorerNodeType.Symlink) { - + } @else { @if (node.data.isLock) { diff --git a/src/assets/icons/sprite-config.json b/src/assets/icons/sprite-config.json index ac4d3fd659c..5c47544f78a 100644 --- a/src/assets/icons/sprite-config.json +++ b/src/assets/icons/sprite-config.json @@ -1,3 +1,3 @@ { - "iconUrl": "assets/icons/sprite.svg?v=c891ca0d0a" + "iconUrl": "assets/icons/sprite.svg?v=29ff3e7fb0" } \ No newline at end of file diff --git a/src/assets/icons/sprite.svg b/src/assets/icons/sprite.svg index f8b0e6663fb..341d86b69b8 100644 --- a/src/assets/icons/sprite.svg +++ b/src/assets/icons/sprite.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From e9613d41767830e279fbd56cab66ec2deb44fdb4 Mon Sep 17 00:00:00 2001 From: RehanY147 Date: Thu, 5 Dec 2024 03:16:00 +0500 Subject: [PATCH 7/7] NAS-132372: Update filesystem.service.spec.ts --- src/app/services/filesystem.service.spec.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/app/services/filesystem.service.spec.ts b/src/app/services/filesystem.service.spec.ts index b17c4e1dc6c..5cf9cd5d4c6 100644 --- a/src/app/services/filesystem.service.spec.ts +++ b/src/app/services/filesystem.service.spec.ts @@ -28,6 +28,12 @@ describe('FilesystemService', () => { type: FileType.File, attributes: [FileAttribute.Immutable], }, + { + path: '/mnt/parent/zvol', + name: 'zvol', + type: FileType.Symlink, + attributes: [FileAttribute.Immutable], + }, ] as FileRecord[]), ]), ], @@ -37,7 +43,7 @@ describe('FilesystemService', () => { describe('getFilesystemNodeProvider', () => { it('returns a TreeNodeProvider that calls filesystem.listdir to list files and directories', async () => { - const treeNodeProvider = spectator.service.getFilesystemNodeProvider(); + const treeNodeProvider = spectator.service.getFilesystemNodeProvider({ datasetsAndZvols: true }); const childNodes = await lastValueFrom( treeNodeProvider({ @@ -72,6 +78,14 @@ describe('FilesystemService', () => { isMountpoint: false, isLock: true, }, + { + hasChildren: false, + name: 'zvol', + path: '/mnt/parent/zvol', + type: ExplorerNodeType.Symlink, + isMountpoint: false, + isLock: true, + }, ]); }); });