feat(lite/pool/VMs): ability to export selected VMs (#7174)
This commit is contained in:
parent
4351aad312
commit
511908bb7d
@ -5,6 +5,7 @@
|
|||||||
- Explicit error if users attempt to connect from a slave host (PR [#7110](https://github.com/vatesfr/xen-orchestra/pull/7110))
|
- Explicit error if users attempt to connect from a slave host (PR [#7110](https://github.com/vatesfr/xen-orchestra/pull/7110))
|
||||||
- More compact UI (PR [#7159](https://github.com/vatesfr/xen-orchestra/pull/7159))
|
- More compact UI (PR [#7159](https://github.com/vatesfr/xen-orchestra/pull/7159))
|
||||||
- Fix dashboard host patches list (PR [#7169](https://github.com/vatesfr/xen-orchestra/pull/7169))
|
- Fix dashboard host patches list (PR [#7169](https://github.com/vatesfr/xen-orchestra/pull/7169))
|
||||||
|
- Ability to export selected VMs (PR [#7174](https://github.com/vatesfr/xen-orchestra/pull/7174))
|
||||||
|
|
||||||
## **0.1.5** (2023-11-07)
|
## **0.1.5** (2023-11-07)
|
||||||
|
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<UiModal>
|
||||||
|
<FormModalLayout :icon="faDisplay">
|
||||||
|
<template #title>
|
||||||
|
{{ $t("export-n-vms-manually", { n: labelWithUrl.length }) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{ $t("export-vms-manually-information") }}
|
||||||
|
</p>
|
||||||
|
<ul class="list">
|
||||||
|
<li v-for="({ url, label }, index) in labelWithUrl" :key="index">
|
||||||
|
<a :href="url.href" target="_blank">
|
||||||
|
{{ label }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<template #buttons>
|
||||||
|
<ModalDeclineButton />
|
||||||
|
</template>
|
||||||
|
</FormModalLayout>
|
||||||
|
</UiModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import FormModalLayout from "@/components/ui/modals/layouts/FormModalLayout.vue";
|
||||||
|
import ModalDeclineButton from "@/components/ui/modals/ModalDeclineButton.vue";
|
||||||
|
import UiModal from "@/components/ui/modals/UiModal.vue";
|
||||||
|
import type { XenApiVm } from "@/libs/xen-api/xen-api.types";
|
||||||
|
import { useVmCollection } from "@/stores/xen-api/vm.store";
|
||||||
|
import { faDisplay } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
blockedUrls: URL[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { getByOpaqueRef } = useVmCollection();
|
||||||
|
|
||||||
|
const labelWithUrl = computed(() =>
|
||||||
|
props.blockedUrls.map((url) => {
|
||||||
|
const ref = url.searchParams.get("ref") as XenApiVm["$ref"];
|
||||||
|
return {
|
||||||
|
url: url,
|
||||||
|
label: getByOpaqueRef(ref)?.name_label ?? ref,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="postcss" scoped>
|
||||||
|
.list {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
65
@xen-orchestra/lite/src/components/modals/VmExportModal.vue
Normal file
65
@xen-orchestra/lite/src/components/modals/VmExportModal.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<UiModal @submit.prevent="handleSubmit">
|
||||||
|
<FormModalLayout :icon="faDisplay">
|
||||||
|
<template #title>
|
||||||
|
{{ $t("export-n-vms", { n: vmRefs.length }) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<FormInputWrapper
|
||||||
|
light
|
||||||
|
learn-more-url="https://xcp-ng.org/blog/2018/12/19/zstd-compression-for-xcp-ng/"
|
||||||
|
:label="$t('select-compression')"
|
||||||
|
>
|
||||||
|
<FormSelect v-model="compressionType">
|
||||||
|
<option
|
||||||
|
v-for="key in Object.keys(VM_COMPRESSION_TYPE)"
|
||||||
|
:key="key"
|
||||||
|
:value="
|
||||||
|
VM_COMPRESSION_TYPE[key as keyof typeof VM_COMPRESSION_TYPE]
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $t(key.toLowerCase()) }}
|
||||||
|
</option>
|
||||||
|
</FormSelect>
|
||||||
|
</FormInputWrapper>
|
||||||
|
|
||||||
|
<template #buttons>
|
||||||
|
<ModalDeclineButton />
|
||||||
|
<ModalApproveButton>
|
||||||
|
{{ $t("export-n-vms", { n: vmRefs.length }) }}
|
||||||
|
</ModalApproveButton>
|
||||||
|
</template>
|
||||||
|
</FormModalLayout>
|
||||||
|
</UiModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { faDisplay } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { inject, ref } from "vue";
|
||||||
|
|
||||||
|
import FormInputWrapper from "@/components/form/FormInputWrapper.vue";
|
||||||
|
import FormSelect from "@/components/form/FormSelect.vue";
|
||||||
|
import FormModalLayout from "@/components/ui/modals/layouts/FormModalLayout.vue";
|
||||||
|
import ModalApproveButton from "@/components/ui/modals/ModalApproveButton.vue";
|
||||||
|
import ModalDeclineButton from "@/components/ui/modals/ModalDeclineButton.vue";
|
||||||
|
import UiModal from "@/components/ui/modals/UiModal.vue";
|
||||||
|
import { IK_MODAL } from "@/types/injection-keys";
|
||||||
|
import { useXenApiStore } from "@/stores/xen-api.store";
|
||||||
|
import { VM_COMPRESSION_TYPE } from "@/libs/xen-api/xen-api.enums";
|
||||||
|
|
||||||
|
import type { XenApiVm } from "@/libs/xen-api/xen-api.types";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
vmRefs: XenApiVm["$ref"][];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const modal = inject(IK_MODAL)!;
|
||||||
|
|
||||||
|
const compressionType = ref(VM_COMPRESSION_TYPE.DISABLED);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
const xenApi = useXenApiStore().getXapi();
|
||||||
|
xenApi.vm.export(props.vmRefs, compressionType.value);
|
||||||
|
modal.approve();
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,51 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<MenuItem :icon="faFileExport">
|
<MenuItem
|
||||||
{{ $t("export") }}
|
v-tooltip="
|
||||||
<template #submenu>
|
vmRefs.length > 0 &&
|
||||||
<MenuItem
|
!isSomeExportable &&
|
||||||
v-tooltip="{ content: $t('coming-soon'), placement: 'left' }"
|
$t('no-selected-vm-can-be-exported')
|
||||||
:icon="faDisplay"
|
"
|
||||||
>
|
:icon="faDisplay"
|
||||||
{{ $t("export-vms") }}
|
:disabled="isDisabled"
|
||||||
</MenuItem>
|
@click="openModal"
|
||||||
<MenuItem
|
>
|
||||||
:icon="faCode"
|
{{ $t("export-vms") }}
|
||||||
@click="
|
|
||||||
exportVmsAsJsonFile(vms, `vms_${new Date().toISOString()}.json`)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ $t("export-table-to", { type: ".json" }) }}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
:icon="faFileCsv"
|
|
||||||
@click="exportVmsAsCsvFile(vms, `vms_${new Date().toISOString()}.csv`)"
|
|
||||||
>
|
|
||||||
{{ $t("export-table-to", { type: ".csv" }) }}
|
|
||||||
</MenuItem>
|
|
||||||
</template>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useVmCollection } from "@/stores/xen-api/vm.store";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { exportVmsAsCsvFile, exportVmsAsJsonFile } from "@/libs/vm";
|
import { faDisplay } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
import MenuItem from "@/components/menu/MenuItem.vue";
|
import MenuItem from "@/components/menu/MenuItem.vue";
|
||||||
import {
|
import { DisabledContext } from "@/context";
|
||||||
faCode,
|
import { useContext } from "@/composables/context.composable";
|
||||||
faDisplay,
|
import { useModal } from "@/composables/modal.composable";
|
||||||
faFileCsv,
|
import { useVmCollection } from "@/stores/xen-api/vm.store";
|
||||||
faFileExport,
|
import { VM_OPERATION } from "@/libs/xen-api/xen-api.enums";
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { vTooltip } from "@/directives/tooltip.directive";
|
import { vTooltip } from "@/directives/tooltip.directive";
|
||||||
|
|
||||||
import type { XenApiVm } from "@/libs/xen-api/xen-api.types";
|
import type { XenApiVm } from "@/libs/xen-api/xen-api.types";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{ vmRefs: XenApiVm["$ref"][] }>();
|
||||||
vmRefs: XenApiVm["$ref"][];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const { getByOpaqueRef: getVm } = useVmCollection();
|
const { getByOpaqueRefs, areSomeOperationAllowed } = useVmCollection();
|
||||||
const vms = computed(() =>
|
|
||||||
props.vmRefs.map(getVm).filter((vm): vm is XenApiVm => vm !== undefined)
|
const isParentDisabled = useContext(DisabledContext);
|
||||||
|
|
||||||
|
const isSomeExportable = computed(() =>
|
||||||
|
getByOpaqueRefs(props.vmRefs).some((vm) =>
|
||||||
|
areSomeOperationAllowed(vm, VM_OPERATION.EXPORT)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isDisabled = computed(
|
||||||
|
() => isParentDisabled.value || !isSomeExportable.value
|
||||||
|
);
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
useModal(() => import("@/components/modals/VmExportModal.vue"), {
|
||||||
|
vmRefs: props.vmRefs,
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<MenuItem :icon="faFileExport">
|
||||||
|
{{ $t("export") }}
|
||||||
|
<template #submenu>
|
||||||
|
<VmActionExportItem :vmRefs="vmRefs" />
|
||||||
|
<MenuItem
|
||||||
|
:icon="faCode"
|
||||||
|
@click="
|
||||||
|
exportVmsAsJsonFile(vms, `vms_${new Date().toISOString()}.json`)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ $t("export-table-to", { type: ".json" }) }}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
:icon="faFileCsv"
|
||||||
|
@click="exportVmsAsCsvFile(vms, `vms_${new Date().toISOString()}.csv`)"
|
||||||
|
>
|
||||||
|
{{ $t("export-table-to", { type: ".csv" }) }}
|
||||||
|
</MenuItem>
|
||||||
|
</template>
|
||||||
|
</MenuItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVmCollection } from "@/stores/xen-api/vm.store";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { exportVmsAsCsvFile, exportVmsAsJsonFile } from "@/libs/vm";
|
||||||
|
import MenuItem from "@/components/menu/MenuItem.vue";
|
||||||
|
import VmActionExportItem from "@/components/vm/VmActionItems/VmActionExportItem.vue";
|
||||||
|
import {
|
||||||
|
faCode,
|
||||||
|
faFileCsv,
|
||||||
|
faFileExport,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import type { XenApiVm } from "@/libs/xen-api/xen-api.types";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
vmRefs: XenApiVm["$ref"][];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { getByOpaqueRef: getVm } = useVmCollection();
|
||||||
|
const vms = computed(() =>
|
||||||
|
props.vmRefs.map(getVm).filter((vm): vm is XenApiVm => vm !== undefined)
|
||||||
|
);
|
||||||
|
</script>
|
@ -21,7 +21,7 @@
|
|||||||
{{ $t("edit-config") }}
|
{{ $t("edit-config") }}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<VmActionSnapshotItem :vm-refs="selectedRefs" />
|
<VmActionSnapshotItem :vm-refs="selectedRefs" />
|
||||||
<VmActionExportItem :vm-refs="selectedRefs" />
|
<VmActionExportItems :vm-refs="selectedRefs" />
|
||||||
<VmActionDeleteItem :vm-refs="selectedRefs" />
|
<VmActionDeleteItem :vm-refs="selectedRefs" />
|
||||||
</AppMenu>
|
</AppMenu>
|
||||||
</template>
|
</template>
|
||||||
@ -32,7 +32,7 @@ import MenuItem from "@/components/menu/MenuItem.vue";
|
|||||||
import UiButton from "@/components/ui/UiButton.vue";
|
import UiButton from "@/components/ui/UiButton.vue";
|
||||||
import VmActionCopyItem from "@/components/vm/VmActionItems/VmActionCopyItem.vue";
|
import VmActionCopyItem from "@/components/vm/VmActionItems/VmActionCopyItem.vue";
|
||||||
import VmActionDeleteItem from "@/components/vm/VmActionItems/VmActionDeleteItem.vue";
|
import VmActionDeleteItem from "@/components/vm/VmActionItems/VmActionDeleteItem.vue";
|
||||||
import VmActionExportItem from "@/components/vm/VmActionItems/VmActionExportItem.vue";
|
import VmActionExportItems from "@/components/vm/VmActionItems/VmActionExportItems.vue";
|
||||||
import VmActionMigrateItem from "@/components/vm/VmActionItems/VmActionMigrateItem.vue";
|
import VmActionMigrateItem from "@/components/vm/VmActionItems/VmActionMigrateItem.vue";
|
||||||
import VmActionPowerStateItems from "@/components/vm/VmActionItems/VmActionPowerStateItems.vue";
|
import VmActionPowerStateItems from "@/components/vm/VmActionItems/VmActionPowerStateItems.vue";
|
||||||
import VmActionSnapshotItem from "@/components/vm/VmActionItems/VmActionSnapshotItem.vue";
|
import VmActionSnapshotItem from "@/components/vm/VmActionItems/VmActionSnapshotItem.vue";
|
||||||
|
@ -491,3 +491,9 @@ export enum CERTIFICATE_TYPE {
|
|||||||
HOST = "host",
|
HOST = "host",
|
||||||
HOST_INTERNAL = "host_internal",
|
HOST_INTERNAL = "host_internal",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum VM_COMPRESSION_TYPE {
|
||||||
|
DISABLED = "false",
|
||||||
|
GZIP = "true",
|
||||||
|
ZSTD = "zstd",
|
||||||
|
}
|
||||||
|
@ -18,6 +18,8 @@ import type {
|
|||||||
import { buildXoObject, typeToRawType } from "@/libs/xen-api/xen-api.utils";
|
import { buildXoObject, typeToRawType } from "@/libs/xen-api/xen-api.utils";
|
||||||
import { JSONRPCClient } from "json-rpc-2.0";
|
import { JSONRPCClient } from "json-rpc-2.0";
|
||||||
import { castArray } from "lodash-es";
|
import { castArray } from "lodash-es";
|
||||||
|
import type { VM_COMPRESSION_TYPE } from "@/libs/xen-api/xen-api.enums";
|
||||||
|
import { useModal } from "@/composables/modal.composable";
|
||||||
|
|
||||||
export default class XenApi {
|
export default class XenApi {
|
||||||
private client: JSONRPCClient;
|
private client: JSONRPCClient;
|
||||||
@ -27,10 +29,12 @@ export default class XenApi {
|
|||||||
Set<(...args: any[]) => void>
|
Set<(...args: any[]) => void>
|
||||||
>();
|
>();
|
||||||
private fromToken: string | undefined;
|
private fromToken: string | undefined;
|
||||||
|
private hostUrl: string;
|
||||||
|
|
||||||
constructor(hostUrl: string) {
|
constructor(hostUrl: string) {
|
||||||
|
this.hostUrl = hostUrl;
|
||||||
this.client = new JSONRPCClient(async (request) => {
|
this.client = new JSONRPCClient(async (request) => {
|
||||||
const response = await fetch(`${hostUrl}/jsonrpc`, {
|
const response = await fetch(`${this.hostUrl}/jsonrpc`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
body: JSON.stringify(request),
|
body: JSON.stringify(request),
|
||||||
@ -380,6 +384,36 @@ export default class XenApi {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
export: (vmRefs: VmRefs, compression: VM_COMPRESSION_TYPE) => {
|
||||||
|
const blockedUrls: URL[] = [];
|
||||||
|
|
||||||
|
castArray(vmRefs).forEach((vmRef) => {
|
||||||
|
const url = new URL(this.hostUrl);
|
||||||
|
url.pathname = "/export/";
|
||||||
|
url.search = new URLSearchParams({
|
||||||
|
session_id: this.sessionId!,
|
||||||
|
ref: vmRef,
|
||||||
|
use_compression: compression,
|
||||||
|
}).toString();
|
||||||
|
|
||||||
|
const _window = window.open(url.href, "_blank");
|
||||||
|
if (_window === null) {
|
||||||
|
blockedUrls.push(url);
|
||||||
|
} else {
|
||||||
|
URL.revokeObjectURL(url.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockedUrls.length > 0) {
|
||||||
|
const { onClose } = useModal(
|
||||||
|
() => import("@/components/modals/VmExportBlockedUrlsModal.vue"),
|
||||||
|
{ blockedUrls }
|
||||||
|
);
|
||||||
|
onClose(() =>
|
||||||
|
blockedUrls.forEach((url) => URL.revokeObjectURL(url.toString()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"delete-vms": "Delete 1 VM | Delete {n} VMs",
|
"delete-vms": "Delete 1 VM | Delete {n} VMs",
|
||||||
"descending": "descending",
|
"descending": "descending",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
|
"disabled": "Disabled",
|
||||||
"display": "Display",
|
"display": "Display",
|
||||||
"do-you-have-needs": "You have needs and/or expectations? Let us know",
|
"do-you-have-needs": "You have needs and/or expectations? Let us know",
|
||||||
"documentation": "Documentation",
|
"documentation": "Documentation",
|
||||||
@ -51,8 +52,11 @@
|
|||||||
"error-no-data": "Error, can't collect data.",
|
"error-no-data": "Error, can't collect data.",
|
||||||
"error-occurred": "An error has occurred",
|
"error-occurred": "An error has occurred",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
|
"export-n-vms": "Export 1 VM | Export {n} VMs",
|
||||||
|
"export-n-vms-manually": "Export 1 VM manually | Export {n} VMs manually",
|
||||||
"export-table-to": "Export table to {type}",
|
"export-table-to": "Export table to {type}",
|
||||||
"export-vms": "Export VMs",
|
"export-vms": "Export VMs",
|
||||||
|
"export-vms-manually-information": "Some VM exports were not able to start automatically, probably due to your browser settings. To export them, you should click on each one. (Alternatively, copy the link as well.)",
|
||||||
"fetching-fresh-data": "Fetching fresh data",
|
"fetching-fresh-data": "Fetching fresh data",
|
||||||
"filter": {
|
"filter": {
|
||||||
"comparison": {
|
"comparison": {
|
||||||
@ -78,6 +82,7 @@
|
|||||||
"fullscreen": "Fullscreen",
|
"fullscreen": "Fullscreen",
|
||||||
"fullscreen-leave": "Leave fullscreen",
|
"fullscreen-leave": "Leave fullscreen",
|
||||||
"go-back": "Go back",
|
"go-back": "Go back",
|
||||||
|
"gzip": "gzip",
|
||||||
"here": "Here",
|
"here": "Here",
|
||||||
"hosts": "Hosts",
|
"hosts": "Hosts",
|
||||||
"keep-me-logged": "Keep me logged in",
|
"keep-me-logged": "Keep me logged in",
|
||||||
@ -104,6 +109,7 @@
|
|||||||
"news": "News",
|
"news": "News",
|
||||||
"news-name": "{name} news",
|
"news-name": "{name} news",
|
||||||
"no-alarm-triggered": "No alarm triggered",
|
"no-alarm-triggered": "No alarm triggered",
|
||||||
|
"no-selected-vm-can-be-exported": "No selected VM can be exported",
|
||||||
"no-selected-vm-can-be-migrated": "No selected VM can be migrated",
|
"no-selected-vm-can-be-migrated": "No selected VM can be migrated",
|
||||||
"no-tasks": "No tasks",
|
"no-tasks": "No tasks",
|
||||||
"not-found": "Not found",
|
"not-found": "Not found",
|
||||||
@ -139,6 +145,7 @@
|
|||||||
},
|
},
|
||||||
"resume": "Resume",
|
"resume": "Resume",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
|
"select-compression": "Select a compression",
|
||||||
"select-destination-host": "Select a destination host",
|
"select-destination-host": "Select a destination host",
|
||||||
"selected-vms-in-execution": "Some selected VMs are running",
|
"selected-vms-in-execution": "Some selected VMs are running",
|
||||||
"send-ctrl-alt-del": "Send Ctrl+Alt+Del",
|
"send-ctrl-alt-del": "Send Ctrl+Alt+Del",
|
||||||
@ -180,5 +187,6 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"vm-is-running": "The VM is running",
|
"vm-is-running": "The VM is running",
|
||||||
"vms": "VMs",
|
"vms": "VMs",
|
||||||
"xo-lite-under-construction": "XOLite is under construction"
|
"xo-lite-under-construction": "XOLite is under construction",
|
||||||
|
"zstd": "zstd"
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"delete-vms": "Supprimer 1 VM | Supprimer {n} VMs",
|
"delete-vms": "Supprimer 1 VM | Supprimer {n} VMs",
|
||||||
"descending": "descendant",
|
"descending": "descendant",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
|
"disabled": "Désactivé",
|
||||||
"display": "Affichage",
|
"display": "Affichage",
|
||||||
"do-you-have-needs": "Vous avez des besoins et/ou des attentes ? Faites le nous savoir",
|
"do-you-have-needs": "Vous avez des besoins et/ou des attentes ? Faites le nous savoir",
|
||||||
"documentation": "Documentation",
|
"documentation": "Documentation",
|
||||||
@ -51,8 +52,11 @@
|
|||||||
"error-no-data": "Erreur, impossible de collecter les données.",
|
"error-no-data": "Erreur, impossible de collecter les données.",
|
||||||
"error-occurred": "Une erreur est survenue",
|
"error-occurred": "Une erreur est survenue",
|
||||||
"export": "Exporter",
|
"export": "Exporter",
|
||||||
|
"export-n-vms": "Exporter 1 VM | Exporter {n} VMs",
|
||||||
|
"export-n-vms-manually": "Exporter 1 VM manuellement | Exporter {n} VMs manuellement",
|
||||||
"export-table-to": "Exporter le tableau en {type}",
|
"export-table-to": "Exporter le tableau en {type}",
|
||||||
"export-vms": "Exporter les VMs",
|
"export-vms": "Exporter les VMs",
|
||||||
|
"export-vms-manually-information": "Certaines exportations de VMs n'ont pas pu démarrer automatiquement, peut-être en raison des paramètres du navigateur. Pour les exporter, vous devrez cliquer sur chacune d'entre elles. (Ou copier le lien.)",
|
||||||
"fetching-fresh-data": "Récupération de données à jour",
|
"fetching-fresh-data": "Récupération de données à jour",
|
||||||
"filter": {
|
"filter": {
|
||||||
"comparison": {
|
"comparison": {
|
||||||
@ -78,6 +82,7 @@
|
|||||||
"fullscreen": "Plein écran",
|
"fullscreen": "Plein écran",
|
||||||
"fullscreen-leave": "Quitter plein écran",
|
"fullscreen-leave": "Quitter plein écran",
|
||||||
"go-back": "Revenir en arrière",
|
"go-back": "Revenir en arrière",
|
||||||
|
"gzip": "gzip",
|
||||||
"here": "Ici",
|
"here": "Ici",
|
||||||
"hosts": "Hôtes",
|
"hosts": "Hôtes",
|
||||||
"keep-me-logged": "Rester connecté",
|
"keep-me-logged": "Rester connecté",
|
||||||
@ -104,6 +109,7 @@
|
|||||||
"news": "Actualités",
|
"news": "Actualités",
|
||||||
"news-name": "Actualités {name}",
|
"news-name": "Actualités {name}",
|
||||||
"no-alarm-triggered": "Aucune alarme déclenchée",
|
"no-alarm-triggered": "Aucune alarme déclenchée",
|
||||||
|
"no-selected-vm-can-be-exported": "Aucune VM sélectionnée ne peut être exportée",
|
||||||
"no-selected-vm-can-be-migrated": "Aucune VM sélectionnée ne peut être migrée",
|
"no-selected-vm-can-be-migrated": "Aucune VM sélectionnée ne peut être migrée",
|
||||||
"no-tasks": "Aucune tâche",
|
"no-tasks": "Aucune tâche",
|
||||||
"not-found": "Non trouvé",
|
"not-found": "Non trouvé",
|
||||||
@ -139,6 +145,7 @@
|
|||||||
},
|
},
|
||||||
"resume": "Reprendre",
|
"resume": "Reprendre",
|
||||||
"save": "Enregistrer",
|
"save": "Enregistrer",
|
||||||
|
"select-compression": "Sélectionnez une compression",
|
||||||
"select-destination-host": "Sélectionnez un hôte de destination",
|
"select-destination-host": "Sélectionnez un hôte de destination",
|
||||||
"selected-vms-in-execution": "Certaines VMs sélectionnées sont en cours d'exécution",
|
"selected-vms-in-execution": "Certaines VMs sélectionnées sont en cours d'exécution",
|
||||||
"send-ctrl-alt-del": "Envoyer Ctrl+Alt+Suppr",
|
"send-ctrl-alt-del": "Envoyer Ctrl+Alt+Suppr",
|
||||||
@ -180,5 +187,6 @@
|
|||||||
"version": "Version",
|
"version": "Version",
|
||||||
"vm-is-running": "La VM est en cours d'exécution",
|
"vm-is-running": "La VM est en cours d'exécution",
|
||||||
"vms": "VMs",
|
"vms": "VMs",
|
||||||
"xo-lite-under-construction": "XOLite est en construction"
|
"xo-lite-under-construction": "XOLite est en construction",
|
||||||
|
"zstd": "zstd"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user