diff --git a/pkg/harvester/components/BootOrderCard.vue b/pkg/harvester/components/BootOrderCard.vue new file mode 100644 index 00000000000..66f3a74b1fd --- /dev/null +++ b/pkg/harvester/components/BootOrderCard.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/pkg/harvester/detail/kubevirt.io.virtualmachine/index.vue b/pkg/harvester/detail/kubevirt.io.virtualmachine/index.vue index e3032ffacd6..f7faae7e902 100644 --- a/pkg/harvester/detail/kubevirt.io.virtualmachine/index.vue +++ b/pkg/harvester/detail/kubevirt.io.virtualmachine/index.vue @@ -13,6 +13,7 @@ import { allDashboardsExist } from '@shell/utils/grafana'; import CloudConfig from '../../edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig'; import Volume from '../../edit/kubevirt.io.virtualmachine/VirtualMachineVolume'; import Network from '../../edit/kubevirt.io.virtualmachine/VirtualMachineNetwork'; +import BootOrder from '../../edit/kubevirt.io.virtualmachine/VirtualMachineBootOrder'; import NodeScheduling from '@shell/components/form/NodeScheduling'; import PodAffinity from '@shell/components/form/PodAffinity'; import AccessCredentials from '../../edit/kubevirt.io.virtualmachine/VirtualMachineAccessCredentials'; @@ -29,6 +30,7 @@ export default { name: 'VMIDetailsPage', components: { + BootOrder, Tab, Tabbed, Events, @@ -175,6 +177,13 @@ export default { + + + + diff --git a/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue b/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue index 72dd744f2bd..937a80c6b2e 100644 --- a/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue +++ b/pkg/harvester/edit/harvesterhci.io.virtualmachinetemplateversion.vue @@ -15,6 +15,7 @@ import UnitInput from '@shell/components/form/UnitInput'; import Reserved from './kubevirt.io.virtualmachine/VirtualMachineReserved'; import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume'; import Network from './kubevirt.io.virtualmachine/VirtualMachineNetwork'; +import BootOrder from './kubevirt.io.virtualmachine/VirtualMachineBootOrder'; import CpuMemory from './kubevirt.io.virtualmachine/VirtualMachineCpuMemory'; import CloudConfig from './kubevirt.io.virtualmachine/VirtualMachineCloudConfig'; import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey'; @@ -33,6 +34,7 @@ export default { name: 'HarvesterEditVMTemplate', components: { + BootOrder, Tab, SSHKey, Volume, @@ -254,6 +256,13 @@ export default { + + + + +import Vue from 'vue'; +import draggable from 'vuedraggable'; +import BootOrderCard from '../../../components/BootOrderCard'; + +import { _VIEW } from '@shell/config/query-params'; +import { clone } from '@shell/utils/object'; +import { BOOT_ORDER_TYPE } from '../../../mixins/harvester-vm'; + +function getId(elem) { + return `${ elem.type }_${ elem.index }_${ elem.id }`; +} + +export default { + components: { + draggable, + BootOrderCard + }, + + props: { + value: { + type: Array, + default: () => { + return []; + } + }, + + mode: { + type: String, + required: true + }, + }, + + data() { + return { + ordered: [], + unordered: [], + bootOrders: [], + hideLast: false, + showNext: null, + hideButtons: null, + }; + }, + + computed: { + isView() { + return this.mode === _VIEW; + }, + }, + + watch: { + value: { + handler(neu) { + const value = clone(neu); + + const ordered = value.filter(f => f.bootOrder).sort((a, b) => a.bootOrder - b.bootOrder); + + const oldUnorderedIds = this.unordered.map(getId); + const unordered = value.filter(f => !f.bootOrder).sort((a, b) => oldUnorderedIds.indexOf(getId(a)) - oldUnorderedIds.indexOf(getId(b))); + + const bootOrders = ordered.map(r => r.bootOrder); + + Vue.set(this, 'ordered', ordered); + Vue.set(this, 'unordered', unordered); + Vue.set(this, 'bootOrders', bootOrders); + }, + immediate: true, + deep: true, + }, + }, + + methods: { + findElem(elem) { + return this.value.find(f => getId(f) === getId(elem)); + }, + + getNextBootOrder() { + let bootOrder = 1; + + while (this.bootOrders.includes(bootOrder)) { + bootOrder++; + } + + return bootOrder; + }, + + addBootOrder() { + this.bootOrders = [ + ...this.bootOrders, + this.getNextBootOrder() + ].sort(); + }, + + onMoveOrdered(v) { + if (v.from.id === 'ordered' && v.to.id === 'unordered') { + this.hideLast = true; + } else { + this.hideLast = false; + } + }, + + onMoveUnordered(v) { + if (v.from.id === 'unordered' && v.to.id === 'ordered') { + if (!this.showNext) { + this.showNext = [ + ...this.bootOrders, + this.getNextBootOrder() + ].sort(); + } + } else { + this.showNext = null; + } + }, + + onMouseDownOrdered(index) { + this.hideButtons = index; + }, + + onMouseUpOrdered() { + this.hideButtons = null; + }, + + swapOrdered(index1, index2) { + const ordered = clone(this.ordered); + + const temp = ordered[index1]; + + ordered[index1] = ordered[index2]; + ordered[index2] = temp; + + this.ordered = ordered; + + this.update(); + }, + + swapToOrdered() { + this.addBootOrder(); + this.ordered.push(this.unordered[0]); + this.unordered.splice(0, 1); + + this.update(); + }, + + swapToOrderedAll() { + this.unordered.forEach(this.addBootOrder); + this.ordered.push(...this.unordered); + this.unordered = []; + + this.update(); + }, + + swapToUnordered() { + this.bootOrders.pop(); + + this.unordered.push(this.ordered[this.ordered.length - 1]); + this.ordered.splice(this.ordered.length - 1, 1); + + this.update(); + }, + + swapToUnorderedAll() { + this.bootOrders = []; + + this.unordered.push(...this.ordered); + this.ordered = []; + + this.update(); + }, + + dragEndOrdered(v) { + if (v.to.id === 'unordered') { + this.bootOrders.pop(); + } + + this.hideLast = false; + this.showNext = null; + this.hideButtons = null; + + this.update(); + }, + + dragEndUnordered(v) { + if (v.to.id === 'ordered') { + this.addBootOrder(); + } + this.hideLast = false; + this.showNext = null; + + this.update(); + }, + + updateRows(bootOrderGetter) { + return (r, index) => { + const el = this.findElem(r); + + el.bootOrder = bootOrderGetter(index); + + return el; + }; + }, + + update() { + const rows = [ + ...this.ordered.map(this.updateRows(i => this.bootOrders[i])), + ...this.unordered.map(this.updateRows(() => undefined)) + ]; + + const diskRows = rows.filter(f => f.type === BOOT_ORDER_TYPE.DISK); + const networkRows = rows.filter(f => f.type === BOOT_ORDER_TYPE.INTERFACE); + + this.$emit('input', { + diskRows, + networkRows + }); + }, + } +}; + + + + + diff --git a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue index 91781a59348..fdfec26cca8 100644 --- a/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue +++ b/pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineNetwork/index.vue @@ -1,6 +1,8 @@ @@ -541,13 +548,21 @@ export default { - + + + + + - +