Skip to content

Commit

Permalink
Added file creation option for FileIO
Browse files Browse the repository at this point in the history
  • Loading branch information
brkelly20 committed Jul 24, 2024
1 parent 8f8dbfa commit c5e6732
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<CardContainer>
<template #header>
Create New File
</template>
<div
class="space-y-content"
>
<InputLabelWrapper>
<template #label>
{{ _("File Size (default in bytes, can use suffixes (kB, K, MB, M, etc.))") }}
</template>

<InputField
:placeholder="'Size'"
type="text"
v-model="tempOptions.fileSize"
/>
<ValidationResultView v-bind="fileSizeValidationResult"/>
</InputLabelWrapper>
</div>
<template v-slot:footer>
<div class="button-group-row justify-end grow">
<button
class="btn btn-secondary"
@click="emit('close')"
>{{ ("Cancel") }}</button>
<button
class="btn btn-primary"
@click="actions.createFile"
:disabled="!validationScope.isValid()"
>{{ ("Create") }}</button>
</div>
</template>
</CardContainer>
</template>

<script setup lang="ts">
import { BashCommand, getServer } from "@45drives/houston-common-lib";
import {
CardContainer, InputField, InputLabelWrapper, pushNotification, Notification, useTempObjectStaging, validationError, ValidationResultView,
ValidationScope,
validationSuccess,
wrapActions
} from "@45drives/houston-common-ui";
import { ref } from "vue";
const _ = cockpit.gettext;
const emit = defineEmits<{
(e: "close"): void;
}>();
const props = defineProps<{ filePath: string }>();
const creationOptions = ref(
{
fileSize: "",
});
const { tempObject: tempOptions, modified, resetChanges} = useTempObjectStaging(creationOptions);
const createFile = () => {
return getServer()
.andThen((server) => server.execute(new BashCommand(`dd if=/dev/zero of=${props.filePath} bs=1 count=0 seek=${tempOptions.value.fileSize.replace(" ", "")}`)))
.map(() => {
pushNotification(new Notification("Success", `Created file successfully.`, "success", 2000))
emit('close');
})
.mapErr((error) => {
pushNotification(new Notification("Error", `Unable to create file: ${error.message}`, "error"));
});
}
const actions = wrapActions({createFile});
const validationScope = new ValidationScope();
const { validationResult: fileSizeValidationResult } = validationScope.useValidator(() => {
if (!tempOptions.value.fileSize) {
return validationError("File Size is required.");
}
return validationSuccess();
});
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<SelectMenu
v-model="tempDevice.deviceType"
:options="deviceTypeOptions"
@change="updateFileValidator"
/>
</InputLabelWrapper>

Expand All @@ -39,7 +40,7 @@
</template>

<InputField
:placeholder="'File path to device'"
:placeholder="'Full file path to device'"
v-model="tempDevice.filePath"
/>
<ValidationResultView v-bind="filePathValidationResult"/>
Expand Down Expand Up @@ -73,18 +74,20 @@
</div>
</template>
</CardContainer>
<Modal :show="showFileIOCreation" @click-outside="showFileIOCreation = false">
<FileIOCreationPrompt @close="{showFileIOCreation = false; updateFileValidator()}" :filePath="tempDevice.filePath"/>
</Modal>
</template>

<script setup lang="ts">
import { CardContainer, InputField, InputLabelWrapper, SelectMenu, ToggleSwitchGroup, ToggleSwitch, useTempObjectStaging, wrapActions, type SelectMenuOption, ValidationResultView, validationSuccess, validationError, ValidationScope } from '@45drives/houston-common-ui';
import { err, ok, type ResultAsync } from 'neverthrow';
import { computed, inject, ref, type Ref } from 'vue';
import { CardContainer, InputField, InputLabelWrapper, Modal, SelectMenu, useTempObjectStaging, wrapActions, type SelectMenuOption, ValidationResultView, validationSuccess, validationError, ValidationScope, type ValidationResultAction, validationWarning } from '@45drives/houston-common-ui';
import { err, ok, okAsync, type ResultAsync } from 'neverthrow';
import { computed, inject, ref, watchEffect, type Ref } from 'vue';
import { DeviceType, VirtualDevice } from '@/tabs/iSCSI/types/VirtualDevice';
import { Command, Path, ProcessError, StringToIntCaster, getServer } from '@45drives/houston-common-lib';
import { Command, FileSystemNode, Path, ProcessError, StringToIntCaster, getServer } from '@45drives/houston-common-lib';
import type { ISCSIDriver } from '@/tabs/iSCSI/types/drivers/ISCSIDriver';
import { useUserSettings } from '@/common/user-settings';
import type { ISCSIDriverClusteredServer } from '@/tabs/iSCSI/types/drivers/ISCSIDriverClusteredServer';
import type { Target } from '@/tabs/iSCSI/types/Target';
import FileIOCreationPrompt from '@/tabs/iSCSI/ui/screens/virtualDevice/FileIOCreationPrompt.vue';
const _ = cockpit.gettext;
Expand All @@ -98,6 +101,8 @@
const deviceTypeOptions: Ref<SelectMenuOption<DeviceType>[]> = ref([]);
const showFileIOCreation = ref(false);
driver.map((driver) => driver.getHandledDeviceTypes()
.map((deviceType) => ({label: deviceType.toString(), value: deviceType})))
.map((options) => deviceTypeOptions.value = options);
Expand Down Expand Up @@ -131,7 +136,24 @@
.mapErr((error) => new ProcessError(`Unable to create device ${tempDevice.value.deviceName}: ${error.message}`))
}
const actions = wrapActions({createDevice});
const createFileIOPrompt = () => {
showFileIOCreation.value = true;
return okAsync(undefined);
}
const fsNode = (path: string) => getServer().map((server) => new FileSystemNode(server, path));
const fileExists = ref<boolean>(false);
const updateFileExists = () => {
return fsNode(tempDevice.value.filePath)
.andThen((node) => node.exists({superuser: "try"})
.map((exists) => fileExists.value = exists));
}
watchEffect(updateFileExists);
const actions = wrapActions({createDevice, createFileIOPrompt});
const validationScope = new ValidationScope();
Expand All @@ -147,19 +169,25 @@
return validationSuccess();
});
const { validationResult: filePathValidationResult } = validationScope.useValidator(async () => {
const { validationResult: filePathValidationResult, triggerUpdate: updateFileValidator } = validationScope.useValidator(async () => {
if (!tempDevice.value.filePath) {
return validationError("Device path is required.");
}
const path = new Path(tempDevice.value.filePath);
const buttonActions: ValidationResultAction[] =
[
{
label: _("Create now"),
callback: async () => {
await actions.createFileIOPrompt();
}
}
];
const fileExists = await getServer()
.andThen((server) => path.existsOn(server))
.unwrapOr(false);
await updateFileExists();
if (!fileExists) {
return validationError("Device path does not exist.");
if (!fileExists.value) {
return tempDevice.value.deviceType === DeviceType.BlockIO ? validationError("Device path does not exist.") : validationError("Device path does not exist.", buttonActions);
}
return validationSuccess();
Expand Down
2 changes: 1 addition & 1 deletion houston-common

0 comments on commit c5e6732

Please sign in to comment.