Skip to content

Commit

Permalink
NAS-132372 / 25.04 / Add /dev/zvol root for explorer (#11106)
Browse files Browse the repository at this point in the history
  • Loading branch information
RehanY147 authored Dec 4, 2024
1 parent 05a0c9d commit b4c2eb0
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/app/enums/explorer-type.enum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum ExplorerNodeType {
Directory = 'directory',
File = 'file',
Symlink = 'symlink',
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
>
@if (node.data.type === ExplorerNodeType.File) {
<ix-icon name="insert_drive_file"></ix-icon>
} @else if (node.data.type === ExplorerNodeType.Symlink) {
<ix-icon [name]="'mdi-database'"></ix-icon>
} @else {
@if (node.data.isLock) {
<ix-icon name="mdi-folder-lock"></ix-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
[tooltip]="helptext.source_path_tooltip | translate"
[required]="true"
[multiple]="false"
[root]="'/'"
[nodeProvider]="fileNodeProvider"
></ix-explorer>
</ix-fieldset>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,9 @@ export class CloudBackupFormComponent implements OnInit {
}

private setFileNodeProvider(): void {
this.fileNodeProvider = this.filesystemService.getFilesystemNodeProvider({ directoriesOnly: true });
this.fileNodeProvider = this.filesystemService.getFilesystemNodeProvider({
datasetsAndZvols: true,
});
}

private setBucketNodeProvider(): void {
Expand Down
16 changes: 15 additions & 1 deletion src/app/services/filesystem.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[]),
]),
],
Expand All @@ -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({
Expand Down Expand Up @@ -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,
},
]);
});
});
Expand Down
61 changes: 52 additions & 9 deletions src/app/services/filesystem.service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Injectable } from '@angular/core';
import { map } 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';
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;
datasetsAndZvols?: boolean;
}
@Injectable({ providedIn: 'root' })
export class FilesystemService {
constructor(
Expand All @@ -18,19 +27,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,
datasetsAndZvols: false,
...providerOptions,
};

return (node: TreeNode<ExplorerNodeData>) => {
if (options.datasetsAndZvols) {
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<FileRecord>?] = [];
if (options.directoriesOnly) {
typeFilter.push(['type', '=', FileType.Directory]);
Expand All @@ -47,29 +71,48 @@ export class FilesystemService {
};

return this.api.call('filesystem.listdir', [node.data.path, typeFilter, queryOptions]).pipe(

map((files) => {
const children: ExplorerNodeData[] = [];
files.forEach((file) => {
if (file.type === FileType.Symlink || !file.hasOwnProperty('name')) {
if ((!options.datasetsAndZvols && file.type === FileType.Symlink) || !file.hasOwnProperty('name')) {
return;
}

if (!options.showHiddenFiles && file.name.startsWith('.')) {
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,
});
});

return children;
}),
catchError((error: ApiError) => {
if (error.reason === '[ENOENT] Directory /dev/zvol does not exist') {
return of([]);
}
return throwError(() => (error));
}),
);
};
}
Expand Down
1 change: 1 addition & 0 deletions src/assets/icons/custom/file-link.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/assets/icons/sprite-config.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"iconUrl": "assets/icons/sprite.svg?v=0a208a877e"
"iconUrl": "assets/icons/sprite.svg?v=29ff3e7fb0"
}
2 changes: 1 addition & 1 deletion src/assets/icons/sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b4c2eb0

Please sign in to comment.