Latest changes from Harvester master - a537c1ae38eb7030542ac371f24ae3336cd9d422

Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
Francesco Torchia
2024-10-23 17:01:40 +02:00
parent 2ca86930ad
commit 118aaf16b7
35 changed files with 1484 additions and 93 deletions
@@ -0,0 +1,129 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import { STATE, NAME, AGE } from '@shell/config/table-headers';
import { allSettled } from '../../utils/promise';
import { BACKUP_TYPE } from '../../config/types';
import { HCI } from '../../types';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
export default {
name: 'BackupList',
components: { ResourceTable },
props: {
id: {
type: String,
required: true,
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
const hash = await allSettled({ backups: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.BACKUP }) });
this.rows = hash.backups;
},
data() {
const inStore = this.$store.getters['currentProduct'].inStore;
const schema = this.$store.getters[`${ inStore }/schemaFor`](HCI.BACKUP);
return {
rows: [],
schema
};
},
computed: {
headers() {
const cols = [
STATE,
{
...NAME,
width: 400
},
{
name: 'targetVM',
labelKey: 'tableHeaders.targetVm',
value: 'attachVM',
align: 'left',
sort: 'attachVM',
formatter: 'AttachVMWithName'
},
{
name: 'backupTarget',
labelKey: 'tableHeaders.backupTarget',
value: 'backupTarget',
sort: 'backupTarget',
align: 'left',
formatter: 'HarvesterBackupTargetValidation'
},
{
name: 'readyToUse',
labelKey: 'tableHeaders.readyToUse',
value: 'status.readyToUse',
sort: 'status.readyToUse',
align: 'center',
formatter: 'Checked',
},
];
if (this.hasBackupProgresses) {
cols.push({
name: 'backupProgress',
labelKey: 'tableHeaders.progress',
value: 'backupProgress',
sort: 'backupProgress',
align: 'left',
formatter: 'HarvesterBackupProgressBar',
});
}
cols.push(AGE);
return cols;
},
hasBackupProgresses() {
return !!this.rows.find(R => R.status?.progress !== undefined);
},
filteredRows() {
let r = this.rows.filter(row => row.spec?.type === BACKUP_TYPE.BACKUP);
if (this.id) {
r = r.filter(backup => backup.metadata.annotations?.[HCI_ANNOTATIONS.SVM_BACKUP_ID] === this.id);
}
return r;
},
},
};
</script>
<template>
<ResourceTable
v-bind="$attrs"
:headers="headers"
:groupable="false"
:rows="filteredRows"
:schema="schema"
key-field="_key"
default-sort-by="age"
>
<template #col:name="{row}">
<td>
<span>
<router-link
v-if="row?.status?.source"
:to="row.detailLocation"
>
{{ row.nameDisplay }}
</router-link>
<span v-else>
{{ row.nameDisplay }}
</span>
</span>
</td>
</template>
</ResourceTable>
</template>
@@ -0,0 +1,97 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import { STATE, NAME, AGE } from '@shell/config/table-headers';
import { allSettled } from '../../utils/promise';
import { BACKUP_TYPE } from '../../config/types';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { HCI } from '../../types';
import { schema } from '../../list/harvesterhci.io.vmsnapshot';
export default {
name: 'SnapshotList',
components: { ResourceTable },
props: {
id: {
type: String,
required: true,
},
},
async fetch() {
const hash = await allSettled({ backups: this.$store.dispatch('harvester/findAll', { type: HCI.BACKUP }) });
this.rows = hash.backups;
},
data() {
return {
rows: [],
schema
};
},
computed: {
headers() {
return [
STATE,
NAME,
{
name: 'targetVM',
labelKey: 'tableHeaders.targetVm',
value: 'attachVM',
align: 'left',
formatter: 'AttachVMWithName'
},
{
name: 'readyToUse',
labelKey: 'tableHeaders.readyToUse',
value: 'status.readyToUse',
align: 'center',
formatter: 'Checked',
},
AGE
];
},
filteredRows() {
let r = this.rows.filter(row => row.spec?.type === BACKUP_TYPE.SNAPSHOT);
if (this.id) {
r = r.filter(row => row.metadata.annotations?.[HCI_ANNOTATIONS.SVM_BACKUP_ID] === this.id);
}
return r;
},
},
};
</script>
<template>
<ResourceTable
v-bind="$attrs"
:headers="headers"
:groupable="false"
:rows="filteredRows"
:schema="schema"
key-field="_key"
default-sort-by="age"
>
<template #col:name="{row}">
<td>
<span>
<router-link
v-if="row?.status?.source"
:to="row.detailLocation"
>
{{ row.nameDisplay }}
</router-link>
<span v-else>
{{ row.nameDisplay }}
</span>
</span>
</td>
</template>
</ResourceTable>
</template>
@@ -0,0 +1,125 @@
<script>
import LabelValue from '@shell/components/LabelValue';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { BACKUP_TYPE } from '../../config/types';
import BackupList from './BackupList';
import SnapshotList from './SnapshotList';
import cronstrue from 'cronstrue';
export default {
name: 'ScheduleVmBackupDetail',
components: {
BackupList,
SnapshotList,
Tab,
Tabbed,
LabelValue,
},
props: {
value: {
type: Object,
required: true,
},
},
computed: {
isBackup() {
return this.value.spec.vmbackup.type === BACKUP_TYPE.BACKUP;
},
isSnapshot() {
return this.value.spec.vmbackup.type === BACKUP_TYPE.SNAPSHOT;
},
cronExpression() {
let cronHint = '';
try {
cronHint = cronstrue.toString(this.value.spec.cron, { verbose: true });
} catch (e) {
cronHint = this.t('generic.invalidCron');
}
return cronHint ? `${ this.value.spec.cron } (${ cronHint })` : this.value.spec.cron;
}
}
};
</script>
<template>
<Tabbed v-bind="$attrs" class="mt-15" :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.virtualMachine.detail.tabs.basics')"
class="bordered-table"
:weight="99"
>
<div class="row">
<div class="col span-6 mb-20">
<LabelValue
:name="t('harvester.schedule.detail.namespace')"
:value="value.metadata.namespace"
/>
</div>
<div class="col span-6 mb-20">
<LabelValue
:name="t('harvester.schedule.detail.sourceVM')"
:value="value.spec.vmbackup.source.name"
/>
</div>
</div>
<div class="row">
<div class="col span-6 mb-20">
<LabelValue
:name="t('harvester.schedule.cron')"
:value="cronExpression"
/>
</div>
<div class="col span-6 mb-20">
<LabelValue
:name="t('harvester.schedule.scheduleType')"
:value="value.spec.vmbackup.type"
/>
</div>
</div>
<div class="row">
<div class="col span-6 mb-20">
<LabelValue
:name="t('harvester.schedule.retain.label')"
:value="value.spec.retain"
/>
</div>
<div class="col span-6 mb-20">
<LabelValue
:name="t('harvester.schedule.maxFailure.label')"
:value="value.spec.maxFailure"
/>
</div>
</div>
</Tab>
<Tab
v-if="isBackup"
name="backups"
:label="t('harvester.schedule.tabs.backups')"
:weight="89"
class="bordered-table"
>
<BackupList :id="value.id" />
</Tab>
<Tab
v-if="isSnapshot"
name="snapshots"
:label="t('harvester.schedule.tabs.snapshots')"
:weight="79"
class="bordered-table"
>
<SnapshotList :id="value.id" />
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
.error {
color: var(--error);
}
</style>
@@ -127,29 +127,29 @@ export default {
<Tabbed v-if="spec" :side-tabs="true" @changed="onTabChanged">
<Tab name="Basics" :label="t('harvester.virtualMachine.detail.tabs.basics')">
<div class="row">
<div class="col span-6">
<div class="col span-6 mb-20">
<LabelValue :name="t('harvester.virtualMachine.detail.details.name')" :value="name" />
</div>
<div class="col span-6">
<div class="col span-6 mb-20">
<LabelValue :name="t('harvester.fields.image')" :value="imageName" />
</div>
</div>
<div class="row">
<div class="col span-6">
<div class="col span-6 mb-20">
<LabelValue :name="t('harvester.virtualMachine.detail.details.hostname')" :value="hostname" />
</div>
<div class="col span-6">
<div class="col span-6 mb-20">
<LabelValue :name="t('harvester.virtualMachine.input.MachineType')" :value="machineType" />
</div>
</div>
<CpuMemory :cpu="cpu" :mode="mode" :memory="memory" />
<div class="mb-20">
<CpuMemory :cpu="cpu" :mode="mode" :memory="memory" />
</div>
<div class="row">
<div class="col span-6">
<div class="col span-6 mb-20">
<LabelValue :name="t('harvester.virtualMachine.detail.details.bootOrder')">
<template #value>
<div>
@@ -162,7 +162,7 @@ export default {
</template>
</LabelValue>
</div>
<div class="col span-6">
<div class="col span-6 mb-20">
<LabelValue :name="t('harvester.virtualMachine.detail.details.CDROMs')">
<template #value>
<div>
@@ -180,7 +180,6 @@ export default {
</div>
</div>
</Tab>
<Tab
name="volume"
:label="t('harvester.tab.volume')"