mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 13:11:43 +00:00
Latest changes from harvester/master
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
parent
deeccf3db6
commit
ec3d88aeb7
@ -3,7 +3,7 @@ import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params';
|
|||||||
import Tag from '@shell/components/Tag';
|
import Tag from '@shell/components/Tag';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DiskTags',
|
name: 'Tags',
|
||||||
|
|
||||||
components: { Tag },
|
components: { Tag },
|
||||||
|
|
||||||
|
|||||||
@ -35,13 +35,23 @@ export default {
|
|||||||
version: '',
|
version: '',
|
||||||
enableLogging: true,
|
enableLogging: true,
|
||||||
readyReleaseNote: false,
|
readyReleaseNote: false,
|
||||||
isOpen: false,
|
isOpen: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['currentCluster']),
|
...mapGetters(['currentCluster']),
|
||||||
|
|
||||||
|
latestUpgrade() {
|
||||||
|
return this.upgrade?.find(u => u.isLatestUpgrade);
|
||||||
|
},
|
||||||
|
|
||||||
|
isUpgradeInProgress() {
|
||||||
|
return this.latestUpgrade &&
|
||||||
|
!this.latestUpgrade.isUpgradeSucceeded &&
|
||||||
|
!this.latestUpgrade.isUpgradeFailed;
|
||||||
|
},
|
||||||
|
|
||||||
versionOptions() {
|
versionOptions() {
|
||||||
const versions = this.$store.getters['harvester/all'](HCI.VERSION);
|
const versions = this.$store.getters['harvester/all'](HCI.VERSION);
|
||||||
|
|
||||||
@ -133,7 +143,7 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
v-if="versionOptions.length"
|
v-if="versionOptions.length && !isUpgradeInProgress"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn bg-warning btn-sm"
|
class="btn bg-warning btn-sm"
|
||||||
@click="open"
|
@click="open"
|
||||||
|
|||||||
@ -34,15 +34,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
const categorySettings = this.settings.filter((s) => {
|
const categorySettings = this.filterCategorySettings();
|
||||||
if (this.category !== 'advanced') {
|
|
||||||
return (CATEGORY[this.category] || []).find(item => item === s.id);
|
|
||||||
} else if (this.category === 'advanced') {
|
|
||||||
const allCategory = Object.keys(CATEGORY);
|
|
||||||
|
|
||||||
return !allCategory.some(category => (CATEGORY[category] || []).find(item => item === s.id));
|
|
||||||
}
|
|
||||||
}) || [];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
HCI_SETTING,
|
HCI_SETTING,
|
||||||
@ -52,7 +44,27 @@ export default {
|
|||||||
|
|
||||||
computed: { ...mapGetters({ t: 'i18n/t' }) },
|
computed: { ...mapGetters({ t: 'i18n/t' }) },
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
settings: {
|
||||||
|
deep: true,
|
||||||
|
handler() {
|
||||||
|
this['categorySettings'] = this.filterCategorySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
filterCategorySettings() {
|
||||||
|
return this.settings.filter((s) => {
|
||||||
|
if (this.category !== 'advanced') {
|
||||||
|
return (CATEGORY[this.category] || []).find(item => item === s.id);
|
||||||
|
} else if (this.category === 'advanced') {
|
||||||
|
const allCategory = Object.keys(CATEGORY);
|
||||||
|
|
||||||
|
return !allCategory.some(category => (CATEGORY[category] || []).find(item => item === s.id));
|
||||||
|
}
|
||||||
|
}) || [];
|
||||||
|
},
|
||||||
showActionMenu(e, setting) {
|
showActionMenu(e, setting) {
|
||||||
const actionElement = e.srcElement;
|
const actionElement = e.srcElement;
|
||||||
|
|
||||||
@ -104,7 +116,7 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-for="(setting, i) in categorySettings" class="advanced-setting mb-20" :key="i" >
|
<div v-for="(setting, i) in categorySettings" :key="i">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h1>
|
<h1>
|
||||||
@ -112,6 +124,9 @@ export default {
|
|||||||
<span v-if="setting.customized" class="modified">
|
<span v-if="setting.customized" class="modified">
|
||||||
Modified
|
Modified
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="setting.technicalPreview" v-clean-tooltip="t('advancedSettings.technicalPreview')" class="technical-preview">
|
||||||
|
Technical Preview
|
||||||
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<h2 v-clean-html="t(setting.description, {}, true)">
|
<h2 v-clean-html="t(setting.description, {}, true)">
|
||||||
</h2>
|
</h2>
|
||||||
@ -200,4 +215,12 @@ export default {
|
|||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.technical-preview {
|
||||||
|
margin-left: 10px;
|
||||||
|
border: 1px solid var(--warning);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 2px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
|
import { DOC_LINKS } from '../config/doc-links';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterUpgradeInfo',
|
name: 'HarvesterUpgradeInfo',
|
||||||
@ -16,6 +17,10 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
releaseVersion() {
|
releaseVersion() {
|
||||||
return !!this.version ? `https://github.com/harvester/harvester/releases/tag/${ this.version }` : `https://github.com/harvester/harvester/releases`;
|
return !!this.version ? `https://github.com/harvester/harvester/releases/tag/${ this.version }` : `https://github.com/harvester/harvester/releases`;
|
||||||
|
},
|
||||||
|
|
||||||
|
upgradeLink() {
|
||||||
|
return DOC_LINKS.UPGRADE_URL;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -26,15 +31,14 @@ export default {
|
|||||||
<Banner color="warning">
|
<Banner color="warning">
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ t('harvester.upgradePage.upgradeInfo.warning') }}:</strong>
|
<strong>{{ t('harvester.upgradePage.upgradeInfo.warning') }}:</strong>
|
||||||
<p v-clean-html="t('harvester.upgradePage.upgradeInfo.doc', {}, true)" class="mb-5">
|
<p v-clean-html="t('harvester.upgradePage.upgradeInfo.doc', {url: upgradeLink}, true)" class="mb-5"></p>
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="mb-5">
|
<p class="mb-5">
|
||||||
{{ t('harvester.upgradePage.upgradeInfo.tip') }}
|
{{ t('harvester.upgradePage.upgradeInfo.tip') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="mb-5">
|
<p class="mb-5">
|
||||||
{{ t('harvester.upgradePage.upgradeInfo.moreNotes') }} <a :href="releaseVersion" target="_blank">{{ t('generic.moreInfo') }} </a>
|
{{ t('harvester.upgradePage.upgradeInfo.moreNotes') }} <a :href="releaseVersion" target="_blank">{{ t('generic.moreInfo') }}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Banner>
|
</Banner>
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export default {
|
|||||||
window.open(
|
window.open(
|
||||||
url,
|
url,
|
||||||
'_blank',
|
'_blank',
|
||||||
'toolbars=0,width=900,height=700,left=0,top=0,noreferrer'
|
`toolbars=0,width=${ screen.width - 200 },height=${ screen.height - 200 },left=0,top=0,noreferrer`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
<script>
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AdditionalGuestMemoryOverheadRatio',
|
||||||
|
|
||||||
|
components: { LabeledInput },
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return { ratio: this.value.value || this.value.default };
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
update() {
|
||||||
|
this.value['value'] = this.ratio;
|
||||||
|
},
|
||||||
|
|
||||||
|
useDefault() {
|
||||||
|
this['ratio'] = this.value.default;
|
||||||
|
this.update();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="ratio"
|
||||||
|
:label="t('harvester.setting.ratio')"
|
||||||
|
@update:value="update"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -10,6 +10,7 @@ import Tip from '@shell/components/Tip';
|
|||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { NODE } from '@shell/config/types';
|
import { NODE } from '@shell/config/types';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
|
import { DOC_LINKS } from '../../config/doc-links';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditStorageNetwork',
|
name: 'HarvesterEditStorageNetwork',
|
||||||
@ -85,6 +86,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
storageNetworkExampleLink() {
|
||||||
|
return DOC_LINKS.STORAGE_NETWORK_EXAMPLE;
|
||||||
|
},
|
||||||
clusterNetworkOptions() {
|
clusterNetworkOptions() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
|
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
|
||||||
@ -206,8 +210,8 @@ export default {
|
|||||||
:placeholder="t('harvester.setting.storageNetwork.range.placeholder')"
|
:placeholder="t('harvester.setting.storageNetwork.range.placeholder')"
|
||||||
label-key="harvester.setting.storageNetwork.range.label"
|
label-key="harvester.setting.storageNetwork.range.label"
|
||||||
/>
|
/>
|
||||||
<Tip class="mb-20" icon="icon icon-info" :text="t('harvester.setting.storageNetwork.tip')">
|
<Tip class="mb-20" icon="icon icon-info">
|
||||||
<t k="harvester.setting.storageNetwork.tip" :raw="true" />
|
<t k="harvester.setting.storageNetwork.tip" :raw="true" :url="storageNetworkExampleLink" />
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
<ArrayList
|
<ArrayList
|
||||||
|
|||||||
12
pkg/harvester/config/doc-links.js
Normal file
12
pkg/harvester/config/doc-links.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const pkgJson = require('../package.json');
|
||||||
|
import semver from 'semver';
|
||||||
|
|
||||||
|
const docVersion = `v${ semver.major(pkgJson.version) }.${ semver.minor(pkgJson.version) }`;
|
||||||
|
|
||||||
|
export const DOC_LINKS = {
|
||||||
|
CONSOLE_URL: `https://docs.harvesterhci.io/${ docVersion }/host/`,
|
||||||
|
RANCHER_INTEGRATION_URL: `https://docs.harvesterhci.io/${ docVersion }/rancher/rancher-integration`,
|
||||||
|
STORAGE_NETWORK_EXAMPLE: `https://docs.harvesterhci.io/${ docVersion }/advanced/storagenetwork#configuration-example`,
|
||||||
|
KSMTUNED_MODE: `https://docs.harvesterhci.io/${ docVersion }/host/#ksmtuned-mode`,
|
||||||
|
UPGRADE_URL: `https://docs.harvesterhci.io/${ docVersion }/upgrade/index`
|
||||||
|
};
|
||||||
@ -23,7 +23,7 @@ export const InterfaceOption = [{
|
|||||||
|
|
||||||
export const SOURCE_TYPE = {
|
export const SOURCE_TYPE = {
|
||||||
NEW: 'New',
|
NEW: 'New',
|
||||||
IMAGE: 'VM Image',
|
IMAGE: 'Virtual Machine Image',
|
||||||
ATTACH_VOLUME: 'Existing Volume',
|
ATTACH_VOLUME: 'Existing Volume',
|
||||||
CONTAINER: 'Container'
|
CONTAINER: 'Container'
|
||||||
};
|
};
|
||||||
@ -41,7 +41,14 @@ export const ACCESS_CREDENTIALS = {
|
|||||||
INJECT_SSH: 'sshPublicKey'
|
INJECT_SSH: 'sshPublicKey'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RunStrategys = ['Always', 'RerunOnFailure', 'Manual', 'Halted'];
|
export const runStrategies = ['Always', 'RerunOnFailure', 'Manual', 'Halted'];
|
||||||
|
|
||||||
|
export const maintenanceStrategies = [
|
||||||
|
'Migrate',
|
||||||
|
'ShutdownAndRestartAfterEnable',
|
||||||
|
'ShutdownAndRestartAfterDisable',
|
||||||
|
'Shutdown'
|
||||||
|
];
|
||||||
|
|
||||||
export const VOLUME_DATA_SOURCE_KIND = {
|
export const VOLUME_DATA_SOURCE_KIND = {
|
||||||
VolumeSnapshot: 'VolumeSnapshot',
|
VolumeSnapshot: 'VolumeSnapshot',
|
||||||
@ -55,10 +62,10 @@ export const FLOW_TYPE = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ADD_ONS = {
|
export const ADD_ONS = {
|
||||||
HARVESTER_SEEDER: 'harvester-seeder',
|
HARVESTER_SEEDER: 'harvester-seeder',
|
||||||
PCI_DEVICE_CONTROLLER: 'pcidevices-controller',
|
PCI_DEVICE_CONTROLLER: 'pcidevices-controller',
|
||||||
RANCHER_LOGGING: 'rancher-logging',
|
NVIDIA_DRIVER_TOOLKIT_CONTROLLER: 'nvidia-driver-toolkit',
|
||||||
RANCHER_MONITORING: 'rancher-monitoring',
|
RANCHER_LOGGING: 'rancher-logging',
|
||||||
VM_IMPORT_CONTROLLER: 'vm-import-controller',
|
RANCHER_MONITORING: 'rancher-monitoring',
|
||||||
NVIDIA_DRIVER_TOOLKIT_CONTROLLER: 'nvidia-driver-toolkit'
|
VM_IMPORT_CONTROLLER: 'vm-import-controller',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,49 +1,55 @@
|
|||||||
export const HCI = {
|
export const HCI = {
|
||||||
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
|
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
|
||||||
CURRENT_IP: 'rke2.io/internal-ip',
|
CURRENT_IP: 'rke2.io/internal-ip',
|
||||||
OWNED_BY: 'harvesterhci.io/owned-by',
|
IMAGE_ID: 'harvesterhci.io/imageId',
|
||||||
IMAGE_ID: 'harvesterhci.io/imageId',
|
SSH_NAMES: 'harvesterhci.io/sshNames',
|
||||||
SSH_NAMES: 'harvesterhci.io/sshNames',
|
NETWORK_IPS: 'network.harvesterhci.io/ips',
|
||||||
NETWORK_IPS: 'network.harvesterhci.io/ips',
|
TEMPLATE_VERSION_CUSTOM_NAME: 'template-version.harvesterhci.io/customName',
|
||||||
TEMPLATE_VERSION_CUSTOM_NAME: 'template-version.harvesterhci.io/customName',
|
CREATOR: 'harvesterhci.io/creator',
|
||||||
CREATOR: 'harvesterhci.io/creator',
|
OS: 'harvesterhci.io/os',
|
||||||
OS: 'harvesterhci.io/os',
|
NETWORK_TYPE: 'network.harvesterhci.io/type',
|
||||||
NETWORK_TYPE: 'network.harvesterhci.io/type',
|
VM_NAME: 'harvesterhci.io/vmName',
|
||||||
VM_NAME: 'harvesterhci.io/vmName',
|
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
|
||||||
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
|
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
|
||||||
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
|
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
|
||||||
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
|
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
|
||||||
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
|
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
|
||||||
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
|
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
||||||
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
|
||||||
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
|
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
|
||||||
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
|
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
|
||||||
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
|
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
|
||||||
MIGRATION_STATE: 'harvesterhci.io/migrationState',
|
MIGRATION_STATE: 'harvesterhci.io/migrationState',
|
||||||
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
|
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
|
||||||
IMAGE_NAME: 'harvesterhci.io/image-name',
|
IMAGE_NAME: 'harvesterhci.io/image-name',
|
||||||
INIT_IP: 'etcd.rke2.cattle.io/node-address',
|
INIT_IP: 'etcd.rke2.cattle.io/node-address',
|
||||||
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
|
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
|
||||||
NETWORK_ROUTE: 'network.harvesterhci.io/route',
|
NETWORK_ROUTE: 'network.harvesterhci.io/route',
|
||||||
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
|
MATCHED_NODES: 'network.harvesterhci.io/matched-nodes',
|
||||||
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
|
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
|
||||||
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
|
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
|
||||||
REAY_MESSAGE: 'harvesterhci.io/read-message',
|
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
|
||||||
DYNAMIC_SSHKEYS_NAMES: 'harvesterhci.io/dynamic-ssh-key-names',
|
REAY_MESSAGE: 'harvesterhci.io/read-message',
|
||||||
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
|
DYNAMIC_SSHKEYS_NAMES: 'harvesterhci.io/dynamic-ssh-key-names',
|
||||||
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
|
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
|
||||||
OS_TYPE: 'harvesterhci.io/os-type',
|
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
|
||||||
HOST_REQUEST: 'management.cattle.io/pod-requests',
|
OS_TYPE: 'harvesterhci.io/os-type',
|
||||||
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
|
STORAGE_PROVISIONER: 'harvesterhci.io/storageProvisioner',
|
||||||
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
|
HOST_REQUEST: 'management.cattle.io/pod-requests',
|
||||||
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
|
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
|
||||||
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
|
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
|
||||||
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
|
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
|
||||||
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
|
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
|
||||||
NODE_MANUFACTURER: 'manufacturer',
|
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
|
||||||
NODE_MODEL: 'model',
|
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
|
||||||
NODE_SERIAL_NUMBER: 'serialNumber',
|
NODE_MANUFACTURER: 'manufacturer',
|
||||||
VM_INSUFFICIENT: 'harvesterhci.io/insufficient-resource-quota',
|
NODE_MODEL: 'model',
|
||||||
NODE_NTP_SYNC_STATUS: 'node.harvesterhci.io/ntp-service',
|
NODE_SERIAL_NUMBER: 'serialNumber',
|
||||||
PARENT_SRIOV: 'harvesterhci.io/parent-sriov-network-device',
|
VM_INSUFFICIENT: 'harvesterhci.io/insufficient-resource-quota',
|
||||||
|
NODE_NTP_SYNC_STATUS: 'node.harvesterhci.io/ntp-service',
|
||||||
|
PARENT_SRIOV: 'harvesterhci.io/parent-sriov-network-device',
|
||||||
|
PARENT_SRIOV_GPU: 'harvesterhci.io/parentSRIOVGPUDevice',
|
||||||
|
VM_MAINTENANCE_MODE_STRATEGY: 'harvesterhci.io/maintain-mode-strategy',
|
||||||
|
NODE_CPU_MANAGER_UPDATE_STATUS: 'harvesterhci.io/cpu-manager-update-status',
|
||||||
|
CPU_MANAGER: 'cpumanager'
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,33 +1,39 @@
|
|||||||
export const HCI_SETTING = {
|
export const HCI_SETTING = {
|
||||||
BACKUP_TARGET: 'backup-target',
|
BACKUP_TARGET: 'backup-target',
|
||||||
CONTAINERD_REGISTRY: 'containerd-registry',
|
CONTAINERD_REGISTRY: 'containerd-registry',
|
||||||
LOG_LEVEL: 'log-level',
|
LOG_LEVEL: 'log-level',
|
||||||
SERVER_VERSION: 'server-version',
|
SERVER_VERSION: 'server-version',
|
||||||
UI_INDEX: 'ui-index',
|
UI_INDEX: 'ui-index',
|
||||||
UI_PLUGIN_INDEX: 'ui-plugin-index',
|
UI_PLUGIN_INDEX: 'ui-plugin-index',
|
||||||
UPGRADE_CHECKER_ENABLED: 'upgrade-checker-enabled',
|
UPGRADE_CHECKER_ENABLED: 'upgrade-checker-enabled',
|
||||||
UPGRADE_CHECKER_URL: 'upgrade-checker-url',
|
UPGRADE_CHECKER_URL: 'upgrade-checker-url',
|
||||||
VLAN: 'vlan',
|
VLAN: 'vlan',
|
||||||
UI_SOURCE: 'ui-source',
|
UI_SOURCE: 'ui-source',
|
||||||
UI_PL: 'ui-pl',
|
UI_PL: 'ui-pl',
|
||||||
HTTP_PROXY: 'http-proxy',
|
HTTP_PROXY: 'http-proxy',
|
||||||
ADDITIONAL_CA: 'additional-ca',
|
ADDITIONAL_CA: 'additional-ca',
|
||||||
OVERCOMMIT_CONFIG: 'overcommit-config',
|
OVERCOMMIT_CONFIG: 'overcommit-config',
|
||||||
CLUSTER_REGISTRATION_URL: 'cluster-registration-url',
|
CLUSTER_REGISTRATION_URL: 'cluster-registration-url',
|
||||||
DEFAULT_STORAGE_CLASS: 'default-storage-class',
|
DEFAULT_STORAGE_CLASS: 'default-storage-class',
|
||||||
SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout',
|
SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout',
|
||||||
SUPPORT_BUNDLE_IMAGE: 'support-bundle-image',
|
SUPPORT_BUNDLE_EXPIRATION: 'support-bundle-expiration',
|
||||||
STORAGE_NETWORK: 'storage-network',
|
SUPPORT_BUNDLE_IMAGE: 'support-bundle-image',
|
||||||
VM_FORCE_RESET_POLICY: 'vm-force-reset-policy',
|
SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT: 'support-bundle-node-collection-timeout',
|
||||||
SSL_CERTIFICATES: 'ssl-certificates',
|
STORAGE_NETWORK: 'storage-network',
|
||||||
SSL_PARAMETERS: 'ssl-parameters',
|
VM_FORCE_RESET_POLICY: 'vm-force-reset-policy',
|
||||||
SUPPORT_BUNDLE_NAMESPACES: 'support-bundle-namespaces',
|
SSL_CERTIFICATES: 'ssl-certificates',
|
||||||
AUTO_DISK_PROVISION_PATHS: 'auto-disk-provision-paths',
|
SSL_PARAMETERS: 'ssl-parameters',
|
||||||
RELEASE_DOWNLOAD_URL: 'release-download-url',
|
SUPPORT_BUNDLE_NAMESPACES: 'support-bundle-namespaces',
|
||||||
CCM_CSI_VERSION: 'harvester-csi-ccm-versions',
|
AUTO_DISK_PROVISION_PATHS: 'auto-disk-provision-paths',
|
||||||
CSI_DRIVER_CONFIG: 'csi-driver-config',
|
RELEASE_DOWNLOAD_URL: 'release-download-url',
|
||||||
VM_TERMINATION_PERIOD: 'default-vm-termination-grace-period-seconds',
|
CCM_CSI_VERSION: 'harvester-csi-ccm-versions',
|
||||||
NTP_SERVERS: 'ntp-servers',
|
CSI_DRIVER_CONFIG: 'csi-driver-config',
|
||||||
|
VM_TERMINATION_PERIOD: 'default-vm-termination-grace-period-seconds',
|
||||||
|
NTP_SERVERS: 'ntp-servers',
|
||||||
|
AUTO_ROTATE_RKE2_CERTS: 'auto-rotate-rke2-certs',
|
||||||
|
KUBECONFIG_DEFAULT_TOKEN_TTL_MINUTES: 'kubeconfig-default-token-ttl-minutes',
|
||||||
|
LONGHORN_V2_DATA_ENGINE_ENABLED: 'longhorn-v2-data-engine-enabled',
|
||||||
|
ADDITIONAL_GUEST_MEMORY_OVERHEAD_RATIO: 'additional-guest-memory-overhead-ratio'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HCI_ALLOWED_SETTINGS = {
|
export const HCI_ALLOWED_SETTINGS = {
|
||||||
@ -41,6 +47,7 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
[HCI_SETTING.VLAN]: {
|
[HCI_SETTING.VLAN]: {
|
||||||
kind: 'custom', from: 'import', alias: 'vlan'
|
kind: 'custom', from: 'import', alias: 'vlan'
|
||||||
},
|
},
|
||||||
|
[HCI_SETTING.AUTO_ROTATE_RKE2_CERTS]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.CSI_DRIVER_CONFIG]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.CSI_DRIVER_CONFIG]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.SERVER_VERSION]: { readOnly: true },
|
[HCI_SETTING.SERVER_VERSION]: { readOnly: true },
|
||||||
[HCI_SETTING.UPGRADE_CHECKER_ENABLED]: { kind: 'boolean' },
|
[HCI_SETTING.UPGRADE_CHECKER_ENABLED]: { kind: 'boolean' },
|
||||||
@ -49,14 +56,16 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
[HCI_SETTING.ADDITIONAL_CA]: {
|
[HCI_SETTING.ADDITIONAL_CA]: {
|
||||||
kind: 'multiline', canReset: true, from: 'import'
|
kind: 'multiline', canReset: true, from: 'import'
|
||||||
},
|
},
|
||||||
[HCI_SETTING.OVERCOMMIT_CONFIG]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.OVERCOMMIT_CONFIG]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.SUPPORT_BUNDLE_TIMEOUT]: {},
|
[HCI_SETTING.SUPPORT_BUNDLE_TIMEOUT]: {},
|
||||||
[HCI_SETTING.SUPPORT_BUNDLE_IMAGE]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: {},
|
||||||
[HCI_SETTING.STORAGE_NETWORK]: { kind: 'custom', from: 'import' },
|
[HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: {},
|
||||||
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.SUPPORT_BUNDLE_IMAGE]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.RANCHER_MANAGER_SUPPORT]: { kind: 'boolean' },
|
[HCI_SETTING.STORAGE_NETWORK]: { kind: 'custom', from: 'import' },
|
||||||
[HCI_SETTING.SSL_CERTIFICATES]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.SSL_PARAMETERS]: {
|
[HCI_SETTING.RANCHER_MANAGER_SUPPORT]: { kind: 'boolean' },
|
||||||
|
[HCI_SETTING.SSL_CERTIFICATES]: { kind: 'json', from: 'import' },
|
||||||
|
[HCI_SETTING.SSL_PARAMETERS]: {
|
||||||
kind: 'json', from: 'import', canReset: true
|
kind: 'json', from: 'import', canReset: true
|
||||||
},
|
},
|
||||||
[HCI_SETTING.SUPPORT_BUNDLE_NAMESPACES]: { from: 'import', canReset: true },
|
[HCI_SETTING.SUPPORT_BUNDLE_NAMESPACES]: { from: 'import', canReset: true },
|
||||||
@ -75,6 +84,9 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
[HCI_SETTING.NTP_SERVERS]: {
|
[HCI_SETTING.NTP_SERVERS]: {
|
||||||
kind: 'json', from: 'import', canReset: true
|
kind: 'json', from: 'import', canReset: true
|
||||||
},
|
},
|
||||||
|
[HCI_SETTING.KUBECONFIG_DEFAULT_TOKEN_TTL_MINUTES]: {},
|
||||||
|
[HCI_SETTING.LONGHORN_V2_DATA_ENGINE_ENABLED]: { kind: 'boolean', technicalPreview: true },
|
||||||
|
[HCI_SETTING.ADDITIONAL_GUEST_MEMORY_OVERHEAD_RATIO]: { kind: 'string', from: 'import' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = {
|
export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = {
|
||||||
|
|||||||
@ -11,6 +11,14 @@ export const IMAGE_DOWNLOAD_SIZE = {
|
|||||||
width: 120
|
width: 120
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const IMAGE_VIRTUAL_SIZE = {
|
||||||
|
name: 'virtualSize',
|
||||||
|
labelKey: 'harvester.tableHeaders.virtualSize',
|
||||||
|
value: 'virtualSize',
|
||||||
|
sort: 'status.virtualSize',
|
||||||
|
width: 120
|
||||||
|
};
|
||||||
|
|
||||||
export const IMAGE_PROGRESS = {
|
export const IMAGE_PROGRESS = {
|
||||||
name: 'Uploaded',
|
name: 'Uploaded',
|
||||||
labelKey: 'tableHeaders.progress',
|
labelKey: 'tableHeaders.progress',
|
||||||
|
|||||||
@ -48,6 +48,13 @@ export default {
|
|||||||
customName() {
|
customName() {
|
||||||
return this.value.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CUSTOM_NAME];
|
return this.value.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CUSTOM_NAME];
|
||||||
},
|
},
|
||||||
|
cpuManagerStatus() {
|
||||||
|
if (this.value.isCPUManagerEnableInProgress) {
|
||||||
|
return this.t('generic.loading');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.t(`generic.${ this.value.isCPUManagerEnabled ? 'enabled' : 'disabled' }`);
|
||||||
|
},
|
||||||
|
|
||||||
consoleUrl() {
|
consoleUrl() {
|
||||||
const consoleUrl = this.value.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
|
const consoleUrl = this.value.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
|
||||||
@ -224,6 +231,9 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
|
<div v-if="!value.isEtcd" class="col span-6">
|
||||||
|
<LabelValue :name="t('harvester.host.detail.cpuManager')" :value="cpuManagerStatus" />
|
||||||
|
</div>
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<LabelValue :name="t('harvester.host.detail.consoleUrl')" :value="consoleUrl.value">
|
<LabelValue :name="t('harvester.host.detail.consoleUrl')" :value="consoleUrl.value">
|
||||||
<a slot="value" :href="consoleUrl.value" target="_blank">{{ consoleUrl.display }}</a>
|
<a slot="value" :href="consoleUrl.value" target="_blank">{{ consoleUrl.display }}</a>
|
||||||
@ -268,7 +278,7 @@ export default {
|
|||||||
<HarvesterStorageUsed
|
<HarvesterStorageUsed
|
||||||
:row="value"
|
:row="value"
|
||||||
:resource-name="t('harvester.host.detail.storage')"
|
:resource-name="t('harvester.host.detail.storage')"
|
||||||
:show-reserved="true"
|
:show-allocated="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,13 +4,15 @@ import LabelValue from '@shell/components/LabelValue';
|
|||||||
import { BadgeState } from '@components/BadgeState';
|
import { BadgeState } from '@components/BadgeState';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import HarvesterDisk from '../../mixins/harvester-disk';
|
import HarvesterDisk from '../../mixins/harvester-disk';
|
||||||
|
import { RadioGroup } from '@components/Form/Radio';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
LabelValue,
|
LabelValue,
|
||||||
BadgeState,
|
BadgeState,
|
||||||
Banner,
|
Banner,
|
||||||
Tag
|
Tag,
|
||||||
|
RadioGroup
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [
|
mixins: [
|
||||||
@ -37,6 +39,18 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
targetDisk() {
|
||||||
|
return this.disks.find(disk => disk.name === this.value.name);
|
||||||
|
},
|
||||||
|
schedulableTooltipMessage() {
|
||||||
|
const { name, path } = this.value;
|
||||||
|
|
||||||
|
if (this.targetDisk && !this.targetDisk.allowScheduling && name && path) {
|
||||||
|
return this.t('harvester.host.disk.allowScheduling.tooltip', { name, path });
|
||||||
|
} else {
|
||||||
|
return this.schedulableCondition.message;
|
||||||
|
}
|
||||||
|
},
|
||||||
allowSchedulingOptions() {
|
allowSchedulingOptions() {
|
||||||
return [{
|
return [{
|
||||||
label: this.t('generic.enabled'),
|
label: this.t('generic.enabled'),
|
||||||
@ -117,6 +131,16 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mt-10">
|
<div class="row mt-10">
|
||||||
<div class="col span-12">
|
<div class="col span-12">
|
||||||
|
<div class="pull-left">
|
||||||
|
<RadioGroup
|
||||||
|
v-model:value="value.allowScheduling"
|
||||||
|
name="diskScheduling"
|
||||||
|
:label="t('harvester.host.disk.allowScheduling.label')"
|
||||||
|
:mode="mode"
|
||||||
|
:options="allowSchedulingOptions"
|
||||||
|
:row="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
{{ t('harvester.host.disk.conditions') }}:
|
{{ t('harvester.host.disk.conditions') }}:
|
||||||
<BadgeState
|
<BadgeState
|
||||||
@ -127,9 +151,9 @@ export default {
|
|||||||
class="mr-10 ml-10 state"
|
class="mr-10 ml-10 state"
|
||||||
/>
|
/>
|
||||||
<BadgeState
|
<BadgeState
|
||||||
v-clean-tooltip="schedulableCondition.message"
|
v-clean-tooltip="schedulableTooltipMessage"
|
||||||
:color="schedulableCondition.status === 'True' ? 'bg-success' : 'bg-error' "
|
:color="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'bg-success' : 'bg-error' "
|
||||||
:icon="schedulableCondition.status === 'True' ? 'icon-checkmark' : 'icon-warning' "
|
:icon="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'icon-checkmark' : 'icon-warning' "
|
||||||
label="Schedulable"
|
label="Schedulable"
|
||||||
class="mr-10 state"
|
class="mr-10 state"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -104,7 +104,7 @@ export default {
|
|||||||
key-field="_key"
|
key-field="_key"
|
||||||
|
|
||||||
>
|
>
|
||||||
<template cell:state="scope">
|
<template cell:state="scope" class="state-col">
|
||||||
<div class="state">
|
<div class="state">
|
||||||
<HarvesterVmState class="vmstate" :row="scope.row" :all-cluster-network="allClusterNetwork" />
|
<HarvesterVmState class="vmstate" :row="scope.row" :all-cluster-network="allClusterNetwork" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -311,14 +311,12 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const current = this.ntpSync?.currentNtpServers || '';
|
|
||||||
|
|
||||||
if (status === 'unsynced') {
|
if (status === 'unsynced') {
|
||||||
return {
|
return {
|
||||||
status: 'unsynced',
|
status: 'unsynced',
|
||||||
warning: {
|
warning: {
|
||||||
key: 'harvester.host.ntp.ntpSyncStatus.isUnsynced',
|
key: 'harvester.host.ntp.ntpSyncStatus.isUnsynced',
|
||||||
current
|
current: this.ntpSync?.currentNtpServers ? `<code>${ this.ntpSync.currentNtpServers }</code>` : '',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,10 @@ export default {
|
|||||||
return this.value?.downSize;
|
return this.value?.downSize;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
virtualSize() {
|
||||||
|
return this.value?.virtualSize;
|
||||||
|
},
|
||||||
|
|
||||||
url() {
|
url() {
|
||||||
return this.value?.spec?.url || '-';
|
return this.value?.spec?.url || '-';
|
||||||
},
|
},
|
||||||
@ -100,6 +104,12 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabelValue :name="t('harvester.image.virtualSize')" :value="virtualSize" class="mb-20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col span-12">
|
<div class="col span-12">
|
||||||
<LabelValue :name="t('nameNsDescription.description.label')" :value="description" class="mb-20" />
|
<LabelValue :name="t('nameNsDescription.description.label')" :value="description" class="mb-20" />
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import NodeScheduling from '@shell/components/form/NodeScheduling';
|
|||||||
import PodAffinity from '@shell/components/form/PodAffinity';
|
import PodAffinity from '@shell/components/form/PodAffinity';
|
||||||
import KeyValue from '@shell/components/form/KeyValue';
|
import KeyValue from '@shell/components/form/KeyValue';
|
||||||
import Labels from '@shell/components/form/Labels';
|
import Labels from '@shell/components/form/Labels';
|
||||||
|
import LabelValue from '@shell/components/LabelValue';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import VM_MIXIN from '../../mixins/harvester-vm';
|
import VM_MIXIN from '../../mixins/harvester-vm';
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ import Events from './VirtualMachineTabs/VirtualMachineEvents';
|
|||||||
import Migration from './VirtualMachineTabs/VirtualMachineMigration';
|
import Migration from './VirtualMachineTabs/VirtualMachineMigration';
|
||||||
import OverviewBasics from './VirtualMachineTabs/VirtualMachineBasics';
|
import OverviewBasics from './VirtualMachineTabs/VirtualMachineBasics';
|
||||||
import OverviewKeypairs from './VirtualMachineTabs/VirtualMachineKeypairs';
|
import OverviewKeypairs from './VirtualMachineTabs/VirtualMachineKeypairs';
|
||||||
|
import { formatSi } from '@shell/utils/units';
|
||||||
|
|
||||||
const VM_METRICS_DETAIL_URL = '/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-grafana:80/proxy/d/harvester-vm-detail-1/vm-info-detail?orgId=1';
|
const VM_METRICS_DETAIL_URL = '/api/v1/namespaces/cattle-monitoring-system/services/http:rancher-monitoring-grafana:80/proxy/d/harvester-vm-detail-1/vm-info-detail?orgId=1';
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ export default {
|
|||||||
Tabbed,
|
Tabbed,
|
||||||
Events,
|
Events,
|
||||||
OverviewBasics,
|
OverviewBasics,
|
||||||
|
LabelValue,
|
||||||
Volume,
|
Volume,
|
||||||
Network,
|
Network,
|
||||||
OverviewKeypairs,
|
OverviewKeypairs,
|
||||||
@ -57,15 +60,18 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
switchToCloud: false,
|
hasResourceQuotaSchema: false,
|
||||||
|
switchToCloud: false,
|
||||||
VM_METRICS_DETAIL_URL,
|
VM_METRICS_DETAIL_URL,
|
||||||
showVmMetrics: false,
|
showVmMetrics: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
this.hasResourceQuotaSchema = !!this.$store.getters[`${ inStore }/schemaFor`](HCI.RESOURCE_QUOTA);
|
||||||
|
|
||||||
const hash = {
|
const hash = {
|
||||||
pods: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
|
pods: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
|
||||||
services: this.$store.dispatch(`${ inStore }/findAll`, { type: SERVICE }),
|
services: this.$store.dispatch(`${ inStore }/findAll`, { type: SERVICE }),
|
||||||
@ -75,6 +81,10 @@ export default {
|
|||||||
restore: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.RESTORE }),
|
restore: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.RESTORE }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.hasResourceQuotaSchema) {
|
||||||
|
hash.resourceQuotas = this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.RESOURCE_QUOTA });
|
||||||
|
}
|
||||||
|
|
||||||
await allHash(hash);
|
await allHash(hash);
|
||||||
|
|
||||||
setPromiseResult(
|
setPromiseResult(
|
||||||
@ -88,6 +98,22 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['currentCluster']),
|
...mapGetters(['currentCluster']),
|
||||||
|
|
||||||
|
totalSnapshotSize() {
|
||||||
|
if (this.value.snapshotSizeQuota === undefined || this.value.snapshotSizeQuota === null) {
|
||||||
|
return ' - ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.value.snapshotSizeQuota === 0) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatSi(this.value.snapshotSizeQuota, {
|
||||||
|
increment: 1024,
|
||||||
|
addSuffix: true,
|
||||||
|
suffix: 'i',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
vmi() {
|
vmi() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
@ -175,10 +201,17 @@ export default {
|
|||||||
<Network v-model:value="networkRows" mode="view" />
|
<Network v-model:value="networkRows" mode="view" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab name="keypairs" :label="t('harvester.virtualMachine.detail.tabs.keypairs')" class="bordered-table" :weight="3">
|
<Tab name="keypairs" :label="t('harvester.virtualMachine.detail.tabs.keypairs')" class="bordered-table" :weight="4">
|
||||||
<OverviewKeypairs :value="value" />
|
<OverviewKeypairs :value="value" />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
|
<Tab v-if="hasResourceQuotaSchema" name="quotas" :label="t('harvester.tab.quotas')" :weight="3">
|
||||||
|
<LabelValue
|
||||||
|
:name="t('harvester.snapshot.totalSnapshotSize')"
|
||||||
|
:value="totalSnapshotSize"
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
v-if="showVmMetrics"
|
v-if="showVmMetrics"
|
||||||
name="vm-metrics"
|
name="vm-metrics"
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { STATE, NAME, AGE } from '@shell/config/table-headers';
|
|||||||
import { matching } from '@shell/utils/selector';
|
import { matching } from '@shell/utils/selector';
|
||||||
import { NODE } from '@shell/config/types';
|
import { NODE } from '@shell/config/types';
|
||||||
import { isEmpty } from '@shell/utils/object';
|
import { isEmpty } from '@shell/utils/object';
|
||||||
|
import { HCI } from '@pkg/harvester/config/labels-annotations';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -55,10 +56,13 @@ export default {
|
|||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
const nodes = this.$store.getters[`${ inStore }/all`](NODE);
|
const nodes = this.$store.getters[`${ inStore }/all`](NODE);
|
||||||
|
const matchedNodes = this.value?.metadata?.annotations?.[HCI.MATCHED_NODES];
|
||||||
const selector = this.value?.spec?.nodeSelector;
|
const selector = this.value?.spec?.nodeSelector;
|
||||||
|
|
||||||
if (!isEmpty(selector)) {
|
if (!isEmpty(selector)) {
|
||||||
return matching(nodes, selector);
|
return matching(nodes, selector);
|
||||||
|
} else if (matchedNodes && matchedNodes.length > 0) {
|
||||||
|
return nodes.filter(node => matchedNodes.includes(node.id));
|
||||||
} else {
|
} else {
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { Banner } from '@components/Banner';
|
|||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||||
import { BadgeState } from '@components/BadgeState';
|
import { BadgeState } from '@components/BadgeState';
|
||||||
|
import { ucFirst } from '@shell/utils/string';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -25,9 +26,9 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
errors: [],
|
errors: [],
|
||||||
unhealthyVM: '',
|
unhealthyVMs: [],
|
||||||
force: false
|
force: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -40,13 +41,21 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
ucFirst,
|
||||||
|
|
||||||
|
onInputForce(v) {
|
||||||
|
if (v) {
|
||||||
|
this.unhealthyVMs = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
},
|
},
|
||||||
|
|
||||||
async apply(buttonCb) {
|
async apply(buttonCb) {
|
||||||
this.errors = [];
|
this.errors = [];
|
||||||
this.unhealthyVM = '';
|
this.unhealthyVMs = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await this.actionResource.doAction('maintenancePossible');
|
const res = await this.actionResource.doAction('maintenancePossible');
|
||||||
@ -62,8 +71,8 @@ export default {
|
|||||||
} else if (res._status === 200 || res._status === 204) {
|
} else if (res._status === 200 || res._status === 204) {
|
||||||
const res = await this.actionResource.doAction('listUnhealthyVM');
|
const res = await this.actionResource.doAction('listUnhealthyVM');
|
||||||
|
|
||||||
if (res.message) {
|
if (res?.length) {
|
||||||
this.unhealthyVM = res;
|
this.unhealthyVMs = res;
|
||||||
buttonCb(false);
|
buttonCb(false);
|
||||||
} else {
|
} else {
|
||||||
await this.actionResource.doAction('enableMaintenanceMode', { force: 'false' });
|
await this.actionResource.doAction('enableMaintenanceMode', { force: 'false' });
|
||||||
@ -95,15 +104,20 @@ export default {
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
v-model:value="force"
|
v-model:value="force"
|
||||||
label-key="harvester.host.enableMaintenance.force"
|
label-key="harvester.host.enableMaintenance.force"
|
||||||
|
@update:value="onInputForce"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Banner color="warning" :label="t('harvester.host.enableMaintenance.protip')" class="mb-0" />
|
|
||||||
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
|
|
||||||
|
|
||||||
<div v-if="unhealthyVM">
|
<Banner color="warning" :label="t('harvester.host.enableMaintenance.protip')" />
|
||||||
<Banner color="error mb-5">
|
|
||||||
|
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="ucFirst(err)" />
|
||||||
|
|
||||||
|
<Banner v-if="!force" class="mt-0" color="warning" :labelKey="'harvester.host.enableMaintenance.shutDownVMs'" />
|
||||||
|
|
||||||
|
<div v-for="(unhealthyVM, i) in unhealthyVMs" :key="i">
|
||||||
|
<Banner color="error mt-0 mb-5">
|
||||||
<p>
|
<p>
|
||||||
{{ unhealthyVM.message }}
|
{{ ucFirst(unhealthyVM.message) }}
|
||||||
</p>
|
</p>
|
||||||
</Banner>
|
</Banner>
|
||||||
|
|
||||||
|
|||||||
@ -72,8 +72,11 @@ export default {
|
|||||||
const nodes = this.$store.getters['harvester/all'](NODE);
|
const nodes = this.$store.getters['harvester/all'](NODE);
|
||||||
|
|
||||||
return nodes.filter((n) => {
|
return nodes.filter((n) => {
|
||||||
// do not allow to migrate to self node
|
const isNotSelfNode = !!this.availableNodes.includes(n.id);
|
||||||
return !!this.availableNodes.includes(n.id);
|
const isNotWitnessNode = n.isEtcd !== 'true'; // do not allow to migrate to self node and witness node
|
||||||
|
const matchingCpuManagerConfig = n.isCPUManagerEnabled; // If cpu-pinning is enabled, filter-out non-enabled CPU manager nodes.
|
||||||
|
|
||||||
|
return isNotSelfNode && isNotWitnessNode && matchingCpuManagerConfig;
|
||||||
}).map((n) => {
|
}).map((n) => {
|
||||||
let label = n?.metadata?.name;
|
let label = n?.metadata?.name;
|
||||||
const value = n?.metadata?.name;
|
const value = n?.metadata?.name;
|
||||||
|
|||||||
121
pkg/harvester/dialog/HarvesterQuotaDialog.vue
Normal file
121
pkg/harvester/dialog/HarvesterQuotaDialog.vue
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<script>
|
||||||
|
import { mapGetters, mapState } from 'vuex';
|
||||||
|
import { Card } from '@components/Card';
|
||||||
|
import { Banner } from '@components/Banner';
|
||||||
|
import AsyncButton from '@shell/components/AsyncButton';
|
||||||
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HarvesterVMQuotaDialog',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
AsyncButton,
|
||||||
|
Card,
|
||||||
|
UnitInput,
|
||||||
|
Banner
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
resources: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.totalSnapshotSize = this.modalData.snapshotSizeQuota;
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
totalSnapshotSize: '',
|
||||||
|
errors: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState('action-menu', ['modalData']),
|
||||||
|
...mapGetters({ t: 'i18n/t' }),
|
||||||
|
|
||||||
|
actionResource() {
|
||||||
|
return this.resources[0];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.totalSnapshotSize = '';
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
|
||||||
|
async save(buttonDone) {
|
||||||
|
try {
|
||||||
|
// call delete action if user input 0Gi or empty string
|
||||||
|
if (this.totalSnapshotSize === null || this.totalSnapshotSize === '0Gi' ) {
|
||||||
|
await this.actionResource.doAction('deleteResourceQuota');
|
||||||
|
} else {
|
||||||
|
await this.actionResource.doAction('updateResourceQuota', { totalSnapshotSizeQuota: this.totalSnapshotSize });
|
||||||
|
}
|
||||||
|
this.close();
|
||||||
|
buttonDone(true);
|
||||||
|
} catch (err) {
|
||||||
|
const error = err?.data || err;
|
||||||
|
const message = exceptionToErrorsArray(error);
|
||||||
|
|
||||||
|
this['errors'] = message;
|
||||||
|
buttonDone(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Card :show-highlight-border="false">
|
||||||
|
<h4
|
||||||
|
slot="title"
|
||||||
|
v-clean-html="t('harvester.modal.quota.editQuota')"
|
||||||
|
class="text-default-text"
|
||||||
|
/>
|
||||||
|
<template #body>
|
||||||
|
<Banner color="info">
|
||||||
|
{{ t('harvester.modal.quota.bannerMessage') }}
|
||||||
|
</Banner>
|
||||||
|
<UnitInput
|
||||||
|
v-model:value="totalSnapshotSize"
|
||||||
|
v-int-number
|
||||||
|
:label="t('harvester.snapshot.totalSnapshotSize')"
|
||||||
|
:disabled="false"
|
||||||
|
:mode="create"
|
||||||
|
:input-exponent="3"
|
||||||
|
:increment="1024"
|
||||||
|
:output-modifier="true"
|
||||||
|
suffix="GiB"
|
||||||
|
class="mb-20"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<div slot="actions" class="actions">
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="btn role-secondary mr-10" @click="close">
|
||||||
|
{{ t('generic.cancel') }}
|
||||||
|
</button>
|
||||||
|
<AsyncButton @click="save" />
|
||||||
|
</div>
|
||||||
|
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.actions {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -149,7 +149,7 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
|
<Banner v-for="(err, i) in errors" :key="i"/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { BadgeState } from '@components/BadgeState';
|
|||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import { RadioGroup, RadioButton } from '@components/Form/Radio';
|
import { RadioGroup, RadioButton } from '@components/Form/Radio';
|
||||||
import HarvesterDisk from '../../mixins/harvester-disk';
|
import HarvesterDisk from '../../mixins/harvester-disk';
|
||||||
import DiskTags from '../../components/DiskTags';
|
import Tags from '../../components/DiskTags';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import { LONGHORN_SYSTEM } from './index';
|
import { LONGHORN_SYSTEM } from './index';
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export default {
|
|||||||
Banner,
|
Banner,
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
RadioButton,
|
RadioButton,
|
||||||
DiskTags,
|
Tags,
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [
|
mixins: [
|
||||||
@ -44,6 +44,18 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
targetDisk() {
|
||||||
|
return this.disks.find(disk => disk.name === this.value.name);
|
||||||
|
},
|
||||||
|
schedulableTooltipMessage() {
|
||||||
|
const { name, path } = this.value;
|
||||||
|
|
||||||
|
if (this.targetDisk && !this.targetDisk.allowScheduling && name && path) {
|
||||||
|
return this.t('harvester.host.disk.allowScheduling.tooltip', { name, path });
|
||||||
|
} else {
|
||||||
|
return this.schedulableCondition.message;
|
||||||
|
}
|
||||||
|
},
|
||||||
allowSchedulingOptions() {
|
allowSchedulingOptions() {
|
||||||
return [{
|
return [{
|
||||||
label: this.t('generic.enabled'),
|
label: this.t('generic.enabled'),
|
||||||
@ -171,7 +183,7 @@ export default {
|
|||||||
<div v-if="!value.isNew">
|
<div v-if="!value.isNew">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col span-12">
|
<div class="col span-12">
|
||||||
<DiskTags
|
<Tags
|
||||||
v-model:value="value.tags"
|
v-model:value="value.tags"
|
||||||
:label="t('harvester.host.disk.tags.label')"
|
:label="t('harvester.host.disk.tags.label')"
|
||||||
:add-label="t('harvester.host.disk.tags.addLabel')"
|
:add-label="t('harvester.host.disk.tags.addLabel')"
|
||||||
@ -181,6 +193,16 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="row mt-10">
|
<div class="row mt-10">
|
||||||
<div class="col span-12">
|
<div class="col span-12">
|
||||||
|
<div class="pull-left">
|
||||||
|
<RadioGroup
|
||||||
|
v-model:value="value.allowScheduling"
|
||||||
|
name="diskScheduling"
|
||||||
|
:label="t('harvester.host.disk.allowScheduling.label')"
|
||||||
|
:mode="mode"
|
||||||
|
:options="allowSchedulingOptions"
|
||||||
|
:row="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
{{ t('harvester.host.disk.conditions') }}:
|
{{ t('harvester.host.disk.conditions') }}:
|
||||||
<BadgeState
|
<BadgeState
|
||||||
@ -191,9 +213,9 @@ export default {
|
|||||||
class="mr-10 ml-10 state"
|
class="mr-10 ml-10 state"
|
||||||
/>
|
/>
|
||||||
<BadgeState
|
<BadgeState
|
||||||
v-clean-tooltip="schedulableCondition.message"
|
v-clean-tooltip="schedulableTooltipMessage"
|
||||||
:color="schedulableCondition.status === 'True' ? 'bg-success' : 'bg-error' "
|
:color="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'bg-success' : 'bg-error' "
|
||||||
:icon="schedulableCondition.status === 'True' ? 'icon-checkmark' : 'icon-warning' "
|
:icon="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'icon-checkmark' : 'icon-warning' "
|
||||||
label="Schedulable"
|
label="Schedulable"
|
||||||
class="mr-10 state"
|
class="mr-10 state"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import UnitInput from '@shell/components/form/UnitInput';
|
|||||||
import { RadioGroup } from '@components/Form/Radio';
|
import { RadioGroup } from '@components/Form/Radio';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
|
import { DOC_LINKS } from '../../config/doc-links';
|
||||||
|
|
||||||
export const ksmtunedMode = [{
|
export const ksmtunedMode = [{
|
||||||
value: 'standard',
|
value: 'standard',
|
||||||
@ -86,6 +87,10 @@ export default {
|
|||||||
|
|
||||||
showKsmt() {
|
showKsmt() {
|
||||||
return this.spec.run === 'run';
|
return this.spec.run === 'run';
|
||||||
|
},
|
||||||
|
|
||||||
|
ksmtunedLink() {
|
||||||
|
return DOC_LINKS.KSMTUNED_MODE;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -135,7 +140,7 @@ export default {
|
|||||||
<Checkbox v-model:value="enableMergeAcrossNodes" :mode="mode" class="check mb-20" type="checkbox" :label="t('harvester.host.ksmtuned.enableMergeNodes')" />
|
<Checkbox v-model:value="enableMergeAcrossNodes" :mode="mode" class="check mb-20" type="checkbox" :label="t('harvester.host.ksmtuned.enableMergeNodes')" />
|
||||||
|
|
||||||
<h3>
|
<h3>
|
||||||
<t k="harvester.host.ksmtuned.modeLink" :raw="true" />
|
<t k="harvester.host.ksmtuned.modeLink" :raw="true" :url="ksmtunedLink" />
|
||||||
</h3>
|
</h3>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-model:value="spec.mode"
|
v-model:value="spec.mode"
|
||||||
|
|||||||
@ -25,10 +25,10 @@ import { _EDIT } from '@shell/config/query-params';
|
|||||||
import { sortBy } from '@shell/utils/sort';
|
import { sortBy } from '@shell/utils/sort';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import DiskTags from '../../components/DiskTags';
|
|
||||||
import HarvesterDisk from './HarvesterDisk';
|
import HarvesterDisk from './HarvesterDisk';
|
||||||
import HarvesterSeeder from './HarvesterSeeder';
|
import HarvesterSeeder from './HarvesterSeeder';
|
||||||
import HarvesterKsmtuned from './HarvesterKsmtuned';
|
import HarvesterKsmtuned from './HarvesterKsmtuned';
|
||||||
|
import Tags from '../../components/DiskTags';
|
||||||
|
|
||||||
export const LONGHORN_SYSTEM = 'longhorn-system';
|
export const LONGHORN_SYSTEM = 'longhorn-system';
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export default {
|
|||||||
ButtonDropdown,
|
ButtonDropdown,
|
||||||
KeyValue,
|
KeyValue,
|
||||||
Banner,
|
Banner,
|
||||||
DiskTags,
|
Tags,
|
||||||
Loading,
|
Loading,
|
||||||
HarvesterSeeder,
|
HarvesterSeeder,
|
||||||
MessageLink,
|
MessageLink,
|
||||||
@ -478,10 +478,11 @@ export default {
|
|||||||
|
|
||||||
const disks = this.longhornNode?.spec?.disks || {};
|
const disks = this.longhornNode?.spec?.disks || {};
|
||||||
|
|
||||||
|
// update each disk tags and scheduling
|
||||||
this.newDisks.map((disk) => {
|
this.newDisks.map((disk) => {
|
||||||
(disks[disk.name] || {}).tags = disk.tags;
|
(disks[disk.name] || {}).tags = disk.tags;
|
||||||
|
(disks[disk.name] || {}).allowScheduling = disk.allowScheduling;
|
||||||
});
|
});
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
const retrySave = async() => {
|
const retrySave = async() => {
|
||||||
@ -505,7 +506,9 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await retrySave();
|
if (this.longhornNode) {
|
||||||
|
await retrySave();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -547,7 +550,7 @@ export default {
|
|||||||
class="row mb-20"
|
class="row mb-20"
|
||||||
>
|
>
|
||||||
<div class="col span-12">
|
<div class="col span-12">
|
||||||
<DiskTags
|
<Tags
|
||||||
v-model:value="longhornNode.spec.tags"
|
v-model:value="longhornNode.spec.tags"
|
||||||
:label="t('harvester.host.tags.label')"
|
:label="t('harvester.host.tags.label')"
|
||||||
:add-label="t('harvester.host.tags.addLabel')"
|
:add-label="t('harvester.host.tags.addLabel')"
|
||||||
@ -650,7 +653,6 @@ export default {
|
|||||||
:value="filteredLabels"
|
:value="filteredLabels"
|
||||||
:add-label="t('labels.addLabel')"
|
:add-label="t('labels.addLabel')"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:title="t('labels.labels.title')"
|
|
||||||
:read-allowed="false"
|
:read-allowed="false"
|
||||||
:value-can-be-empty="true"
|
:value-can-be-empty="true"
|
||||||
@update:value="updateHostLabels"
|
@update:value="updateHostLabels"
|
||||||
|
|||||||
@ -143,10 +143,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
input(neu) {
|
input(neu) {
|
||||||
const pattern = /^([1-9]|[1-9][0-9]{1,2}|[1-3][0-9]{3}|40[0-9][0-4])$/;
|
if (neu === '') {
|
||||||
|
this.config.vlan = '';
|
||||||
|
|
||||||
if (!pattern.test(neu) && neu !== '') {
|
return;
|
||||||
this.config.vlan = neu > 4094 ? 4094 : 1;
|
}
|
||||||
|
const newValue = Number(neu);
|
||||||
|
|
||||||
|
if (newValue > 4094) {
|
||||||
|
this.config.vlan = 4094;
|
||||||
|
} else if (newValue < 1) {
|
||||||
|
this.config.vlan = 1;
|
||||||
|
} else {
|
||||||
|
this.config.vlan = newValue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import { STORAGE_CLASS, LONGHORN } from '@shell/config/types';
|
|||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { clone } from '@shell/utils/object';
|
import { clone } from '@shell/utils/object';
|
||||||
import { CSI_DRIVER } from '../../types';
|
import { CSI_DRIVER } from '../../types';
|
||||||
import DiskTags from '../../components/DiskTags';
|
import Tags from '../../components/DiskTags';
|
||||||
|
|
||||||
const LONGHORN_DRIVER = 'driver.longhorn.io';
|
const LONGHORN_DRIVER = 'driver.longhorn.io';
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ export default {
|
|||||||
Tab,
|
Tab,
|
||||||
Tabbed,
|
Tabbed,
|
||||||
Loading,
|
Loading,
|
||||||
DiskTags,
|
Tags,
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CreateEditView],
|
mixins: [CreateEditView],
|
||||||
@ -304,7 +304,7 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col span-8 value">
|
<div class="col span-8 value">
|
||||||
<DiskTags
|
<Tags
|
||||||
v-model:value="scope.row.value.values"
|
v-model:value="scope.row.value.values"
|
||||||
:add-label="t('generic.add')"
|
:add-label="t('generic.add')"
|
||||||
:mode="modeOverride"
|
:mode="modeOverride"
|
||||||
|
|||||||
@ -162,6 +162,7 @@ export default {
|
|||||||
buttonCb(false);
|
buttonCb(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.value.spec.url = this.value.spec.url?.trim() || '';
|
||||||
this.save(buttonCb);
|
this.save(buttonCb);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -277,6 +278,7 @@ export default {
|
|||||||
:can-yaml="showEditAsYaml ? true : false"
|
:can-yaml="showEditAsYaml ? true : false"
|
||||||
:apply-hooks="applyHooks"
|
:apply-hooks="applyHooks"
|
||||||
@finish="saveImage"
|
@finish="saveImage"
|
||||||
|
@error="e=>errors=e"
|
||||||
>
|
>
|
||||||
<NameNsDescription
|
<NameNsDescription
|
||||||
ref="nd"
|
ref="nd"
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations
|
|||||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
import { AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
import { AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import { RunStrategys } from '../config/harvester-map';
|
|
||||||
import VM_MIXIN from '../mixins/harvester-vm';
|
import VM_MIXIN from '../mixins/harvester-vm';
|
||||||
import Reserved from './kubevirt.io.virtualmachine/VirtualMachineReserved';
|
import Reserved from './kubevirt.io.virtualmachine/VirtualMachineReserved';
|
||||||
import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume';
|
import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume';
|
||||||
@ -74,7 +73,6 @@ export default {
|
|||||||
description: '',
|
description: '',
|
||||||
defaultVersion: null,
|
defaultVersion: null,
|
||||||
isDefaultVersion: false,
|
isDefaultVersion: false,
|
||||||
RunStrategys,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -263,7 +261,7 @@ export default {
|
|||||||
<Tab
|
<Tab
|
||||||
name="nodeScheduling"
|
name="nodeScheduling"
|
||||||
:label="t('workload.container.titles.nodeScheduling')"
|
:label="t('workload.container.titles.nodeScheduling')"
|
||||||
:weight="-89"
|
:weight="-3"
|
||||||
>
|
>
|
||||||
<template #default="{active}">
|
<template #default="{active}">
|
||||||
<NodeScheduling
|
<NodeScheduling
|
||||||
@ -274,7 +272,7 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab :label="t('harvester.tab.vmScheduling')" name="vmScheduling" :weight="-90">
|
<Tab :label="t('harvester.tab.vmScheduling')" name="vmScheduling" :weight="-4">
|
||||||
<template #default="{active}">
|
<template #default="{active}">
|
||||||
<PodAffinity
|
<PodAffinity
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
@ -287,13 +285,42 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
|
<Tab
|
||||||
|
:name="t('generic.labels')"
|
||||||
|
:label="t('harvester.tab.instanceLabel')"
|
||||||
|
:weight="-5"
|
||||||
|
>
|
||||||
|
<Labels
|
||||||
|
:default-container-class="'labels-and-annotations-container'"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:display-side-by-side="false"
|
||||||
|
:show-annotations="false"
|
||||||
|
:show-label-title="false"
|
||||||
|
>
|
||||||
|
<template #labels="{toggler}">
|
||||||
|
<KeyValue
|
||||||
|
key="labels"
|
||||||
|
:value="value.instanceLabels"
|
||||||
|
:protected-keys="value.systemLabels || []"
|
||||||
|
:toggle-filter="toggler"
|
||||||
|
:add-label="t('labels.addLabel')"
|
||||||
|
:mode="mode"
|
||||||
|
:read-allowed="false"
|
||||||
|
:value-can-be-empty="true"
|
||||||
|
@input="value.setInstanceLabels($event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Labels>
|
||||||
|
</Tab>
|
||||||
|
|
||||||
<Tab name="advanced" :label="t('harvester.tab.advanced')" :weight="-99">
|
<Tab name="advanced" :label="t('harvester.tab.advanced')" :weight="-99">
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-model:value="runStrategy"
|
v-model:value="runStrategy"
|
||||||
label-key="harvester.virtualMachine.runStrategy"
|
label-key="harvester.virtualMachine.runStrategy"
|
||||||
:options="RunStrategys"
|
:options="runStrategies"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -308,6 +335,37 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-20">
|
||||||
|
<div class="col span-6">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="maintenanceStrategy"
|
||||||
|
label-key="harvester.virtualMachine.maintenanceStrategy.label"
|
||||||
|
:options="maintenanceStrategies"
|
||||||
|
:get-option-label="getMaintenanceStrategyOptionLabel"
|
||||||
|
:mode="mode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col span-6">
|
||||||
|
<Reserved
|
||||||
|
:reserved-memory="reservedMemory"
|
||||||
|
:mode="mode"
|
||||||
|
@updateReserved="updateReserved"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-20">
|
||||||
|
<div class="col span-6">
|
||||||
|
<Checkbox
|
||||||
|
v-model:value="cpuPinning"
|
||||||
|
class="check"
|
||||||
|
type="checkbox"
|
||||||
|
label-key="harvester.virtualMachine.cpuPinning.label"
|
||||||
|
:mode="mode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<a v-if="showAdvanced" v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
<a v-if="showAdvanced" v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
||||||
<a v-else v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
<a v-else v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
||||||
@ -315,13 +373,6 @@ export default {
|
|||||||
|
|
||||||
<div v-if="showAdvanced">
|
<div v-if="showAdvanced">
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<div class="col span-6">
|
|
||||||
<Reserved
|
|
||||||
:reserved-memory="reservedMemory"
|
|
||||||
:mode="mode"
|
|
||||||
@updateReserved="updateReserved"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<UnitInput
|
<UnitInput
|
||||||
v-model:value="terminationGracePeriodSeconds"
|
v-model:value="terminationGracePeriodSeconds"
|
||||||
@ -386,35 +437,6 @@ export default {
|
|||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
|
||||||
:name="t('generic.labels')"
|
|
||||||
:label="t('harvester.tab.instanceLabel')"
|
|
||||||
:weight="-99"
|
|
||||||
>
|
|
||||||
<Labels
|
|
||||||
:default-container-class="'labels-and-annotations-container'"
|
|
||||||
:value="value"
|
|
||||||
:mode="mode"
|
|
||||||
:display-side-by-side="false"
|
|
||||||
:show-annotations="false"
|
|
||||||
:show-label-title="false"
|
|
||||||
>
|
|
||||||
<template #labels="{toggler}">
|
|
||||||
<KeyValue
|
|
||||||
key="labels"
|
|
||||||
:value="value.instanceLabels"
|
|
||||||
:protected-keys="value.systemLabels || []"
|
|
||||||
:toggle-filter="toggler"
|
|
||||||
:add-label="t('labels.addLabel')"
|
|
||||||
:mode="mode"
|
|
||||||
:read-allowed="false"
|
|
||||||
:value-can-be-empty="true"
|
|
||||||
@update:value="value.setInstanceLabels($event)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Labels>
|
|
||||||
</Tab>
|
|
||||||
</Tabbed>
|
</Tabbed>
|
||||||
</CruResource>
|
</CruResource>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export default {
|
|||||||
const hash = await allHash(_hash);
|
const hash = await allHash(_hash);
|
||||||
|
|
||||||
this.snapshots = hash.snapshots;
|
this.snapshots = hash.snapshots;
|
||||||
|
this.images = hash.images;
|
||||||
|
|
||||||
const defaultStorage = this.$store.getters[`harvester/all`](STORAGE_CLASS).find( O => O.isDefault);
|
const defaultStorage = this.$store.getters[`harvester/all`](STORAGE_CLASS).find( O => O.isDefault);
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ export default {
|
|||||||
storage,
|
storage,
|
||||||
imageId,
|
imageId,
|
||||||
snapshots: [],
|
snapshots: [],
|
||||||
|
images: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -108,10 +110,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
imageOption() {
|
imageOption() {
|
||||||
const choices = this.$store.getters['harvester/all'](HCI.IMAGE);
|
|
||||||
|
|
||||||
return sortBy(
|
return sortBy(
|
||||||
choices
|
this.images
|
||||||
.filter(obj => obj.isReady)
|
.filter(obj => obj.isReady)
|
||||||
.map((obj) => {
|
.map((obj) => {
|
||||||
return {
|
return {
|
||||||
@ -249,7 +249,17 @@ export default {
|
|||||||
|
|
||||||
this.value['spec'] = spec;
|
this.value['spec'] = spec;
|
||||||
},
|
},
|
||||||
|
updateImage() {
|
||||||
|
if (this.isVMImage && this.imageId) {
|
||||||
|
const imageResource = this.images?.find(image => this.imageId === image.id);
|
||||||
|
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
|
||||||
|
|
||||||
|
if (imageSize) {
|
||||||
|
this.storage = `${ Math.ceil(imageSize / 1024 / 1024 / 1024) }Gi`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
},
|
||||||
generateYaml() {
|
generateYaml() {
|
||||||
const out = saferDump(this.value);
|
const out = saferDump(this.value);
|
||||||
|
|
||||||
@ -300,7 +310,7 @@ export default {
|
|||||||
required
|
required
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
@update:value="update"
|
@update:value="updateImage"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
|
|||||||
@ -140,15 +140,24 @@ export default {
|
|||||||
|
|
||||||
onImageChange() {
|
onImageChange() {
|
||||||
const imageResource = this.$store.getters['harvester/all'](HCI.IMAGE)?.find( I => this.value.image === I.id);
|
const imageResource = this.$store.getters['harvester/all'](HCI.IMAGE)?.find( I => this.value.image === I.id);
|
||||||
|
const isIsoImage = /iso$/i.test(imageResource?.imageSuffix);
|
||||||
|
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
|
||||||
|
|
||||||
if (this.idx === 0) {
|
if (isIsoImage) {
|
||||||
if (/iso$/i.test(imageResource?.imageSuffix)) {
|
this.value['type'] = 'cd-rom';
|
||||||
this.value['type'] = 'cd-rom';
|
this.value['bus'] = 'sata';
|
||||||
this.value['bus'] = 'sata';
|
} else {
|
||||||
} else {
|
this.value['type'] = 'disk';
|
||||||
this.value['type'] = 'disk';
|
this.value['bus'] = 'virtio';
|
||||||
this.value['bus'] = 'virtio';
|
}
|
||||||
|
|
||||||
|
if (imageSize) {
|
||||||
|
let imageSizeGiB = Math.ceil(imageSize / 1024 / 1024 / 1024);
|
||||||
|
|
||||||
|
if (!isIsoImage) {
|
||||||
|
imageSizeGiB = Math.max(imageSizeGiB, 10);
|
||||||
}
|
}
|
||||||
|
this.value['size'] = `${ imageSizeGiB }Gi`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
|
|||||||
@ -28,7 +28,6 @@ import CreateEditView from '@shell/mixins/create-edit-view';
|
|||||||
|
|
||||||
import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
||||||
import VM_MIXIN from '../../mixins/harvester-vm';
|
import VM_MIXIN from '../../mixins/harvester-vm';
|
||||||
import { RunStrategys } from '../../config/harvester-map';
|
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import RestartVMDialog from '../../dialog/RestartVMDialog';
|
import RestartVMDialog from '../../dialog/RestartVMDialog';
|
||||||
import VirtualMachineVGpuDevices from './VirtualMachineVGpuDevices/index';
|
import VirtualMachineVGpuDevices from './VirtualMachineVGpuDevices/index';
|
||||||
@ -40,6 +39,8 @@ import Network from './VirtualMachineNetwork';
|
|||||||
import Volume from './VirtualMachineVolume';
|
import Volume from './VirtualMachineVolume';
|
||||||
import SSHKey from './VirtualMachineSSHKey';
|
import SSHKey from './VirtualMachineSSHKey';
|
||||||
import Reserved from './VirtualMachineReserved';
|
import Reserved from './VirtualMachineReserved';
|
||||||
|
import { Banner } from '@components/Banner';
|
||||||
|
import MessageLink from '@shell/components/MessageLink';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditVM',
|
name: 'HarvesterEditVM',
|
||||||
@ -68,6 +69,8 @@ export default {
|
|||||||
UnitInput,
|
UnitInput,
|
||||||
VirtualMachineVGpuDevices,
|
VirtualMachineVGpuDevices,
|
||||||
KeyValue,
|
KeyValue,
|
||||||
|
Banner,
|
||||||
|
MessageLink
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CreateEditView, VM_MIXIN],
|
mixins: [CreateEditView, VM_MIXIN],
|
||||||
@ -96,13 +99,19 @@ export default {
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
hostname,
|
hostname,
|
||||||
isRestartImmediately,
|
isRestartImmediately,
|
||||||
RunStrategys,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({ t: 'i18n/t' }),
|
...mapGetters({ t: 'i18n/t' }),
|
||||||
|
|
||||||
|
to() {
|
||||||
|
return {
|
||||||
|
name: 'harvester-c-cluster-resource',
|
||||||
|
params: { cluster: this.$store.getters['clusterId'], resource: HCI.HOST },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
machineTypeOptions() {
|
machineTypeOptions() {
|
||||||
return [{
|
return [{
|
||||||
label: 'None',
|
label: 'None',
|
||||||
@ -173,6 +182,26 @@ export default {
|
|||||||
hasStartAction() {
|
hasStartAction() {
|
||||||
return this.value.hasAction('start');
|
return this.value.hasAction('start');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
enableCpuPinningCheckbox() {
|
||||||
|
if (this.mode === 'create') {
|
||||||
|
return this.nodes.some(node => node.isCPUManagerEnabled); // any one of nodes has label cpuManager=true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
showCpuPinningBanner() {
|
||||||
|
if (this.mode === 'edit') {
|
||||||
|
return this.cpuPinning !== !!this.cloneVM.spec.template.spec.domain.cpu.dedicatedCpuPlacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mode === 'create') {
|
||||||
|
return this.nodes.every(node => !node.isCPUManagerEnabled); // no node enabled CPU manager
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@ -273,6 +302,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
cancelAction() {
|
||||||
|
const { fromPage = HCI.VM } = this.$route?.query; // default back to VM list page
|
||||||
|
const cancelOverride = {
|
||||||
|
name: this.doneRoute,
|
||||||
|
params: { resource: fromPage }
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$router.replace(cancelOverride);
|
||||||
|
},
|
||||||
saveVM(buttonCb) {
|
saveVM(buttonCb) {
|
||||||
clear(this.errors);
|
clear(this.errors);
|
||||||
|
|
||||||
@ -437,12 +475,14 @@ export default {
|
|||||||
id="vm"
|
id="vm"
|
||||||
:done-route="doneRoute"
|
:done-route="doneRoute"
|
||||||
:resource="value"
|
:resource="value"
|
||||||
|
:cancelEvent="true"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:can-yaml="isSingle ? true : false"
|
:can-yaml="isSingle ? true : false"
|
||||||
:errors="errors"
|
:errors="errors"
|
||||||
:generate-yaml="generateYaml"
|
:generate-yaml="generateYaml"
|
||||||
:apply-hooks="applyHooks"
|
:apply-hooks="applyHooks"
|
||||||
@finish="saveVM"
|
@finish="saveVM"
|
||||||
|
@cancel="cancelAction"
|
||||||
>
|
>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
v-if="isCreate"
|
v-if="isCreate"
|
||||||
@ -606,44 +646,54 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab v-if="enabledSriovgpu" :label="t('harvester.tab.vGpuDevices')" name="vGpuDevices" :weight="-6">
|
||||||
v-if="enabledSriovgpu"
|
<VirtualMachineVGpuDevices :mode="mode" :value="spec.template.spec" :vm="value" />
|
||||||
:label="t('harvester.tab.vGpuDevices')"
|
</Tab>
|
||||||
name="vGpuDevices"
|
|
||||||
:weight="-6"
|
<Tab v-if="isEdit" :label="t('harvester.tab.accessCredentials')" name="accessCredentials" :weight="-7">
|
||||||
>
|
<AccessCredentials v-model:value="accessCredentials" :mode="mode" :resource="value" :is-qemu-installed="isQemuInstalled" />
|
||||||
<VirtualMachineVGpuDevices
|
|
||||||
:mode="mode"
|
|
||||||
:value="spec.template.spec"
|
|
||||||
:vm="value"
|
|
||||||
/>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
v-if="isEdit"
|
name="instanceLabel"
|
||||||
:label="t('harvester.tab.accessCredentials')"
|
:label="t('harvester.tab.instanceLabel')"
|
||||||
name="accessCredentials"
|
:weight="-8"
|
||||||
:weight="-7"
|
|
||||||
>
|
>
|
||||||
<AccessCredentials
|
<Labels
|
||||||
v-model:value="accessCredentials"
|
:default-container-class="'labels-and-annotations-container'"
|
||||||
|
:value="value"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:resource="value"
|
:display-side-by-side="false"
|
||||||
:is-qemu-installed="isQemuInstalled"
|
:show-annotations="false"
|
||||||
/>
|
:show-label-title="false"
|
||||||
|
>
|
||||||
|
<template #labels="{toggler}">
|
||||||
|
<KeyValue
|
||||||
|
key="labels"
|
||||||
|
:value="value.instanceLabels"
|
||||||
|
:protected-keys="value.systemLabels || []"
|
||||||
|
:toggle-filter="toggler"
|
||||||
|
:add-label="t('labels.addLabel')"
|
||||||
|
:mode="mode"
|
||||||
|
:read-allowed="false"
|
||||||
|
:value-can-be-empty="true"
|
||||||
|
@update:value="value.setInstanceLabels($event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Labels>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
name="advanced"
|
name="advanced"
|
||||||
:label="t('harvester.tab.advanced')"
|
:label="t('harvester.tab.advanced')"
|
||||||
:weight="-8"
|
:weight="-9"
|
||||||
>
|
>
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-model:value="runStrategy"
|
v-model:value="runStrategy"
|
||||||
label-key="harvester.virtualMachine.runStrategy"
|
label-key="harvester.virtualMachine.runStrategy"
|
||||||
:options="RunStrategys"
|
:options="runStrategies"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -659,24 +709,30 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<a
|
<div class="col span-6">
|
||||||
v-if="showAdvanced"
|
<LabeledSelect
|
||||||
v-t="'harvester.generic.showMore'"
|
v-model:value="maintenanceStrategy"
|
||||||
role="button"
|
label-key="harvester.virtualMachine.maintenanceStrategy.label"
|
||||||
@click="toggleAdvanced"
|
:options="maintenanceStrategies"
|
||||||
/>
|
:get-option-label="getMaintenanceStrategyOptionLabel"
|
||||||
<a
|
:mode="mode"
|
||||||
v-else
|
/>
|
||||||
v-t="'harvester.generic.showMore'"
|
</div>
|
||||||
role="button"
|
<div class="col span-6">
|
||||||
@click="toggleAdvanced"
|
<Reserved
|
||||||
/>
|
:reserved-memory="reservedMemory"
|
||||||
|
:mode="mode"
|
||||||
|
@updateReserved="updateReserved"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="row mb-20">
|
||||||
v-if="showAdvanced"
|
<a v-if="showAdvanced" v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
||||||
class="mb-20"
|
<a v-else v-t="'harvester.generic.showMore'" role="button" @click="toggleAdvanced" />
|
||||||
>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showAdvanced" class="mb-20">
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<LabeledInput
|
<LabeledInput
|
||||||
@ -698,13 +754,6 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<div class="col span-6">
|
|
||||||
<Reserved
|
|
||||||
:reserved-memory="reservedMemory"
|
|
||||||
:mode="mode"
|
|
||||||
@updateReserved="updateReserved"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<UnitInput
|
<UnitInput
|
||||||
v-model:value="terminationGracePeriodSeconds"
|
v-model:value="terminationGracePeriodSeconds"
|
||||||
@ -730,6 +779,16 @@ export default {
|
|||||||
@updateDataTemplateId="updateDataTemplateId"
|
@updateDataTemplateId="updateDataTemplateId"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
v-model:value="cpuPinning"
|
||||||
|
:disabled="!enableCpuPinningCheckbox"
|
||||||
|
class="check"
|
||||||
|
type="checkbox"
|
||||||
|
tooltip-key="harvester.virtualMachine.cpuPinning.tooltip"
|
||||||
|
label-key="harvester.virtualMachine.cpuPinning.label"
|
||||||
|
:mode="mode"
|
||||||
|
/>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model:value="installUSBTablet"
|
v-model:value="installUSBTablet"
|
||||||
class="check mt-20"
|
class="check mt-20"
|
||||||
@ -773,35 +832,21 @@ export default {
|
|||||||
:label="t('harvester.virtualMachine.secureBoot')"
|
:label="t('harvester.virtualMachine.secureBoot')"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
<Banner
|
||||||
|
v-if="showCpuPinningBanner"
|
||||||
<Tab
|
color="warning"
|
||||||
name="instanceLabel"
|
|
||||||
:label="t('harvester.tab.instanceLabel')"
|
|
||||||
:weight="-99"
|
|
||||||
>
|
|
||||||
<Labels
|
|
||||||
:default-container-class="'labels-and-annotations-container'"
|
|
||||||
:value="value"
|
|
||||||
:mode="mode"
|
|
||||||
:display-side-by-side="false"
|
|
||||||
:show-annotations="false"
|
|
||||||
:show-label-title="false"
|
|
||||||
>
|
>
|
||||||
<template #labels="{toggler}">
|
<MessageLink
|
||||||
<KeyValue
|
v-if="mode === 'create'"
|
||||||
key="labels"
|
:to="to"
|
||||||
:value="value.instanceLabels"
|
prefix-label="harvester.virtualMachine.advancedOptions.cpuManager.prefix"
|
||||||
:protected-keys="value.systemLabels || []"
|
middle-label="harvester.virtualMachine.advancedOptions.cpuManager.middle"
|
||||||
:toggle-filter="toggler"
|
suffix-label="harvester.virtualMachine.advancedOptions.cpuManager.suffix"
|
||||||
:add-label="t('labels.addLabel')"
|
/>
|
||||||
:mode="mode"
|
<span v-if="mode==='edit'">
|
||||||
:read-allowed="false"
|
{{ t('harvester.virtualMachine.cpuPinning.restartVMMessage') }}
|
||||||
:value-can-be-empty="true"
|
</span>
|
||||||
@update:value="value.setInstanceLabels($event)"
|
</Banner>
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Labels>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabbed>
|
</Tabbed>
|
||||||
|
|
||||||
|
|||||||
@ -314,12 +314,21 @@ export default {
|
|||||||
} else if (selector[HOSTNAME] && Object.keys(selector).length === 1) {
|
} else if (selector[HOSTNAME] && Object.keys(selector).length === 1) {
|
||||||
const matchNode = allNodes.find(n => n.id === selector[HOSTNAME]);
|
const matchNode = allNodes.find(n => n.id === selector[HOSTNAME]);
|
||||||
|
|
||||||
this.matchingNodes = {
|
if (matchNode) {
|
||||||
matched: 1,
|
this.matchingNodes = {
|
||||||
total: allNodes.length,
|
matched: 1,
|
||||||
none: false,
|
total: allNodes.length,
|
||||||
sample: matchNode ? matchNode.nameDisplay : selector[HOSTNAME],
|
none: false,
|
||||||
};
|
sample: matchNode.nameDisplay,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.matchingNodes = {
|
||||||
|
matched: 0,
|
||||||
|
total: 0,
|
||||||
|
none: true,
|
||||||
|
sample: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const match = matching(allNodes, selector);
|
const match = matching(allNodes, selector);
|
||||||
|
|
||||||
|
|||||||
44
pkg/harvester/formatters/HarvesterCPUPinning.vue
Normal file
44
pkg/harvester/formatters/HarvesterCPUPinning.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HarvesterCPUPinningFormatter',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String, // id
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
row() {
|
||||||
|
return this.rows.find(r => r.id === this.value);
|
||||||
|
},
|
||||||
|
cpuManagerStatus() {
|
||||||
|
if (this.row?.isCPUManagerEnableInProgress) {
|
||||||
|
return this.t('generic.loading');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.row?.isCPUManagerEnabled ? this.t('generic.enabled') : this.t('generic.disabled');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span v-if="row?.isCPUManagerEnableInProgress" v-clean-tooltip="cpuManagerStatus">
|
||||||
|
<i class="icon icon-spinner icon-spin" />
|
||||||
|
</span>
|
||||||
|
<span v-else-if="row?.isCPUManagerEnabled" v-clean-tooltip="cpuManagerStatus">
|
||||||
|
<i class="icon icon-checkmark" />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
v-clean-tooltip="cpuManagerStatus"
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
—
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
@ -23,77 +23,76 @@ export default {
|
|||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
|
||||||
showReserved: {
|
showAllocated: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
this.longhornSettings = await this.$store.dispatch(`${ inStore }/findAll`, { type: LONGHORN.SETTINGS });
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {};
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const longhornSettings = this.$store.getters[`${ inStore }/all`](LONGHORN.SETTINGS) || [];
|
||||||
|
|
||||||
|
return { longhornSettings };
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
usage() {
|
storageStats() {
|
||||||
|
const stats = {
|
||||||
|
used: 0,
|
||||||
|
scheduled: 0,
|
||||||
|
maximum: 0,
|
||||||
|
reserved: 0,
|
||||||
|
total: 0
|
||||||
|
};
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
const longhornNode = this.$store.getters[`${ inStore }/byId`](LONGHORN.NODES, `longhorn-system/${ this.row.id }`) || {};
|
const node = this.$store.getters[`${ inStore }/byId`](LONGHORN.NODES, `longhorn-system/${ this.row.id }`) || {};
|
||||||
|
const storageOverProvisioningPercentageSetting = this.longhornSettings.find(s => s.id === 'longhorn-system/storage-over-provisioning-percentage');
|
||||||
|
const disks = node?.spec?.disks || {};
|
||||||
|
const diskStatus = node?.status?.diskStatus || {};
|
||||||
|
|
||||||
return longhornNode?.used || 0;
|
stats.used += node?.spec?.allowScheduling ? node.used : 0;
|
||||||
},
|
|
||||||
|
|
||||||
reserved() {
|
Object.keys(disks).map((key) => {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
stats.scheduled += node?.spec?.allowScheduling ? (diskStatus[key]?.storageScheduled || 0) : 0;
|
||||||
const longhornNode = this.$store.getters[`${ inStore }/byId`](LONGHORN.NODES, `longhorn-system/${ this.row.id }`);
|
stats.reserved += disks[key]?.storageReserved || 0;
|
||||||
let reserved = 0;
|
});
|
||||||
|
Object.values(diskStatus).map((diskStat) => {
|
||||||
const disks = longhornNode?.spec?.disks || {};
|
stats.maximum += diskStat?.storageMaximum || 0;
|
||||||
|
|
||||||
Object.values(disks).map((disk) => {
|
|
||||||
if (disk.allowScheduling) {
|
|
||||||
reserved += disk.storageReserved;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return reserved;
|
stats.total = ((stats.maximum - stats.reserved) * Number(storageOverProvisioningPercentageSetting?.value ?? 0)) / 100;
|
||||||
},
|
|
||||||
|
|
||||||
total() {
|
return stats;
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
const longhornNode = this.$store.getters[`${ inStore }/byId`](LONGHORN.NODES, `longhorn-system/${ this.row.id }`);
|
|
||||||
let out = 0;
|
|
||||||
|
|
||||||
const diskStatus = longhornNode?.status?.diskStatus || {};
|
|
||||||
|
|
||||||
Object.values(diskStatus).map((disk) => {
|
|
||||||
if (disk?.storageMaximum) {
|
|
||||||
out += disk.storageMaximum;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return out;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
units() {
|
units() {
|
||||||
const exponent = exponentNeeded(this.total, 1024);
|
const exponent = exponentNeeded(this.storageStats.maximum, 1024);
|
||||||
|
|
||||||
return `${ UNITS[exponent] }iB`;
|
return `${ UNITS[exponent] }iB`;
|
||||||
},
|
},
|
||||||
|
|
||||||
used() {
|
used() {
|
||||||
let out = this.formatter(this.usage || 0);
|
let out = this.formatter(this.storageStats.used);
|
||||||
|
|
||||||
if (!Number.parseFloat(out) > 0) {
|
if (!Number.parseFloat(out) > 0) {
|
||||||
out = this.formatter(this.usage || 0, { canRoundToZero: false });
|
out = this.formatter(this.storageStats.used, { canRoundToZero: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
formatReserved() {
|
formatAllocated() {
|
||||||
let out = this.formatter(this.reserved || 0);
|
let out = this.formatter(this.storageStats.scheduled);
|
||||||
|
|
||||||
if (!Number.parseFloat(out) > 0) {
|
if (!Number.parseFloat(out) > 0) {
|
||||||
out = this.formatter(this.reserved || 0, { canRoundToZero: false });
|
out = this.formatter(this.storageStats.scheduled, { canRoundToZero: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@ -102,15 +101,15 @@ export default {
|
|||||||
usedAmountTemplateValues() {
|
usedAmountTemplateValues() {
|
||||||
return {
|
return {
|
||||||
used: this.used,
|
used: this.used,
|
||||||
total: this.formatter(this.total || 0),
|
total: this.formatter(this.storageStats.maximum),
|
||||||
unit: this.units,
|
unit: this.units,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
reservedAmountTemplateValues() {
|
allocatedAmountTemplateValues() {
|
||||||
return {
|
return {
|
||||||
used: this.formatReserved,
|
used: this.formatAllocated,
|
||||||
total: this.formatter(this.total || 0),
|
total: this.formatter(this.storageStats.total),
|
||||||
unit: this.units,
|
unit: this.units,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -118,7 +117,7 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
formatter(value, format) {
|
formatter(value, format) {
|
||||||
const minExponent = exponentNeeded(this.total, 1024);
|
const minExponent = exponentNeeded(this.storageStats.maximum, 1024);
|
||||||
const formatOptions = {
|
const formatOptions = {
|
||||||
addSuffix: false,
|
addSuffix: false,
|
||||||
increment: 1024,
|
increment: 1024,
|
||||||
@ -137,21 +136,21 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-if="showReserved"
|
v-if="showAllocated"
|
||||||
>
|
>
|
||||||
<ConsumptionGauge
|
<ConsumptionGauge
|
||||||
:capacity="total"
|
:capacity="storageStats.total"
|
||||||
:used="reserved"
|
:used="storageStats.scheduled"
|
||||||
:units="units"
|
:units="units"
|
||||||
:number-formatter="formatter"
|
:number-formatter="formatter"
|
||||||
:resource-name="resourceName"
|
:resource-name="resourceName"
|
||||||
>
|
>
|
||||||
<template #title="{formattedPercentage}">
|
<template #title="{formattedPercentage}">
|
||||||
<span>
|
<span>
|
||||||
{{ t('clusterIndexPage.hardwareResourceGauge.reserved') }}
|
{{ t('clusterIndexPage.hardwareResourceGauge.allocated') }}
|
||||||
</span>
|
</span>
|
||||||
<span class="precent-data">
|
<span class="precent-data">
|
||||||
{{ t('node.detail.glance.consumptionGauge.amount', reservedAmountTemplateValues) }}
|
{{ t('node.detail.glance.consumptionGauge.amount', allocatedAmountTemplateValues) }}
|
||||||
<span class="ml-10 percentage">
|
<span class="ml-10 percentage">
|
||||||
/ {{ formattedPercentage }}
|
/ {{ formattedPercentage }}
|
||||||
</span>
|
</span>
|
||||||
@ -160,13 +159,13 @@ export default {
|
|||||||
</ConsumptionGauge>
|
</ConsumptionGauge>
|
||||||
</div>
|
</div>
|
||||||
<ConsumptionGauge
|
<ConsumptionGauge
|
||||||
:capacity="total"
|
:capacity="storageStats.maximum"
|
||||||
:used="usage"
|
:used="storageStats.used"
|
||||||
:units="units"
|
:units="units"
|
||||||
:number-formatter="formatter"
|
:number-formatter="formatter"
|
||||||
:resource-name="showReserved ? '' : resourceName"
|
:resource-name="showAllocated ? '' : resourceName"
|
||||||
:class="{
|
:class="{
|
||||||
'mt-10': showReserved,
|
'mt-10': showAllocated,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #title="{formattedPercentage}">
|
<template #title="{formattedPercentage}">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -134,15 +134,16 @@ export default {
|
|||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
const hash = {
|
const hash = {
|
||||||
vms: this.fetchClusterResources(HCI.VM),
|
vms: this.fetchClusterResources(HCI.VM),
|
||||||
nodes: this.fetchClusterResources(NODE),
|
nodes: this.fetchClusterResources(NODE),
|
||||||
events: this.fetchClusterResources(EVENT),
|
events: this.fetchClusterResources(EVENT),
|
||||||
metricNodes: this.fetchClusterResources(METRIC.NODE),
|
metricNodes: this.fetchClusterResources(METRIC.NODE),
|
||||||
settings: this.fetchClusterResources(HCI.SETTING),
|
settings: this.fetchClusterResources(HCI.SETTING),
|
||||||
services: this.fetchClusterResources(SERVICE),
|
services: this.fetchClusterResources(SERVICE),
|
||||||
metric: this.fetchClusterResources(METRIC.NODE),
|
metric: this.fetchClusterResources(METRIC.NODE),
|
||||||
longhornNode: this.fetchClusterResources(LONGHORN.NODES) || [],
|
longhornNodes: this.fetchClusterResources(LONGHORN.NODES),
|
||||||
_pods: this.$store.dispatch('harvester/findAll', { type: POD }),
|
longhornSettings: this.fetchClusterResources(LONGHORN.SETTINGS),
|
||||||
|
_pods: this.$store.dispatch('harvester/findAll', { type: POD }),
|
||||||
};
|
};
|
||||||
|
|
||||||
(this.accessibleResources || []).map((a) => {
|
(this.accessibleResources || []).map((a) => {
|
||||||
@ -155,6 +156,10 @@ export default {
|
|||||||
hash.addons = this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS });
|
hash.addons = this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.ADD_ONS });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.$store.getters[`${ inStore }/schemaFor`](LONGHORN.NODES)) {
|
||||||
|
this.hasLonghornSchema = true;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await allHash(hash);
|
const res = await allHash(hash);
|
||||||
|
|
||||||
for ( const k in res ) {
|
for ( const k in res ) {
|
||||||
@ -226,6 +231,7 @@ export default {
|
|||||||
showClusterMetrics: false,
|
showClusterMetrics: false,
|
||||||
showVmMetrics: false,
|
showVmMetrics: false,
|
||||||
enabledMonitoringAddon: false,
|
enabledMonitoringAddon: false,
|
||||||
|
hasLonghornSchema: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -305,8 +311,7 @@ export default {
|
|||||||
|
|
||||||
currentVersion() {
|
currentVersion() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
const settings = this.$store.getters[`${ inStore }/all`](HCI.SETTING);
|
const setting = this.$store.getters[`${ inStore }/byId`](HCI.SETTING, 'server-version');
|
||||||
const setting = settings.find( S => S.id === 'server-version');
|
|
||||||
|
|
||||||
return setting?.value || setting?.default;
|
return setting?.value || setting?.default;
|
||||||
},
|
},
|
||||||
@ -364,53 +369,46 @@ export default {
|
|||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
storageUsage() {
|
storageStats() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const storageOverProvisioningPercentageSetting = this.longhornSettings.find(s => s.id === 'longhorn-system/storage-over-provisioning-percentage');
|
||||||
const longhornNodes = this.$store.getters[`${ inStore }/all`](LONGHORN.NODES) || [];
|
const stats = this.longhornNodes.reduce((total, node) => {
|
||||||
|
|
||||||
return longhornNodes.filter(node => node.spec?.allowScheduling).reduce((total, node) => {
|
|
||||||
return total + node.used;
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
storageReservedTotal() {
|
|
||||||
let out = 0;
|
|
||||||
|
|
||||||
(this.longhornNode || []).filter(node => node.spec?.allowScheduling).forEach((node) => {
|
|
||||||
const disks = node?.spec?.disks || {};
|
const disks = node?.spec?.disks || {};
|
||||||
|
|
||||||
Object.values(disks).map((disk) => {
|
|
||||||
if (disk.allowScheduling) {
|
|
||||||
out += disk.storageReserved;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return out;
|
|
||||||
},
|
|
||||||
|
|
||||||
storageTotal() {
|
|
||||||
let out = 0;
|
|
||||||
|
|
||||||
(this.longhornNode || []).forEach((node) => {
|
|
||||||
const diskStatus = node?.status?.diskStatus || {};
|
const diskStatus = node?.status?.diskStatus || {};
|
||||||
|
|
||||||
Object.values(diskStatus).map((disk) => {
|
total.used += node?.spec?.allowScheduling ? node.used : 0;
|
||||||
if (disk?.storageMaximum) {
|
|
||||||
out += disk.storageMaximum;
|
Object.keys(disks).map((key) => {
|
||||||
}
|
total.scheduled += node?.spec?.allowScheduling ? (diskStatus[key]?.storageScheduled || 0) : 0;
|
||||||
|
total.reserved += disks[key]?.storageReserved || 0;
|
||||||
});
|
});
|
||||||
|
Object.values(diskStatus).map((diskStat) => {
|
||||||
|
total.maximum += diskStat?.storageMaximum || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}, {
|
||||||
|
used: 0,
|
||||||
|
scheduled: 0,
|
||||||
|
maximum: 0,
|
||||||
|
reserved: 0,
|
||||||
|
total: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
return out;
|
stats.total = ((stats.maximum - stats.reserved) * Number(storageOverProvisioningPercentageSetting?.value ?? 0)) / 100;
|
||||||
|
|
||||||
|
return stats;
|
||||||
},
|
},
|
||||||
|
|
||||||
storageUsed() {
|
storageUsed() {
|
||||||
return this.createMemoryValues(this.storageTotal, this.storageUsage);
|
const stats = this.storageStats;
|
||||||
|
|
||||||
|
return this.createMemoryValues(stats.maximum, stats.used);
|
||||||
},
|
},
|
||||||
|
|
||||||
storageReserved() {
|
storageAllocated() {
|
||||||
return this.createMemoryValues(this.storageTotal, this.storageReservedTotal);
|
const stats = this.storageStats;
|
||||||
|
|
||||||
|
return this.createMemoryValues(stats.total, stats.scheduled);
|
||||||
},
|
},
|
||||||
|
|
||||||
vmEvents() {
|
vmEvents() {
|
||||||
@ -644,7 +642,7 @@ export default {
|
|||||||
<div
|
<div
|
||||||
class="hardware-resource-gauges"
|
class="hardware-resource-gauges"
|
||||||
:class="{
|
:class="{
|
||||||
live: !storageTotal,
|
live: !hasLonghornSchema,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<HardwareResourceGauge
|
<HardwareResourceGauge
|
||||||
@ -658,10 +656,11 @@ export default {
|
|||||||
:used="ramUsed"
|
:used="ramUsed"
|
||||||
/>
|
/>
|
||||||
<HardwareResourceGauge
|
<HardwareResourceGauge
|
||||||
v-if="storageTotal"
|
v-if="hasLonghornSchema"
|
||||||
:name="t('harvester.dashboard.hardwareResourceGauge.storage')"
|
:name="t('harvester.dashboard.hardwareResourceGauge.storage')"
|
||||||
:used="storageUsed"
|
:used="storageUsed"
|
||||||
:reserved="storageReserved"
|
:reserved="storageAllocated"
|
||||||
|
:reserved-title="t('clusterIndexPage.hardwareResourceGauge.allocated')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import metricPoller from '@shell/mixins/metric-poller';
|
import metricPoller from '@shell/mixins/metric-poller';
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
|
import { DOC_LINKS } from '../config/doc-links';
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
id: HCI.HOST,
|
id: HCI.HOST,
|
||||||
@ -91,6 +92,7 @@ export default {
|
|||||||
value: 'internalIp',
|
value: 'internalIp',
|
||||||
formatter: 'CopyToClipboard',
|
formatter: 'CopyToClipboard',
|
||||||
sort: ['internalIp'],
|
sort: ['internalIp'],
|
||||||
|
align: 'center',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -98,7 +100,7 @@ export default {
|
|||||||
const metricCol = [
|
const metricCol = [
|
||||||
{
|
{
|
||||||
name: 'cpu',
|
name: 'cpu',
|
||||||
labelKey: 'tableHeaders.cpu',
|
labelKey: 'node.detail.glance.consumptionGauge.cpu',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
formatter: 'HarvesterCPUUsed',
|
formatter: 'HarvesterCPUUsed',
|
||||||
formatterOpts: { showUsed: true },
|
formatterOpts: { showUsed: true },
|
||||||
@ -121,12 +123,23 @@ export default {
|
|||||||
labelKey: 'tableHeaders.storage',
|
labelKey: 'tableHeaders.storage',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
formatter: 'HarvesterStorageUsed',
|
formatter: 'HarvesterStorageUsed',
|
||||||
formatterOpts: { showReserved: true },
|
formatterOpts: { showAllocated: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
out.splice(-1, 0, storageHeader);
|
out.splice(-1, 0, storageHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
name: 'cpuManager',
|
||||||
|
labelKey: 'harvester.tableHeaders.cpuManager',
|
||||||
|
value: 'id',
|
||||||
|
formatter: 'HarvesterCPUPinning',
|
||||||
|
formatterOpts: { rows: this.rows },
|
||||||
|
width: 150,
|
||||||
|
align: 'center',
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
if (this.hasLonghornSchema) {
|
if (this.hasLonghornSchema) {
|
||||||
out.push({
|
out.push({
|
||||||
name: 'diskState',
|
name: 'diskState',
|
||||||
@ -143,7 +156,7 @@ export default {
|
|||||||
name: 'console',
|
name: 'console',
|
||||||
label: ' ',
|
label: ' ',
|
||||||
align: 'right',
|
align: 'right',
|
||||||
width: 65,
|
width: 80,
|
||||||
});
|
});
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@ -151,6 +164,10 @@ export default {
|
|||||||
|
|
||||||
schema() {
|
schema() {
|
||||||
return schema;
|
return schema;
|
||||||
|
},
|
||||||
|
|
||||||
|
consoleDocLink() {
|
||||||
|
return DOC_LINKS.CONSOLE_URL;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -169,7 +186,15 @@ export default {
|
|||||||
|
|
||||||
goto(row) {
|
goto(row) {
|
||||||
window.open(row.consoleUrl, '_blank');
|
window.open(row.consoleUrl, '_blank');
|
||||||
}
|
},
|
||||||
|
|
||||||
|
consoleTooltip(row) {
|
||||||
|
if (!row.consoleUrl) {
|
||||||
|
return this.t('harvester.host.noConsoleUrl');
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
typeDisplay() {
|
typeDisplay() {
|
||||||
@ -199,10 +224,19 @@ export default {
|
|||||||
|
|
||||||
>
|
>
|
||||||
<template #cell:console="{row}">
|
<template #cell:console="{row}">
|
||||||
<button type="button" class="btn btn-sm role-primary" :disabled="!row.consoleUrl" @click="goto(row)">
|
<div class="console-button">
|
||||||
{{ t('harvester.host.console') }}
|
<button v-clean-tooltip="consoleTooltip(row)" type="button" class="mr-5 btn btn-sm role-primary" :disabled="!row.consoleUrl" @click="goto(row)">
|
||||||
</button>
|
{{ t('harvester.host.console') }}
|
||||||
|
</button>
|
||||||
|
<a v-if="!row.consoleUrl" :href="consoleDocLink" target="_blank"><i class="icon icon-info" /></a>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ResourceTable>
|
</ResourceTable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.console-button {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -103,7 +103,6 @@ export default {
|
|||||||
:schema="schema"
|
:schema="schema"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
key-field="_key"
|
key-field="_key"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -56,8 +56,6 @@ export default {
|
|||||||
this.$store.dispatch('type-map/configureType', { match: HCI.VOLUME, isCreatable: false });
|
this.$store.dispatch('type-map/configureType', { match: HCI.VOLUME, isCreatable: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('ppppppppppp', hash);
|
|
||||||
|
|
||||||
this.rows = hash.pvcs;
|
this.rows = hash.pvcs;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -151,23 +149,23 @@ export default {
|
|||||||
|
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
cell:state="scope"
|
cell:state="scope"
|
||||||
>
|
>
|
||||||
<div class="state">
|
<div class="state">
|
||||||
<HarvesterVolumeState
|
<HarvesterVolumeState
|
||||||
class="vmstate"
|
class="vmstate"
|
||||||
:row="scope.row"
|
:row="scope.row"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
cell:AttachedVM="scope"
|
cell:AttachedVM="scope"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="getVMName(scope.row)"
|
v-if="getVMName(scope.row)"
|
||||||
:to="goTo(scope.row)"
|
:to="goTo(scope.row)"
|
||||||
>
|
>
|
||||||
{{ getVMName(scope.row) }}
|
{{ getVMName(scope.row) }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -81,6 +81,10 @@ export default {
|
|||||||
backups: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.BACKUP }),
|
backups: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.BACKUP }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.$store.getters[`${ inStore }/schemaFor`](HCI.RESOURCE_QUOTA)) {
|
||||||
|
_hash.resourceQuotas = this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.RESOURCE_QUOTA });
|
||||||
|
}
|
||||||
|
|
||||||
if (this.$store.getters[`${ inStore }/schemaFor`](NODE)) {
|
if (this.$store.getters[`${ inStore }/schemaFor`](NODE)) {
|
||||||
_hash.nodes = this.$store.dispatch(`${ inStore }/findAll`, { type: NODE });
|
_hash.nodes = this.$store.dispatch(`${ inStore }/findAll`, { type: NODE });
|
||||||
this.hasNode = true;
|
this.hasNode = true;
|
||||||
@ -116,7 +120,7 @@ export default {
|
|||||||
headers() {
|
headers() {
|
||||||
const restoreCol = {
|
const restoreCol = {
|
||||||
name: 'restoreProgress',
|
name: 'restoreProgress',
|
||||||
labelKey: 'tableHeaders.restore',
|
labelKey: 'harvester.tableHeaders.restore',
|
||||||
value: 'restoreProgress',
|
value: 'restoreProgress',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
formatter: 'HarvesterBackupProgressBar',
|
formatter: 'HarvesterBackupProgressBar',
|
||||||
@ -128,7 +132,7 @@ export default {
|
|||||||
value: 'nodeName',
|
value: 'nodeName',
|
||||||
sort: ['realAttachNodeName'],
|
sort: ['realAttachNodeName'],
|
||||||
formatter: 'HarvesterHost',
|
formatter: 'HarvesterHost',
|
||||||
labelKey: 'tableHeaders.node'
|
labelKey: 'harvester.tableHeaders.vm.node'
|
||||||
};
|
};
|
||||||
|
|
||||||
const cols = clone(VM_HEADERS);
|
const cols = clone(VM_HEADERS);
|
||||||
@ -137,7 +141,7 @@ export default {
|
|||||||
cols.splice(-1, 0, nodeCol);
|
cols.splice(-1, 0, nodeCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasRestoredVMs) {
|
if (this.hasBackUpRestoreInProgress) {
|
||||||
cols.splice(-1, 0, restoreCol);
|
cols.splice(-1, 0, restoreCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,8 +154,11 @@ export default {
|
|||||||
return [...this.allVMs, ...matchVMIs];
|
return [...this.allVMs, ...matchVMIs];
|
||||||
},
|
},
|
||||||
|
|
||||||
hasRestoredVMs() {
|
/**
|
||||||
return !!this.rows.find(r => !!r.restoreResource);
|
* We want to show the progress bar only for Backup's restore; snapshot's restore is immediate.
|
||||||
|
*/
|
||||||
|
hasBackUpRestoreInProgress() {
|
||||||
|
return !!this.rows.find(r => r.restoreResource && !r.restoreResource.fromSnapshot && !r.restoreResource.isComplete);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -179,7 +186,7 @@ export default {
|
|||||||
key-field="_key"
|
key-field="_key"
|
||||||
|
|
||||||
>
|
>
|
||||||
<template cell:state="scope">
|
<template cell:state="scope" class="state-col">
|
||||||
<div class="state">
|
<div class="state">
|
||||||
<HarvesterVmState class="vmstate" :row="scope.row" :all-node-network="allNodeNetworks" :all-cluster-network="allClusterNetworks" />
|
<HarvesterVmState class="vmstate" :row="scope.row" :all-node-network="allNodeNetworks" :all-cluster-network="allClusterNetworks" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -144,6 +144,10 @@ export default {
|
|||||||
return !!spec?.template?.spec?.domain?.firmware?.bootloader?.efi?.secureBoot;
|
return !!spec?.template?.spec?.domain?.firmware?.bootloader?.efi?.secureBoot;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isCpuPinning(spec) {
|
||||||
|
return !!spec?.template?.spec?.domain?.cpu?.dedicatedCpuPlacement;
|
||||||
|
},
|
||||||
|
|
||||||
getCloudInitNoCloud(spec) {
|
getCloudInitNoCloud(spec) {
|
||||||
const secret = this.getSecret(spec);
|
const secret = this.getSecret(spec);
|
||||||
let userData = secret?.decodedData?.userdata;
|
let userData = secret?.decodedData?.userdata;
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import {
|
|||||||
import { HOSTNAME } from '@shell/config/labels-annotations';
|
import { HOSTNAME } from '@shell/config/labels-annotations';
|
||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
import { uniq } from '@shell/utils/array';
|
import { uniq } from '@shell/utils/array';
|
||||||
import { ADD_ONS, SOURCE_TYPE, ACCESS_CREDENTIALS } from '../../config/harvester-map';
|
import { ADD_ONS, SOURCE_TYPE, ACCESS_CREDENTIALS, maintenanceStrategies, runStrategies } from '../../config/harvester-map';
|
||||||
import { HCI_SETTING } from '../../config/settings';
|
import { HCI_SETTING } from '../../config/settings';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import { parseVolumeClaimTemplates } from '../../utils/vm';
|
import { parseVolumeClaimTemplates } from '../../utils/vm';
|
||||||
@ -136,6 +136,9 @@ export default {
|
|||||||
spec: null,
|
spec: null,
|
||||||
osType: 'linux',
|
osType: 'linux',
|
||||||
sshKey: [],
|
sshKey: [],
|
||||||
|
maintenanceStrategies,
|
||||||
|
maintenanceStrategy: 'Migrate',
|
||||||
|
runStrategies,
|
||||||
runStrategy: 'RerunOnFailure',
|
runStrategy: 'RerunOnFailure',
|
||||||
installAgent: true,
|
installAgent: true,
|
||||||
hasCreateVolumes: [],
|
hasCreateVolumes: [],
|
||||||
@ -164,6 +167,7 @@ export default {
|
|||||||
enabledSriovgpu: false,
|
enabledSriovgpu: false,
|
||||||
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
|
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
|
||||||
terminationGracePeriodSeconds: '',
|
terminationGracePeriodSeconds: '',
|
||||||
|
cpuPinning: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -316,6 +320,11 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!vm.metadata.labels) {
|
||||||
|
vm.metadata.labels = {};
|
||||||
|
}
|
||||||
|
const maintenanceStrategy = vm.metadata.labels?.[HCI_ANNOTATIONS.VM_MAINTENANCE_MODE_STRATEGY] || 'Migrate';
|
||||||
|
|
||||||
const runStrategy = spec.runStrategy || 'RerunOnFailure';
|
const runStrategy = spec.runStrategy || 'RerunOnFailure';
|
||||||
const machineType = value.machineType;
|
const machineType = value.machineType;
|
||||||
const cpu = spec.template.spec.domain?.cpu?.cores;
|
const cpu = spec.template.spec.domain?.cpu?.cores;
|
||||||
@ -352,6 +361,7 @@ export default {
|
|||||||
const efiEnabled = this.isEfiEnabled(spec);
|
const efiEnabled = this.isEfiEnabled(spec);
|
||||||
const tpmEnabled = this.isTpmEnabled(spec);
|
const tpmEnabled = this.isTpmEnabled(spec);
|
||||||
const secureBoot = this.isSecureBoot(spec);
|
const secureBoot = this.isSecureBoot(spec);
|
||||||
|
const cpuPinning = this.isCpuPinning(spec);
|
||||||
|
|
||||||
const secretRef = this.getSecret(spec);
|
const secretRef = this.getSecret(spec);
|
||||||
const accessCredentials = this.getAccessCredentials(spec);
|
const accessCredentials = this.getAccessCredentials(spec);
|
||||||
@ -362,6 +372,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this['spec'] = spec;
|
this['spec'] = spec;
|
||||||
|
this['maintenanceStrategy'] = maintenanceStrategy;
|
||||||
this['runStrategy'] = runStrategy;
|
this['runStrategy'] = runStrategy;
|
||||||
this['secretRef'] = secretRef;
|
this['secretRef'] = secretRef;
|
||||||
this['accessCredentials'] = accessCredentials;
|
this['accessCredentials'] = accessCredentials;
|
||||||
@ -382,6 +393,7 @@ export default {
|
|||||||
this['efiEnabled'] = efiEnabled;
|
this['efiEnabled'] = efiEnabled;
|
||||||
this['tpmEnabled'] = tpmEnabled;
|
this['tpmEnabled'] = tpmEnabled;
|
||||||
this['secureBoot'] = secureBoot;
|
this['secureBoot'] = secureBoot;
|
||||||
|
this['cpuPinning'] = cpuPinning;
|
||||||
|
|
||||||
this['hasCreateVolumes'] = hasCreateVolumes;
|
this['hasCreateVolumes'] = hasCreateVolumes;
|
||||||
this['networkRows'] = networkRows;
|
this['networkRows'] = networkRows;
|
||||||
@ -401,15 +413,37 @@ export default {
|
|||||||
let out = [];
|
let out = [];
|
||||||
|
|
||||||
if (_disks.length === 0) {
|
if (_disks.length === 0) {
|
||||||
|
let bus = 'virtio';
|
||||||
|
let type = HARD_DISK;
|
||||||
|
let size = '10Gi';
|
||||||
|
|
||||||
|
const imageResource = this.images.find( I => this.imageId === I.id);
|
||||||
|
const isIsoImage = /iso$/i.test(imageResource?.imageSuffix);
|
||||||
|
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
|
||||||
|
|
||||||
|
if (isIsoImage) {
|
||||||
|
bus = 'sata';
|
||||||
|
type = CD_ROM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageSize) {
|
||||||
|
let imageSizeGiB = Math.ceil(imageSize / 1024 / 1024 / 1024);
|
||||||
|
|
||||||
|
if (!isIsoImage) {
|
||||||
|
imageSizeGiB = Math.max(imageSizeGiB, 10);
|
||||||
|
}
|
||||||
|
size = `${ imageSizeGiB }Gi`;
|
||||||
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
id: randomStr(5),
|
id: randomStr(5),
|
||||||
source: SOURCE_TYPE.IMAGE,
|
source: SOURCE_TYPE.IMAGE,
|
||||||
name: 'disk-0',
|
name: 'disk-0',
|
||||||
accessMode: 'ReadWriteMany',
|
accessMode: 'ReadWriteMany',
|
||||||
bus: 'virtio',
|
bus,
|
||||||
volumeName: '',
|
volumeName: '',
|
||||||
size: '10Gi',
|
size,
|
||||||
type: HARD_DISK,
|
type,
|
||||||
storageClassName: '',
|
storageClassName: '',
|
||||||
image: this.imageId,
|
image: this.imageId,
|
||||||
volumeMode: 'Block',
|
volumeMode: 'Block',
|
||||||
@ -574,6 +608,12 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
vm.metadata.annotations[HCI_ANNOTATIONS.VM_RESERVED_MEMORY] = this.reservedMemory;
|
vm.metadata.annotations[HCI_ANNOTATIONS.VM_RESERVED_MEMORY] = this.reservedMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.maintenanceStrategy === 'Migrate') {
|
||||||
|
delete vm.metadata.labels[HCI_ANNOTATIONS.VM_MAINTENANCE_MODE_STRATEGY];
|
||||||
|
} else {
|
||||||
|
vm.metadata.labels[HCI_ANNOTATIONS.VM_MAINTENANCE_MODE_STRATEGY] = this.maintenanceStrategy;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
parseDiskRows(disk) {
|
parseDiskRows(disk) {
|
||||||
@ -681,27 +721,24 @@ export default {
|
|||||||
spec = this.multiVMScheduler(spec);
|
spec = this.multiVMScheduler(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.value.metadata['annotations'] = {
|
this.value.metadata['annotations'] = {...this.value.metadata.annotations,
|
||||||
...this.value.metadata.annotations,
|
|
||||||
[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates),
|
[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates),
|
||||||
[HCI_ANNOTATIONS.NETWORK_IPS]: JSON.stringify(this.value.networkIps)
|
[HCI_ANNOTATIONS.NETWORK_IPS]: JSON.stringify(this.value.networkIps)};
|
||||||
};
|
|
||||||
|
|
||||||
this.value.metadata['labels'] = {
|
this.value.metadata['labels'] = {...this.value.metadata.labels,
|
||||||
...this.value.metadata.labels,
|
|
||||||
[HCI_ANNOTATIONS.CREATOR]: 'harvester',
|
[HCI_ANNOTATIONS.CREATOR]: 'harvester',
|
||||||
[HCI_ANNOTATIONS.OS]: this.osType
|
[HCI_ANNOTATIONS.OS]: this.osType};
|
||||||
};
|
|
||||||
|
|
||||||
this.value['spec'] = spec;
|
this.value['spec'] = spec;
|
||||||
this['spec'] = spec;
|
this['spec'] = spec;
|
||||||
} else if (this.resource === HCI.VM_VERSION) {
|
} else if (this.resource === HCI.VM_VERSION) {
|
||||||
this.value.spec.vm['spec'] = spec;
|
this.value.spec.vm['spec'] = spec;
|
||||||
this.value.spec.vm.metadata['annotations'] = { ...this.value.spec.vm.metadata.annotations, [HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates) };
|
this.value.spec.vm.metadata['annotations'] = {
|
||||||
this.value.spec.vm.metadata['labels'] = {
|
...this.value.spec.vm.metadata.annotations,
|
||||||
...this.value.spec.vm.metadata.labels,
|
[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates),
|
||||||
[HCI_ANNOTATIONS.OS]: this.osType
|
|
||||||
};
|
};
|
||||||
|
this.value.spec.vm.metadata['labels'] = {...this.value.spec.vm.metadata.labels,
|
||||||
|
[HCI_ANNOTATIONS.OS]: this.osType,};
|
||||||
this['spec'] = spec;
|
this['spec'] = spec;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -819,6 +856,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getMaintenanceStrategyOptionLabel(opt) {
|
||||||
|
return this.t(`harvester.virtualMachine.maintenanceStrategy.options.${ opt.label || opt }`);
|
||||||
|
},
|
||||||
|
|
||||||
getInitUserData(config) {
|
getInitUserData(config) {
|
||||||
const _QGA_JSON = this.getMatchQGA(config.osType);
|
const _QGA_JSON = this.getMatchQGA(config.osType);
|
||||||
|
|
||||||
@ -1339,6 +1380,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setCpuPinning(value) {
|
||||||
|
if (value) {
|
||||||
|
set(this.spec.template.spec.domain.cpu, 'dedicatedCpuPlacement', true);
|
||||||
|
} else {
|
||||||
|
delete this.spec.template.spec.domain.cpu['dedicatedCpuPlacement'];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setTPM(tpmEnabled) {
|
setTPM(tpmEnabled) {
|
||||||
if (tpmEnabled) {
|
if (tpmEnabled) {
|
||||||
set(this.spec.template.spec.domain.devices, 'tpm', {});
|
set(this.spec.template.spec.domain.devices, 'tpm', {});
|
||||||
@ -1462,6 +1511,10 @@ export default {
|
|||||||
this.setBootMethod({ efi: this.efiEnabled, secureBoot: val });
|
this.setBootMethod({ efi: this.efiEnabled, secureBoot: val });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cpuPinning(value) {
|
||||||
|
this.setCpuPinning(value);
|
||||||
|
},
|
||||||
|
|
||||||
tpmEnabled(val) {
|
tpmEnabled(val) {
|
||||||
this.setTPM(val);
|
this.setTPM(val);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import SYSTEM_NAMESPACES from '@shell/config/system-namespaces';
|
|||||||
import { get } from '@shell/utils/object';
|
import { get } from '@shell/utils/object';
|
||||||
import { NAMESPACE } from '@shell/config/types';
|
import { NAMESPACE } from '@shell/config/types';
|
||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
|
||||||
|
import { HCI } from '../../types';
|
||||||
|
|
||||||
const OBSCURE_NAMESPACE_PREFIX = [
|
const OBSCURE_NAMESPACE_PREFIX = [
|
||||||
'c-', // cluster namespace
|
'c-', // cluster namespace
|
||||||
@ -37,15 +38,32 @@ export default class HciNamespace extends namespace {
|
|||||||
weight: -10,
|
weight: -10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editQuotaAction = {
|
||||||
|
action: 'editNSQuota',
|
||||||
|
label: this.t('harvester.modal.quota.editQuota'),
|
||||||
|
icon: 'icon icon-storage',
|
||||||
|
enabled: !!this?.actions?.updateResourceQuota && !!this?.actions?.deleteResourceQuota,
|
||||||
|
weight: -11,
|
||||||
|
};
|
||||||
|
|
||||||
if (remove > -1) {
|
if (remove > -1) {
|
||||||
out.splice(remove, 1);
|
out.splice(remove, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertAt(out, out.length - 1, promptRemove);
|
insertAt(out, out.length - 1, promptRemove);
|
||||||
|
insertAt(out, out.length - 5, editQuotaAction);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editNSQuota(resources = this) {
|
||||||
|
this.$dispatch('promptModal', {
|
||||||
|
resources,
|
||||||
|
snapshotSizeQuota: this.snapshotSizeQuota,
|
||||||
|
component: 'HarvesterQuotaDialog'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
promptRemove(resources = this) {
|
promptRemove(resources = this) {
|
||||||
this.$dispatch('promptModal', {
|
this.$dispatch('promptModal', {
|
||||||
resources,
|
resources,
|
||||||
@ -54,6 +72,17 @@ export default class HciNamespace extends namespace {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get nsResourceQuota() {
|
||||||
|
const inStore = this.$rootGetters['currentProduct'].inStore;
|
||||||
|
const allResQuotas = this.$rootGetters[`${ inStore }/all`](HCI.RESOURCE_QUOTA);
|
||||||
|
|
||||||
|
return allResQuotas.find( RQ => RQ.metadata.namespace === this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get snapshotSizeQuota() {
|
||||||
|
return this.nsResourceQuota?.spec?.snapshotLimit?.namespaceTotalSnapshotSizeQuota;
|
||||||
|
}
|
||||||
|
|
||||||
get isSystem() {
|
get isSystem() {
|
||||||
const systemNamespaces = ['fleet-default'];
|
const systemNamespaces = ['fleet-default'];
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,22 @@ export default class HciNode extends HarvesterResource {
|
|||||||
total: 1
|
total: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const enableCPUManager = {
|
||||||
|
action: 'enableCPUManager',
|
||||||
|
enabled: this.hasAction('enableCPUManager') && !this.isCPUManagerEnableInProgress && !this.isCPUManagerEnabled && !this.isEtcd, // witness node doesn't have CPU manager
|
||||||
|
icon: 'icon icon-fw icon-os-management',
|
||||||
|
label: this.t('harvester.action.enableCPUManager'),
|
||||||
|
total: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableCPUManager = {
|
||||||
|
action: 'disableCPUManager',
|
||||||
|
enabled: this.hasAction('disableCPUManager') && !this.isCPUManagerEnableInProgress && this.isCPUManagerEnabled && !this.isEtcd,
|
||||||
|
icon: 'icon icon-fw icon-os-management',
|
||||||
|
label: this.t('harvester.action.disableCPUManager'),
|
||||||
|
total: 1
|
||||||
|
};
|
||||||
|
|
||||||
const shutDown = {
|
const shutDown = {
|
||||||
action: 'shutDown',
|
action: 'shutDown',
|
||||||
enabled: this.hasAction('powerActionPossible') && this.hasAction('powerAction') && !this.isStopped && !!this.inventory,
|
enabled: this.hasAction('powerActionPossible') && this.hasAction('powerAction') && !this.isStopped && !!this.inventory,
|
||||||
@ -88,6 +104,8 @@ export default class HciNode extends HarvesterResource {
|
|||||||
uncordon,
|
uncordon,
|
||||||
enableMaintenance,
|
enableMaintenance,
|
||||||
disableMaintenance,
|
disableMaintenance,
|
||||||
|
enableCPUManager,
|
||||||
|
disableCPUManager,
|
||||||
shutDown,
|
shutDown,
|
||||||
powerOn,
|
powerOn,
|
||||||
reboot,
|
reboot,
|
||||||
@ -134,15 +152,12 @@ export default class HciNode extends HarvesterResource {
|
|||||||
|
|
||||||
get consoleUrl() {
|
get consoleUrl() {
|
||||||
const url = this.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
|
const url = this.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
|
||||||
|
const validator = /^[a-z]+:\/\//;
|
||||||
|
|
||||||
if (!url) {
|
if (!url?.match(validator)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
||||||
return `http://${ url }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +323,14 @@ export default class HciNode extends HarvesterResource {
|
|||||||
this.doAction('disableMaintenanceMode', {});
|
this.doAction('disableMaintenanceMode', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableCPUManager() {
|
||||||
|
this.doActionGrowl('enableCPUManager', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
disableCPUManager() {
|
||||||
|
this.doActionGrowl('disableCPUManager', {});
|
||||||
|
}
|
||||||
|
|
||||||
get isUnSchedulable() {
|
get isUnSchedulable() {
|
||||||
return (
|
return (
|
||||||
this.metadata?.labels?.[HCI_ANNOTATIONS.NODE_SCHEDULABLE] === 'false' ||
|
this.metadata?.labels?.[HCI_ANNOTATIONS.NODE_SCHEDULABLE] === 'false' ||
|
||||||
@ -347,6 +370,28 @@ export default class HciNode extends HarvesterResource {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCPUManagerEnabled() {
|
||||||
|
return this.metadata?.labels?.[HCI_ANNOTATIONS.CPU_MANAGER] === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCPUManagerEnableInProgress() {
|
||||||
|
return this.cpuManagerUpdateStatus === 'requested' || this.cpuManagerUpdateStatus === 'running';
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCPUManagerEnableFailed() {
|
||||||
|
return this.cpuManagerUpdateStatus === 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
get cpuManagerUpdateStatus() {
|
||||||
|
try {
|
||||||
|
const cpuManagerUpdate = JSON.parse(this.metadata.annotations[HCI_ANNOTATIONS.NODE_CPU_MANAGER_UPDATE_STATUS] || '{}');
|
||||||
|
|
||||||
|
return cpuManagerUpdate.status || '';
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get longhornDisks() {
|
get longhornDisks() {
|
||||||
const inStore = this.$rootGetters['currentProduct'].inStore;
|
const inStore = this.$rootGetters['currentProduct'].inStore;
|
||||||
const longhornNode = this.$rootGetters[`${ inStore }/byId`](
|
const longhornNode = this.$rootGetters[`${ inStore }/byId`](
|
||||||
|
|||||||
@ -20,12 +20,10 @@ export default class HciPv extends HarvesterResource {
|
|||||||
const storageClassName =
|
const storageClassName =
|
||||||
realMode === _CLONE ? this.spec.storageClassName : '';
|
realMode === _CLONE ? this.spec.storageClassName : '';
|
||||||
|
|
||||||
this['spec'] = {
|
this['spec'] = {accessModes,
|
||||||
accessModes,
|
|
||||||
storageClassName,
|
storageClassName,
|
||||||
volumeName: '',
|
volumeName: '',
|
||||||
resources: { requests: { storage } }
|
resources: { requests: { storage } }};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get availableActions() {
|
get availableActions() {
|
||||||
@ -98,7 +96,6 @@ export default class HciPv extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get stateDisplay() {
|
get stateDisplay() {
|
||||||
const ownedBy = this?.metadata?.annotations?.[HCI_ANNOTATIONS.OWNED_BY];
|
|
||||||
const volumeError = this.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
|
const volumeError = this.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
|
||||||
const degradedVolume = volumeError === DEGRADED_ERROR;
|
const degradedVolume = volumeError === DEGRADED_ERROR;
|
||||||
const status = this?.status?.phase === 'Bound' && !volumeError && this.isLonghornVolumeReady ? 'Ready' : 'Not Ready';
|
const status = this?.status?.phase === 'Bound' && !volumeError && this.isLonghornVolumeReady ? 'Ready' : 'Not Ready';
|
||||||
@ -107,7 +104,7 @@ export default class HciPv extends HarvesterResource {
|
|||||||
|
|
||||||
if (findBy(conditions, 'type', 'Resizing')?.status === 'True') {
|
if (findBy(conditions, 'type', 'Resizing')?.status === 'True') {
|
||||||
return 'Resizing';
|
return 'Resizing';
|
||||||
} else if (ownedBy && !volumeError) {
|
} else if (!!this.attachVM && !volumeError) {
|
||||||
return 'In-use';
|
return 'In-use';
|
||||||
} else if (degradedVolume) {
|
} else if (degradedVolume) {
|
||||||
return 'Degraded';
|
return 'Degraded';
|
||||||
@ -179,17 +176,19 @@ export default class HciPv extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get attachVM() {
|
get attachVM() {
|
||||||
const allVMs = this.$rootGetters['harvester/all'](HCI.VM);
|
const allVMs = this.$rootGetters['harvester/all'](HCI.VM) || [];
|
||||||
const ownedBy =
|
|
||||||
get(this, `metadata.annotations."${ HCI_ANNOTATIONS.OWNED_BY }"`) || '';
|
const findAttachVM = (vm) => {
|
||||||
|
const attachVolumes = vm.spec.template?.spec?.volumes || [];
|
||||||
|
|
||||||
|
if (vm.namespace === this.namespace && attachVolumes.length > 0) {
|
||||||
|
return attachVolumes.find(vol => vol.persistentVolumeClaim?.claimName === this.name);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ownedBy) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ownedId = JSON.parse(ownedBy)[0]?.refs?.[0];
|
return allVMs.find(findAttachVM);
|
||||||
|
|
||||||
return allVMs.find(D => D.id === ownedId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isAvailable() {
|
get isAvailable() {
|
||||||
|
|||||||
@ -9,6 +9,10 @@ export default class HciUpgrade extends HarvesterResource {
|
|||||||
return this?.metadata?.labels?.[HCI.LATEST_UPGRADE] === 'true';
|
return this?.metadata?.labels?.[HCI.LATEST_UPGRADE] === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isUpgradeFailed() {
|
||||||
|
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Failed';
|
||||||
|
}
|
||||||
|
|
||||||
get isUpgradeSucceeded() {
|
get isUpgradeSucceeded() {
|
||||||
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Succeeded';
|
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Succeeded';
|
||||||
}
|
}
|
||||||
@ -117,10 +121,10 @@ export default class HciUpgrade extends HarvesterResource {
|
|||||||
get upgradeImageMessage() {
|
get upgradeImageMessage() {
|
||||||
const conditions = this?.status?.conditions || [];
|
const conditions = this?.status?.conditions || [];
|
||||||
const imageReady = conditions.find( cond => cond.type === 'ImageReady');
|
const imageReady = conditions.find( cond => cond.type === 'ImageReady');
|
||||||
const hasError = imageReady?.status === 'False';
|
const success = imageReady?.status === 'True';
|
||||||
const message = imageReady?.message || imageReady?.reason;
|
const message = imageReady?.message || imageReady?.reason;
|
||||||
|
|
||||||
return hasError ? message : '';
|
return success ? '' : message;
|
||||||
}
|
}
|
||||||
|
|
||||||
get nodeUpgradeMessage() {
|
get nodeUpgradeMessage() {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
{
|
{
|
||||||
action: 'createFromImage',
|
action: 'createFromImage',
|
||||||
enabled: canCreateVM,
|
enabled: canCreateVM,
|
||||||
icon: 'icon icon-fw icon-spinner',
|
icon: 'icon icon-circle-plus',
|
||||||
label: this.t('harvester.action.createVM'),
|
label: this.t('harvester.action.createVM'),
|
||||||
disabled: !this.isReady,
|
disabled: !this.isReady,
|
||||||
},
|
},
|
||||||
@ -74,7 +74,7 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
router.push({
|
router.push({
|
||||||
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-create`,
|
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-create`,
|
||||||
params: { resource: HCI.VM },
|
params: { resource: HCI.VM },
|
||||||
query: { image: this.id }
|
query: { image: this.id, fromPage: HCI.IMAGE }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +101,10 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
const imported = this.getStatusConditionOfType('Imported');
|
const imported = this.getStatusConditionOfType('Imported');
|
||||||
|
|
||||||
if (imported?.status === 'Unknown') {
|
if (imported?.status === 'Unknown') {
|
||||||
|
if (this.spec.sourceType === 'restore') {
|
||||||
|
return 'Restoring';
|
||||||
|
}
|
||||||
|
|
||||||
if (this.spec.sourceType === 'download') {
|
if (this.spec.sourceType === 'download') {
|
||||||
return 'Downloading';
|
return 'Downloading';
|
||||||
}
|
}
|
||||||
@ -131,7 +135,8 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
const conditions = this?.status?.conditions || [];
|
const conditions = this?.status?.conditions || [];
|
||||||
const initialized = conditions.find( cond => cond.type === 'Initialized');
|
const initialized = conditions.find( cond => cond.type === 'Initialized');
|
||||||
const imported = conditions.find( cond => cond.type === 'Imported');
|
const imported = conditions.find( cond => cond.type === 'Imported');
|
||||||
const message = initialized?.message || imported?.message;
|
const retryLimitExceeded = conditions.find( cond => cond.type === 'RetryLimitExceeded');
|
||||||
|
const message = initialized?.message || imported?.message || retryLimitExceeded?.message;
|
||||||
|
|
||||||
return ucFirst(message);
|
return ucFirst(message);
|
||||||
}
|
}
|
||||||
@ -167,6 +172,21 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get virtualSize() {
|
||||||
|
const virtualSize = this.status?.virtualSize;
|
||||||
|
|
||||||
|
if (!virtualSize) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatSi(virtualSize, {
|
||||||
|
increment: 1024,
|
||||||
|
maxPrecision: 2,
|
||||||
|
suffix: 'B',
|
||||||
|
firstSuffix: 'B',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getStatusConditionOfType(type, defaultValue = []) {
|
getStatusConditionOfType(type, defaultValue = []) {
|
||||||
const conditions = Array.isArray(get(this, 'status.conditions')) ? this.status.conditions : defaultValue;
|
const conditions = Array.isArray(get(this, 'status.conditions')) ? this.status.conditions : defaultValue;
|
||||||
|
|
||||||
|
|||||||
@ -133,7 +133,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
{
|
{
|
||||||
action: 'softrebootVM',
|
action: 'softrebootVM',
|
||||||
enabled: !!this.actions?.softreboot,
|
enabled: !!this.actions?.softreboot,
|
||||||
icon: 'icon icon-refresh',
|
icon: 'icon icon-pipeline',
|
||||||
label: this.t('harvester.action.softreboot')
|
label: this.t('harvester.action.softreboot')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -152,9 +152,15 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
{
|
{
|
||||||
action: 'takeVMSnapshot',
|
action: 'takeVMSnapshot',
|
||||||
enabled: !!this.actions?.backup,
|
enabled: !!this.actions?.backup,
|
||||||
icon: 'icon icon-backup',
|
icon: 'icon icon-snapshot',
|
||||||
label: this.t('harvester.action.vmSnapshot')
|
label: this.t('harvester.action.vmSnapshot')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
action: 'editVMQuota',
|
||||||
|
enabled: !!this.actions?.updateResourceQuota && !!this.actions.deleteResourceQuota,
|
||||||
|
icon: 'icon icon-storage',
|
||||||
|
label: this.t('harvester.action.editVMQuota')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: 'restoreVM',
|
action: 'restoreVM',
|
||||||
enabled: !!this.actions?.restore,
|
enabled: !!this.actions?.restore,
|
||||||
@ -331,6 +337,14 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editVMQuota(resources = this) {
|
||||||
|
this.$dispatch('promptModal', {
|
||||||
|
resources,
|
||||||
|
snapshotSizeQuota: this.snapshotSizeQuota,
|
||||||
|
component: 'HarvesterQuotaDialog'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
unplugVolume(diskName) {
|
unplugVolume(diskName) {
|
||||||
const resources = this;
|
const resources = this;
|
||||||
|
|
||||||
@ -541,6 +555,17 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get nsResourceQuota() {
|
||||||
|
const inStore = this.productInStore;
|
||||||
|
const allResQuotas = this.$rootGetters[`${ inStore }/all`](HCI.RESOURCE_QUOTA);
|
||||||
|
|
||||||
|
return allResQuotas.find( RQ => RQ.namespace === this.metadata.namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
get snapshotSizeQuota() {
|
||||||
|
return this.nsResourceQuota?.spec?.snapshotLimit?.vmTotalSnapshotSizeQuota?.[this.metadata.name];
|
||||||
|
}
|
||||||
|
|
||||||
get vmi() {
|
get vmi() {
|
||||||
const inStore = this.productInStore;
|
const inStore = this.productInStore;
|
||||||
|
|
||||||
@ -646,6 +671,10 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isTerminating() {
|
||||||
|
return !!this?.metadata?.deletionTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
get otherState() {
|
get otherState() {
|
||||||
const state = (this.vmi &&
|
const state = (this.vmi &&
|
||||||
[VMIPhase.Scheduling, VMIPhase.Scheduled].includes(
|
[VMIPhase.Scheduling, VMIPhase.Scheduled].includes(
|
||||||
@ -685,18 +714,21 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
|
|
||||||
const allRestore = this.$rootGetters[`${ inStore }/all`](HCI.RESTORE);
|
const allRestore = this.$rootGetters[`${ inStore }/all`](HCI.RESTORE);
|
||||||
|
|
||||||
return allRestore.find(O => O.id === id);
|
const res = allRestore.find(O => O.id === id);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
const allBackups = this.$rootGetters[`${ inStore }/all`](HCI.BACKUP);
|
||||||
|
|
||||||
|
res.fromSnapshot = !!allBackups
|
||||||
|
.filter(b => b.spec?.type !== BACKUP_TYPE.BACKUP)
|
||||||
|
.find(s => s.id === `${ res.spec?.virtualMachineBackupNamespace }/${ res.spec?.virtualMachineBackupName }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
get restoreProgress() {
|
get restoreProgress() {
|
||||||
const inStore = this.productInStore;
|
if (this.isVMError || this.isTerminating) {
|
||||||
const allBackups = this.$rootGetters[`${ inStore }/all`](HCI.BACKUP);
|
|
||||||
|
|
||||||
const isSnapshotRestore = !!allBackups
|
|
||||||
.filter(b => b.spec?.type !== BACKUP_TYPE.BACKUP)
|
|
||||||
.find(s => s.id === `${ this.restoreResource?.spec?.virtualMachineBackupNamespace }/${ this.restoreResource?.spec?.virtualMachineBackupName }`);
|
|
||||||
|
|
||||||
if (isSnapshotRestore) {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +758,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return 'Restoring';
|
return 'Restoring';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this?.metadata?.deletionTimestamp) {
|
if (this.isTerminating) {
|
||||||
return 'Terminating';
|
return 'Terminating';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import CommunityLinks from '@shell/components/CommunityLinks';
|
|||||||
import { SCHEMA } from '@shell/config/types';
|
import { SCHEMA } from '@shell/config/types';
|
||||||
import HarvesterSupportBundle from '../../../../dialog/HarvesterSupportBundle';
|
import HarvesterSupportBundle from '../../../../dialog/HarvesterSupportBundle';
|
||||||
import { HCI } from '../../../../types';
|
import { HCI } from '../../../../types';
|
||||||
|
import { DOC_LINKS } from '../../../../config/doc-links';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -71,13 +72,18 @@ export default {
|
|||||||
const { host, params } = this.internalPrefix;
|
const { host, params } = this.internalPrefix;
|
||||||
|
|
||||||
return `https://${ host }/k8s/clusters/${ params.cluster }/api/v1/namespaces/longhorn-system/services/http:longhorn-frontend:80/proxy/#/dashboard`;
|
return `https://${ host }/k8s/clusters/${ params.cluster }/api/v1/namespaces/longhorn-system/services/http:longhorn-frontend:80/proxy/#/dashboard`;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
rancherIntegrationLink() {
|
||||||
|
return DOC_LINKS.RANCHER_INTEGRATION_URL;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open() {
|
open() {
|
||||||
this.$store.commit('harvester-common/toggleBundleModal', true);
|
this.$store.commit('harvester-common/toggleBundleModal', true);
|
||||||
}
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -89,10 +95,7 @@ export default {
|
|||||||
<IndentedPanel>
|
<IndentedPanel>
|
||||||
<div class="content mt-20">
|
<div class="content mt-20">
|
||||||
<div class="promo">
|
<div class="promo">
|
||||||
<div
|
<div v-if="showSupportBundle" class="box mb-20 box-primary">
|
||||||
v-if="showSupportBundle"
|
|
||||||
class="box mb-20 box-primary"
|
|
||||||
>
|
|
||||||
<h2>
|
<h2>
|
||||||
{{ t('harvester.modal.bundle.title') }}
|
{{ t('harvester.modal.bundle.title') }}
|
||||||
</h2>
|
</h2>
|
||||||
@ -109,10 +112,7 @@ export default {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="box box-primary" :class="{'mb-20': dev }">
|
||||||
class="box box-primary"
|
|
||||||
:class="{'mb-20': dev }"
|
|
||||||
>
|
|
||||||
<h2>
|
<h2>
|
||||||
{{ t('harvester.support.kubeconfig.title') }}
|
{{ t('harvester.support.kubeconfig.title') }}
|
||||||
</h2>
|
</h2>
|
||||||
@ -129,41 +129,24 @@ export default {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-if="dev" class="row">
|
||||||
v-if="dev"
|
|
||||||
class="row"
|
|
||||||
>
|
|
||||||
<div class="col span-6 box box-primary">
|
<div class="col span-6 box box-primary">
|
||||||
<h2>
|
<h2>
|
||||||
<a
|
<a rel="nofollow noopener noreferrer" target="_blank" :href="rancherLink">{{ t('harvester.support.internal.rancher.title') }} <i class="icon icon-external-link" /></a>
|
||||||
rel="nofollow noopener noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
:href="rancherLink"
|
|
||||||
>{{ t('harvester.support.internal.rancher.title') }} <i class="icon icon-external-link" /></a>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
<p class="warning">
|
<p class="warning">
|
||||||
<t
|
<t k="harvester.support.internal.rancher.titleDescription" :raw="true" :url="rancherIntegrationLink" />
|
||||||
k="harvester.support.internal.rancher.titleDescription"
|
|
||||||
:raw="true"
|
|
||||||
/>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col span-6 box box-primary">
|
<div class="col span-6 box box-primary">
|
||||||
<h2>
|
<h2>
|
||||||
<a
|
<a rel="nofollow noopener noreferrer" target="_blank" :href="longhornLink">{{ t('harvester.support.internal.longhorn.title') }} <i class="icon icon-external-link" /></a>
|
||||||
rel="nofollow noopener noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
:href="longhornLink"
|
|
||||||
>{{ t('harvester.support.internal.longhorn.title') }} <i class="icon icon-external-link" /></a>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
<p class="warning">
|
<p class="warning">
|
||||||
<t
|
<t k="harvester.support.internal.longhorn.titleDescription" :raw="true" />
|
||||||
k="harvester.support.internal.longhorn.titleDescription"
|
|
||||||
:raw="true"
|
|
||||||
/>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -173,17 +156,9 @@ export default {
|
|||||||
<CommunityLinks :link-options="options" />
|
<CommunityLinks :link-options="options" />
|
||||||
</div>
|
</div>
|
||||||
<div class="external">
|
<div class="external">
|
||||||
<a
|
<a href="https://www.suse.com/suse-harvester/support-matrix/all-supported-versions" target="_blank" rel="noopener noreferrer nofollow">{{ t('harvester.support.community.learnMore') }} <i class="icon icon-external-link" /></a>
|
||||||
href="https://www.suse.com/suse-harvester/support-matrix/all-supported-versions"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer nofollow"
|
|
||||||
>{{ t('harvester.support.community.learnMore') }} <i class="icon icon-external-link" /></a>
|
|
||||||
or
|
or
|
||||||
<a
|
<a href="https://www.suse.com/products/harvester/" target="_blank" rel="noopener noreferrer nofollow">{{ t('harvester.support.community.pricing') }} <i class="icon icon-external-link" /></a>
|
||||||
href="https://www.suse.com/products/harvester/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer nofollow"
|
|
||||||
>{{ t('harvester.support.community.pricing') }} <i class="icon icon-external-link" /></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</IndentedPanel>
|
</IndentedPanel>
|
||||||
|
|||||||
@ -18,16 +18,16 @@ export default {
|
|||||||
const cluster = await dispatch('management/find', {
|
const cluster = await dispatch('management/find', {
|
||||||
type: MANAGEMENT.CLUSTER,
|
type: MANAGEMENT.CLUSTER,
|
||||||
id,
|
id,
|
||||||
opt: { url: `${MANAGEMENT.CLUSTER}s/${escape(id)}` }
|
opt: { url: `${ MANAGEMENT.CLUSTER }s/${ escape(id) }` }
|
||||||
}, { root: true });
|
}, { root: true });
|
||||||
|
|
||||||
let virtualBase = `/k8s/clusters/${escape(id)}/v1/harvester`;
|
let virtualBase = `/k8s/clusters/${ escape(id) }/v1/harvester`;
|
||||||
|
|
||||||
if (id === 'local') {
|
if (id === 'local') {
|
||||||
virtualBase = `/v1/harvester`;
|
virtualBase = `/v1/harvester`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cluster) {
|
if ( !cluster ) {
|
||||||
commit('clusterId', null, { root: true });
|
commit('clusterId', null, { root: true });
|
||||||
commit('applyConfig', { baseUrl: null });
|
commit('applyConfig', { baseUrl: null });
|
||||||
throw new ClusterNotFoundError(id);
|
throw new ClusterNotFoundError(id);
|
||||||
@ -44,22 +44,22 @@ export default {
|
|||||||
|
|
||||||
const projectArgs = {
|
const projectArgs = {
|
||||||
type: MANAGEMENT.PROJECT,
|
type: MANAGEMENT.PROJECT,
|
||||||
opt: {
|
opt: {
|
||||||
url: `${MANAGEMENT.PROJECT}/${escape(id)}`,
|
url: `${ MANAGEMENT.PROJECT }/${ escape(id) }`,
|
||||||
watchNamespace: id
|
watchNamespace: id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchProjects = async () => {
|
const fetchProjects = async() => {
|
||||||
let limit = 30000;
|
let limit = 30000;
|
||||||
const sleep = 100;
|
const sleep = 100;
|
||||||
|
|
||||||
while (limit > 0 && !rootState.managementReady) {
|
while ( limit > 0 && !rootState.managementReady ) {
|
||||||
await setTimeout(() => { }, sleep);
|
await setTimeout(() => {}, sleep);
|
||||||
limit -= sleep;
|
limit -= sleep;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rootGetters['management/schemaFor'](MANAGEMENT.PROJECT)) {
|
if ( rootGetters['management/schemaFor'](MANAGEMENT.PROJECT) ) {
|
||||||
return dispatch('management/findAll', projectArgs, { root: true });
|
return dispatch('management/findAll', projectArgs, { root: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -67,8 +67,8 @@ export default {
|
|||||||
if (id !== 'local' && getters['schemaFor'](MANAGEMENT.SETTING)) { // multi-cluster
|
if (id !== 'local' && getters['schemaFor'](MANAGEMENT.SETTING)) { // multi-cluster
|
||||||
const settings = await dispatch('findAll', {
|
const settings = await dispatch('findAll', {
|
||||||
type: MANAGEMENT.SETTING,
|
type: MANAGEMENT.SETTING,
|
||||||
id: SETTING.SYSTEM_NAMESPACES,
|
id: SETTING.SYSTEM_NAMESPACES,
|
||||||
opt: { url: `${virtualBase}/${MANAGEMENT.SETTING}s/`, force: true }
|
opt: { url: `${ virtualBase }/${ MANAGEMENT.SETTING }s/`, force: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const systemNamespaces = settings?.find((x: any) => x.id === SETTING.SYSTEM_NAMESPACES);
|
const systemNamespaces = settings?.find((x: any) => x.id === SETTING.SYSTEM_NAMESPACES);
|
||||||
@ -80,17 +80,21 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hash: { [key: string]: Promise<any> } = {
|
const hash: { [key: string]: Promise<any>} = {
|
||||||
projects: fetchProjects(),
|
projects: fetchProjects(),
|
||||||
virtualCount: dispatch('findAll', { type: COUNT }),
|
virtualCount: dispatch('findAll', { type: COUNT }),
|
||||||
virtualNamespaces: dispatch('findAll', { type: NAMESPACE }),
|
virtualNamespaces: dispatch('findAll', { type: NAMESPACE }),
|
||||||
settings: dispatch('findAll', { type: HCI.SETTING }),
|
settings: dispatch('findAll', { type: HCI.SETTING }),
|
||||||
clusters: dispatch('management/findAll', {
|
clusters: dispatch('management/findAll', {
|
||||||
type: MANAGEMENT.CLUSTER,
|
type: MANAGEMENT.CLUSTER,
|
||||||
opt: { force: true }
|
opt: { force: true }
|
||||||
}, { root: true }),
|
}, { root: true }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (getters['schemaFor'](HCI.RESOURCE_QUOTA)) {
|
||||||
|
hash.resourceQuota = dispatch('findAll', { type: HCI.RESOURCE_QUOTA });
|
||||||
|
}
|
||||||
|
|
||||||
if (getters['schemaFor'](HCI.UPGRADE)) {
|
if (getters['schemaFor'](HCI.UPGRADE)) {
|
||||||
hash.upgrades = dispatch('findAll', { type: HCI.UPGRADE });
|
hash.upgrades = dispatch('findAll', { type: HCI.UPGRADE });
|
||||||
}
|
}
|
||||||
@ -101,15 +105,15 @@ export default {
|
|||||||
|
|
||||||
commit('updateNamespaces', {
|
commit('updateNamespaces', {
|
||||||
filters: [],
|
filters: [],
|
||||||
all: getters.filterNamespace(),
|
all: getters.filterNamespace(),
|
||||||
getters
|
getters
|
||||||
}, { root: true });
|
}, { root: true });
|
||||||
|
|
||||||
// Solve compatibility with Rancher v2.6.x, fell remove these codes after not support v2.6.x
|
// Solve compatibility with Rancher v2.6.x, fell remove these codes after not support v2.6.x
|
||||||
const definition = {
|
const definition = {
|
||||||
def: false,
|
def: false,
|
||||||
parseJSON: true,
|
parseJSON: true,
|
||||||
inheritFrom: DEV,
|
inheritFrom: DEV,
|
||||||
asUserPreference: true,
|
asUserPreference: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,9 +138,9 @@ export default {
|
|||||||
|
|
||||||
if (isMultiCluster) {
|
if (isMultiCluster) {
|
||||||
commit('managementChanged', {
|
commit('managementChanged', {
|
||||||
ready: true,
|
ready: true,
|
||||||
isMultiCluster: true,
|
isMultiCluster: true,
|
||||||
isRancher: true,
|
isRancher: true,
|
||||||
}, { root: true });
|
}, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -14,9 +14,9 @@ export default {
|
|||||||
divider,
|
divider,
|
||||||
notFilterNamespaces
|
notFilterNamespaces
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const out: { id: string, kind: string, label: string }[] = [{
|
const out = [{
|
||||||
id: ALL,
|
id: ALL,
|
||||||
kind: NAMESPACE_FILTER_KINDS.SPECIAL,
|
kind: NAMESPACE_FILTER_KINDS.SPECIAL,
|
||||||
label: rootGetters['i18n/t']('nav.ns.all'),
|
label: rootGetters['i18n/t']('nav.ns.all'),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -30,11 +30,9 @@ export default {
|
|||||||
MANAGEMENT.PROJECT
|
MANAGEMENT.PROJECT
|
||||||
);
|
);
|
||||||
|
|
||||||
projects = sortBy(
|
projects = sortBy(filterBy(projects, 'spec.clusterName', cluster.id), [
|
||||||
filterBy(projects, 'spec.clusterName', cluster.id),
|
'nameDisplay',
|
||||||
['nameDisplay'],
|
]).filter((project: any) => project.nameDisplay !== 'System');
|
||||||
null
|
|
||||||
).filter((project: any) => project.nameDisplay !== 'System');
|
|
||||||
|
|
||||||
const projectsById: any = {};
|
const projectsById: any = {};
|
||||||
const namespacesByProject: any = {};
|
const namespacesByProject: any = {};
|
||||||
@ -72,8 +70,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
id: `project://${id}`,
|
id: `project://${ id }`,
|
||||||
kind: 'project',
|
kind: 'project',
|
||||||
label: project.nameDisplay,
|
label: project.nameDisplay,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -90,8 +88,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
id: ALL_ORPHANS,
|
id: ALL_ORPHANS,
|
||||||
kind: 'project',
|
kind: 'project',
|
||||||
label: rootGetters['i18n/t']('nav.ns.orphan'),
|
label: rootGetters['i18n/t']('nav.ns.orphan'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -28,12 +28,12 @@ const harvesterFactory = (): CoreStoreSpecifics => {
|
|||||||
return steveFactory;
|
return steveFactory;
|
||||||
};
|
};
|
||||||
const config: CoreStoreConfig = {
|
const config: CoreStoreConfig = {
|
||||||
namespace: PRODUCT_NAME,
|
namespace: PRODUCT_NAME,
|
||||||
isClusterStore: true
|
isClusterStore: true
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
specifics: harvesterFactory(),
|
specifics: harvesterFactory(),
|
||||||
config,
|
config,
|
||||||
init: steveStoreInit
|
init: steveStoreInit
|
||||||
};
|
};
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export const HCI = {
|
|||||||
FLOW: 'harvesterhci.io.logging.flow',
|
FLOW: 'harvesterhci.io.logging.flow',
|
||||||
OUTPUT: 'harvesterhci.io.logging.output',
|
OUTPUT: 'harvesterhci.io.logging.output',
|
||||||
STORAGE: 'harvesterhci.io.storage',
|
STORAGE: 'harvesterhci.io.storage',
|
||||||
|
RESOURCE_QUOTA: 'harvesterhci.io.resourcequota',
|
||||||
KSTUNED: 'node.harvesterhci.io.ksmtuned',
|
KSTUNED: 'node.harvesterhci.io.ksmtuned',
|
||||||
PCI_DEVICE: 'devices.harvesterhci.io.pcidevice',
|
PCI_DEVICE: 'devices.harvesterhci.io.pcidevice',
|
||||||
PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim',
|
PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user