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';
|
||||
|
||||
export default {
|
||||
name: 'DiskTags',
|
||||
name: 'Tags',
|
||||
|
||||
components: { Tag },
|
||||
|
||||
|
||||
@ -35,13 +35,23 @@ export default {
|
||||
version: '',
|
||||
enableLogging: true,
|
||||
readyReleaseNote: false,
|
||||
isOpen: false,
|
||||
isOpen: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['currentCluster']),
|
||||
|
||||
latestUpgrade() {
|
||||
return this.upgrade?.find(u => u.isLatestUpgrade);
|
||||
},
|
||||
|
||||
isUpgradeInProgress() {
|
||||
return this.latestUpgrade &&
|
||||
!this.latestUpgrade.isUpgradeSucceeded &&
|
||||
!this.latestUpgrade.isUpgradeFailed;
|
||||
},
|
||||
|
||||
versionOptions() {
|
||||
const versions = this.$store.getters['harvester/all'](HCI.VERSION);
|
||||
|
||||
@ -133,7 +143,7 @@ export default {
|
||||
/>
|
||||
</h1>
|
||||
<button
|
||||
v-if="versionOptions.length"
|
||||
v-if="versionOptions.length && !isUpgradeInProgress"
|
||||
type="button"
|
||||
class="btn bg-warning btn-sm"
|
||||
@click="open"
|
||||
|
||||
@ -34,15 +34,7 @@ export default {
|
||||
},
|
||||
|
||||
data() {
|
||||
const categorySettings = 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));
|
||||
}
|
||||
}) || [];
|
||||
const categorySettings = this.filterCategorySettings();
|
||||
|
||||
return {
|
||||
HCI_SETTING,
|
||||
@ -52,7 +44,27 @@ export default {
|
||||
|
||||
computed: { ...mapGetters({ t: 'i18n/t' }) },
|
||||
|
||||
watch: {
|
||||
settings: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this['categorySettings'] = this.filterCategorySettings();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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) {
|
||||
const actionElement = e.srcElement;
|
||||
|
||||
@ -104,7 +116,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<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="title">
|
||||
<h1>
|
||||
@ -112,6 +124,9 @@ export default {
|
||||
<span v-if="setting.customized" class="modified">
|
||||
Modified
|
||||
</span>
|
||||
<span v-if="setting.technicalPreview" v-clean-tooltip="t('advancedSettings.technicalPreview')" class="technical-preview">
|
||||
Technical Preview
|
||||
</span>
|
||||
</h1>
|
||||
<h2 v-clean-html="t(setting.description, {}, true)">
|
||||
</h2>
|
||||
@ -200,4 +215,12 @@ export default {
|
||||
padding: 2px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.technical-preview {
|
||||
margin-left: 10px;
|
||||
border: 1px solid var(--warning);
|
||||
border-radius: 5px;
|
||||
padding: 2px 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { Banner } from '@components/Banner';
|
||||
import { DOC_LINKS } from '../config/doc-links';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterUpgradeInfo',
|
||||
@ -16,6 +17,10 @@ export default {
|
||||
computed: {
|
||||
releaseVersion() {
|
||||
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">
|
||||
<div>
|
||||
<strong>{{ t('harvester.upgradePage.upgradeInfo.warning') }}:</strong>
|
||||
<p v-clean-html="t('harvester.upgradePage.upgradeInfo.doc', {}, true)" class="mb-5">
|
||||
</p>
|
||||
<p v-clean-html="t('harvester.upgradePage.upgradeInfo.doc', {url: upgradeLink}, true)" class="mb-5"></p>
|
||||
|
||||
<p class="mb-5">
|
||||
{{ t('harvester.upgradePage.upgradeInfo.tip') }}
|
||||
</p>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</Banner>
|
||||
|
||||
@ -61,7 +61,7 @@ export default {
|
||||
window.open(
|
||||
url,
|
||||
'_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 { NODE } from '@shell/config/types';
|
||||
import { HCI } from '../../types';
|
||||
import { DOC_LINKS } from '../../config/doc-links';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterEditStorageNetwork',
|
||||
@ -85,6 +86,9 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
storageNetworkExampleLink() {
|
||||
return DOC_LINKS.STORAGE_NETWORK_EXAMPLE;
|
||||
},
|
||||
clusterNetworkOptions() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
|
||||
@ -206,8 +210,8 @@ export default {
|
||||
:placeholder="t('harvester.setting.storageNetwork.range.placeholder')"
|
||||
label-key="harvester.setting.storageNetwork.range.label"
|
||||
/>
|
||||
<Tip class="mb-20" icon="icon icon-info" :text="t('harvester.setting.storageNetwork.tip')">
|
||||
<t k="harvester.setting.storageNetwork.tip" :raw="true" />
|
||||
<Tip class="mb-20" icon="icon icon-info">
|
||||
<t k="harvester.setting.storageNetwork.tip" :raw="true" :url="storageNetworkExampleLink" />
|
||||
</Tip>
|
||||
|
||||
<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 = {
|
||||
NEW: 'New',
|
||||
IMAGE: 'VM Image',
|
||||
IMAGE: 'Virtual Machine Image',
|
||||
ATTACH_VOLUME: 'Existing Volume',
|
||||
CONTAINER: 'Container'
|
||||
};
|
||||
@ -41,7 +41,14 @@ export const ACCESS_CREDENTIALS = {
|
||||
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 = {
|
||||
VolumeSnapshot: 'VolumeSnapshot',
|
||||
@ -57,8 +64,8 @@ export const FLOW_TYPE = {
|
||||
export const ADD_ONS = {
|
||||
HARVESTER_SEEDER: 'harvester-seeder',
|
||||
PCI_DEVICE_CONTROLLER: 'pcidevices-controller',
|
||||
NVIDIA_DRIVER_TOOLKIT_CONTROLLER: 'nvidia-driver-toolkit',
|
||||
RANCHER_LOGGING: 'rancher-logging',
|
||||
RANCHER_MONITORING: 'rancher-monitoring',
|
||||
VM_IMPORT_CONTROLLER: 'vm-import-controller',
|
||||
NVIDIA_DRIVER_TOOLKIT_CONTROLLER: 'nvidia-driver-toolkit'
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
export const HCI = {
|
||||
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
|
||||
CURRENT_IP: 'rke2.io/internal-ip',
|
||||
OWNED_BY: 'harvesterhci.io/owned-by',
|
||||
IMAGE_ID: 'harvesterhci.io/imageId',
|
||||
SSH_NAMES: 'harvesterhci.io/sshNames',
|
||||
NETWORK_IPS: 'network.harvesterhci.io/ips',
|
||||
@ -18,6 +17,7 @@ export const HCI = {
|
||||
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
||||
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
|
||||
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
|
||||
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
|
||||
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
|
||||
MIGRATION_STATE: 'harvesterhci.io/migrationState',
|
||||
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
|
||||
@ -25,6 +25,7 @@ export const HCI = {
|
||||
INIT_IP: 'etcd.rke2.cattle.io/node-address',
|
||||
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
|
||||
NETWORK_ROUTE: 'network.harvesterhci.io/route',
|
||||
MATCHED_NODES: 'network.harvesterhci.io/matched-nodes',
|
||||
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
|
||||
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
|
||||
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
|
||||
@ -33,6 +34,7 @@ export const HCI = {
|
||||
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
|
||||
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
|
||||
OS_TYPE: 'harvesterhci.io/os-type',
|
||||
STORAGE_PROVISIONER: 'harvesterhci.io/storageProvisioner',
|
||||
HOST_REQUEST: 'management.cattle.io/pod-requests',
|
||||
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
|
||||
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
|
||||
@ -46,4 +48,8 @@ export const HCI = {
|
||||
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'
|
||||
};
|
||||
|
||||
@ -16,7 +16,9 @@ export const HCI_SETTING = {
|
||||
CLUSTER_REGISTRATION_URL: 'cluster-registration-url',
|
||||
DEFAULT_STORAGE_CLASS: 'default-storage-class',
|
||||
SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout',
|
||||
SUPPORT_BUNDLE_EXPIRATION: 'support-bundle-expiration',
|
||||
SUPPORT_BUNDLE_IMAGE: 'support-bundle-image',
|
||||
SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT: 'support-bundle-node-collection-timeout',
|
||||
STORAGE_NETWORK: 'storage-network',
|
||||
VM_FORCE_RESET_POLICY: 'vm-force-reset-policy',
|
||||
SSL_CERTIFICATES: 'ssl-certificates',
|
||||
@ -28,6 +30,10 @@ export const HCI_SETTING = {
|
||||
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 = {
|
||||
@ -41,6 +47,7 @@ export const HCI_ALLOWED_SETTINGS = {
|
||||
[HCI_SETTING.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.SERVER_VERSION]: { readOnly: true },
|
||||
[HCI_SETTING.UPGRADE_CHECKER_ENABLED]: { kind: 'boolean' },
|
||||
@ -51,6 +58,8 @@ export const HCI_ALLOWED_SETTINGS = {
|
||||
},
|
||||
[HCI_SETTING.OVERCOMMIT_CONFIG]: { kind: 'json', from: 'import' },
|
||||
[HCI_SETTING.SUPPORT_BUNDLE_TIMEOUT]: {},
|
||||
[HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: {},
|
||||
[HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: {},
|
||||
[HCI_SETTING.SUPPORT_BUNDLE_IMAGE]: { kind: 'json', from: 'import' },
|
||||
[HCI_SETTING.STORAGE_NETWORK]: { kind: 'custom', from: 'import' },
|
||||
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
|
||||
@ -75,6 +84,9 @@ export const HCI_ALLOWED_SETTINGS = {
|
||||
[HCI_SETTING.NTP_SERVERS]: {
|
||||
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 = {
|
||||
|
||||
@ -11,6 +11,14 @@ export const IMAGE_DOWNLOAD_SIZE = {
|
||||
width: 120
|
||||
};
|
||||
|
||||
export const IMAGE_VIRTUAL_SIZE = {
|
||||
name: 'virtualSize',
|
||||
labelKey: 'harvester.tableHeaders.virtualSize',
|
||||
value: 'virtualSize',
|
||||
sort: 'status.virtualSize',
|
||||
width: 120
|
||||
};
|
||||
|
||||
export const IMAGE_PROGRESS = {
|
||||
name: 'Uploaded',
|
||||
labelKey: 'tableHeaders.progress',
|
||||
|
||||
@ -48,6 +48,13 @@ export default {
|
||||
customName() {
|
||||
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() {
|
||||
const consoleUrl = this.value.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
|
||||
@ -224,6 +231,9 @@ export default {
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<LabelValue :name="t('harvester.host.detail.consoleUrl')" :value="consoleUrl.value">
|
||||
<a slot="value" :href="consoleUrl.value" target="_blank">{{ consoleUrl.display }}</a>
|
||||
@ -268,7 +278,7 @@ export default {
|
||||
<HarvesterStorageUsed
|
||||
:row="value"
|
||||
:resource-name="t('harvester.host.detail.storage')"
|
||||
:show-reserved="true"
|
||||
:show-allocated="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,13 +4,15 @@ import LabelValue from '@shell/components/LabelValue';
|
||||
import { BadgeState } from '@components/BadgeState';
|
||||
import { Banner } from '@components/Banner';
|
||||
import HarvesterDisk from '../../mixins/harvester-disk';
|
||||
import { RadioGroup } from '@components/Form/Radio';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LabelValue,
|
||||
BadgeState,
|
||||
Banner,
|
||||
Tag
|
||||
Tag,
|
||||
RadioGroup
|
||||
},
|
||||
|
||||
mixins: [
|
||||
@ -37,6 +39,18 @@ export default {
|
||||
return {};
|
||||
},
|
||||
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() {
|
||||
return [{
|
||||
label: this.t('generic.enabled'),
|
||||
@ -117,6 +131,16 @@ export default {
|
||||
</div>
|
||||
<div class="row mt-10">
|
||||
<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">
|
||||
{{ t('harvester.host.disk.conditions') }}:
|
||||
<BadgeState
|
||||
@ -127,9 +151,9 @@ export default {
|
||||
class="mr-10 ml-10 state"
|
||||
/>
|
||||
<BadgeState
|
||||
v-clean-tooltip="schedulableCondition.message"
|
||||
:color="schedulableCondition.status === 'True' ? 'bg-success' : 'bg-error' "
|
||||
:icon="schedulableCondition.status === 'True' ? 'icon-checkmark' : 'icon-warning' "
|
||||
v-clean-tooltip="schedulableTooltipMessage"
|
||||
:color="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'bg-success' : 'bg-error' "
|
||||
:icon="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'icon-checkmark' : 'icon-warning' "
|
||||
label="Schedulable"
|
||||
class="mr-10 state"
|
||||
/>
|
||||
|
||||
@ -104,7 +104,7 @@ export default {
|
||||
key-field="_key"
|
||||
|
||||
>
|
||||
<template cell:state="scope">
|
||||
<template cell:state="scope" class="state-col">
|
||||
<div class="state">
|
||||
<HarvesterVmState class="vmstate" :row="scope.row" :all-cluster-network="allClusterNetwork" />
|
||||
</div>
|
||||
|
||||
@ -311,14 +311,12 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
const current = this.ntpSync?.currentNtpServers || '';
|
||||
|
||||
if (status === 'unsynced') {
|
||||
return {
|
||||
status: 'unsynced',
|
||||
warning: {
|
||||
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;
|
||||
},
|
||||
|
||||
virtualSize() {
|
||||
return this.value?.virtualSize;
|
||||
},
|
||||
|
||||
url() {
|
||||
return this.value?.spec?.url || '-';
|
||||
},
|
||||
@ -100,6 +104,12 @@ export default {
|
||||
</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="col span-12">
|
||||
<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 KeyValue from '@shell/components/form/KeyValue';
|
||||
import Labels from '@shell/components/form/Labels';
|
||||
import LabelValue from '@shell/components/LabelValue';
|
||||
import { HCI } from '../../types';
|
||||
import VM_MIXIN from '../../mixins/harvester-vm';
|
||||
|
||||
@ -22,6 +23,7 @@ import Events from './VirtualMachineTabs/VirtualMachineEvents';
|
||||
import Migration from './VirtualMachineTabs/VirtualMachineMigration';
|
||||
import OverviewBasics from './VirtualMachineTabs/VirtualMachineBasics';
|
||||
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';
|
||||
|
||||
@ -33,6 +35,7 @@ export default {
|
||||
Tabbed,
|
||||
Events,
|
||||
OverviewBasics,
|
||||
LabelValue,
|
||||
Volume,
|
||||
Network,
|
||||
OverviewKeypairs,
|
||||
@ -57,6 +60,7 @@ export default {
|
||||
|
||||
data() {
|
||||
return {
|
||||
hasResourceQuotaSchema: false,
|
||||
switchToCloud: false,
|
||||
VM_METRICS_DETAIL_URL,
|
||||
showVmMetrics: false,
|
||||
@ -66,6 +70,8 @@ export default {
|
||||
async created() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
|
||||
this.hasResourceQuotaSchema = !!this.$store.getters[`${ inStore }/schemaFor`](HCI.RESOURCE_QUOTA);
|
||||
|
||||
const hash = {
|
||||
pods: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
|
||||
services: this.$store.dispatch(`${ inStore }/findAll`, { type: SERVICE }),
|
||||
@ -75,6 +81,10 @@ export default {
|
||||
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);
|
||||
|
||||
setPromiseResult(
|
||||
@ -88,6 +98,22 @@ export default {
|
||||
computed: {
|
||||
...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() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
|
||||
@ -175,10 +201,17 @@ export default {
|
||||
<Network v-model:value="networkRows" mode="view" />
|
||||
</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" />
|
||||
</Tab>
|
||||
|
||||
<Tab v-if="hasResourceQuotaSchema" name="quotas" :label="t('harvester.tab.quotas')" :weight="3">
|
||||
<LabelValue
|
||||
:name="t('harvester.snapshot.totalSnapshotSize')"
|
||||
:value="totalSnapshotSize"
|
||||
/>
|
||||
</Tab>
|
||||
|
||||
<Tab
|
||||
v-if="showVmMetrics"
|
||||
name="vm-metrics"
|
||||
|
||||
@ -8,6 +8,7 @@ import { STATE, NAME, AGE } from '@shell/config/table-headers';
|
||||
import { matching } from '@shell/utils/selector';
|
||||
import { NODE } from '@shell/config/types';
|
||||
import { isEmpty } from '@shell/utils/object';
|
||||
import { HCI } from '@pkg/harvester/config/labels-annotations';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -55,10 +56,13 @@ export default {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
|
||||
const nodes = this.$store.getters[`${ inStore }/all`](NODE);
|
||||
const matchedNodes = this.value?.metadata?.annotations?.[HCI.MATCHED_NODES];
|
||||
const selector = this.value?.spec?.nodeSelector;
|
||||
|
||||
if (!isEmpty(selector)) {
|
||||
return matching(nodes, selector);
|
||||
} else if (matchedNodes && matchedNodes.length > 0) {
|
||||
return nodes.filter(node => matchedNodes.includes(node.id));
|
||||
} else {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { Banner } from '@components/Banner';
|
||||
import { Checkbox } from '@components/Form/Checkbox';
|
||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||
import { BadgeState } from '@components/BadgeState';
|
||||
import { ucFirst } from '@shell/utils/string';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -26,7 +27,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
errors: [],
|
||||
unhealthyVM: '',
|
||||
unhealthyVMs: [],
|
||||
force: false
|
||||
};
|
||||
},
|
||||
@ -40,13 +41,21 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
ucFirst,
|
||||
|
||||
onInputForce(v) {
|
||||
if (v) {
|
||||
this.unhealthyVMs = [];
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$emit('close');
|
||||
},
|
||||
|
||||
async apply(buttonCb) {
|
||||
this.errors = [];
|
||||
this.unhealthyVM = '';
|
||||
this.unhealthyVMs = [];
|
||||
|
||||
try {
|
||||
const res = await this.actionResource.doAction('maintenancePossible');
|
||||
@ -62,8 +71,8 @@ export default {
|
||||
} else if (res._status === 200 || res._status === 204) {
|
||||
const res = await this.actionResource.doAction('listUnhealthyVM');
|
||||
|
||||
if (res.message) {
|
||||
this.unhealthyVM = res;
|
||||
if (res?.length) {
|
||||
this.unhealthyVMs = res;
|
||||
buttonCb(false);
|
||||
} else {
|
||||
await this.actionResource.doAction('enableMaintenanceMode', { force: 'false' });
|
||||
@ -95,15 +104,20 @@ export default {
|
||||
<Checkbox
|
||||
v-model:value="force"
|
||||
label-key="harvester.host.enableMaintenance.force"
|
||||
@update:value="onInputForce"
|
||||
/>
|
||||
</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="error mb-5">
|
||||
<Banner color="warning" :label="t('harvester.host.enableMaintenance.protip')" />
|
||||
|
||||
<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>
|
||||
{{ unhealthyVM.message }}
|
||||
{{ ucFirst(unhealthyVM.message) }}
|
||||
</p>
|
||||
</Banner>
|
||||
|
||||
|
||||
@ -72,8 +72,11 @@ export default {
|
||||
const nodes = this.$store.getters['harvester/all'](NODE);
|
||||
|
||||
return nodes.filter((n) => {
|
||||
// do not allow to migrate to self node
|
||||
return !!this.availableNodes.includes(n.id);
|
||||
const isNotSelfNode = !!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) => {
|
||||
let label = 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>
|
||||
|
||||
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
|
||||
<Banner v-for="(err, i) in errors" :key="i"/>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
@ -5,7 +5,7 @@ import { BadgeState } from '@components/BadgeState';
|
||||
import { Banner } from '@components/Banner';
|
||||
import { RadioGroup, RadioButton } from '@components/Form/Radio';
|
||||
import HarvesterDisk from '../../mixins/harvester-disk';
|
||||
import DiskTags from '../../components/DiskTags';
|
||||
import Tags from '../../components/DiskTags';
|
||||
import { HCI } from '../../types';
|
||||
import { LONGHORN_SYSTEM } from './index';
|
||||
|
||||
@ -17,7 +17,7 @@ export default {
|
||||
Banner,
|
||||
RadioGroup,
|
||||
RadioButton,
|
||||
DiskTags,
|
||||
Tags,
|
||||
},
|
||||
|
||||
mixins: [
|
||||
@ -44,6 +44,18 @@ export default {
|
||||
return {};
|
||||
},
|
||||
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() {
|
||||
return [{
|
||||
label: this.t('generic.enabled'),
|
||||
@ -171,7 +183,7 @@ export default {
|
||||
<div v-if="!value.isNew">
|
||||
<div class="row">
|
||||
<div class="col span-12">
|
||||
<DiskTags
|
||||
<Tags
|
||||
v-model:value="value.tags"
|
||||
:label="t('harvester.host.disk.tags.label')"
|
||||
:add-label="t('harvester.host.disk.tags.addLabel')"
|
||||
@ -181,6 +193,16 @@ export default {
|
||||
</div>
|
||||
<div class="row mt-10">
|
||||
<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">
|
||||
{{ t('harvester.host.disk.conditions') }}:
|
||||
<BadgeState
|
||||
@ -191,9 +213,9 @@ export default {
|
||||
class="mr-10 ml-10 state"
|
||||
/>
|
||||
<BadgeState
|
||||
v-clean-tooltip="schedulableCondition.message"
|
||||
:color="schedulableCondition.status === 'True' ? 'bg-success' : 'bg-error' "
|
||||
:icon="schedulableCondition.status === 'True' ? 'icon-checkmark' : 'icon-warning' "
|
||||
v-clean-tooltip="schedulableTooltipMessage"
|
||||
:color="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'bg-success' : 'bg-error' "
|
||||
:icon="schedulableCondition.status === 'True' && targetDisk?.allowScheduling ? 'icon-checkmark' : 'icon-warning' "
|
||||
label="Schedulable"
|
||||
class="mr-10 state"
|
||||
/>
|
||||
|
||||
@ -5,6 +5,7 @@ import UnitInput from '@shell/components/form/UnitInput';
|
||||
import { RadioGroup } from '@components/Form/Radio';
|
||||
import { Checkbox } from '@components/Form/Checkbox';
|
||||
import { HCI } from '../../types';
|
||||
import { DOC_LINKS } from '../../config/doc-links';
|
||||
|
||||
export const ksmtunedMode = [{
|
||||
value: 'standard',
|
||||
@ -86,6 +87,10 @@ export default {
|
||||
|
||||
showKsmt() {
|
||||
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')" />
|
||||
|
||||
<h3>
|
||||
<t k="harvester.host.ksmtuned.modeLink" :raw="true" />
|
||||
<t k="harvester.host.ksmtuned.modeLink" :raw="true" :url="ksmtunedLink" />
|
||||
</h3>
|
||||
<RadioGroup
|
||||
v-model:value="spec.mode"
|
||||
|
||||
@ -25,10 +25,10 @@ import { _EDIT } from '@shell/config/query-params';
|
||||
import { sortBy } from '@shell/utils/sort';
|
||||
import { Banner } from '@components/Banner';
|
||||
import { HCI } from '../../types';
|
||||
import DiskTags from '../../components/DiskTags';
|
||||
import HarvesterDisk from './HarvesterDisk';
|
||||
import HarvesterSeeder from './HarvesterSeeder';
|
||||
import HarvesterKsmtuned from './HarvesterKsmtuned';
|
||||
import Tags from '../../components/DiskTags';
|
||||
|
||||
export const LONGHORN_SYSTEM = 'longhorn-system';
|
||||
|
||||
@ -46,7 +46,7 @@ export default {
|
||||
ButtonDropdown,
|
||||
KeyValue,
|
||||
Banner,
|
||||
DiskTags,
|
||||
Tags,
|
||||
Loading,
|
||||
HarvesterSeeder,
|
||||
MessageLink,
|
||||
@ -478,10 +478,11 @@ export default {
|
||||
|
||||
const disks = this.longhornNode?.spec?.disks || {};
|
||||
|
||||
// update each disk tags and scheduling
|
||||
this.newDisks.map((disk) => {
|
||||
(disks[disk.name] || {}).tags = disk.tags;
|
||||
(disks[disk.name] || {}).allowScheduling = disk.allowScheduling;
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
|
||||
const retrySave = async() => {
|
||||
@ -505,7 +506,9 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
if (this.longhornNode) {
|
||||
await retrySave();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -547,7 +550,7 @@ export default {
|
||||
class="row mb-20"
|
||||
>
|
||||
<div class="col span-12">
|
||||
<DiskTags
|
||||
<Tags
|
||||
v-model:value="longhornNode.spec.tags"
|
||||
:label="t('harvester.host.tags.label')"
|
||||
:add-label="t('harvester.host.tags.addLabel')"
|
||||
@ -650,7 +653,6 @@ export default {
|
||||
:value="filteredLabels"
|
||||
:add-label="t('labels.addLabel')"
|
||||
:mode="mode"
|
||||
:title="t('labels.labels.title')"
|
||||
:read-allowed="false"
|
||||
:value-can-be-empty="true"
|
||||
@update:value="updateHostLabels"
|
||||
|
||||
@ -143,10 +143,19 @@ export default {
|
||||
},
|
||||
|
||||
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 !== '') {
|
||||
this.config.vlan = neu > 4094 ? 4094 : 1;
|
||||
return;
|
||||
}
|
||||
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 { clone } from '@shell/utils/object';
|
||||
import { CSI_DRIVER } from '../../types';
|
||||
import DiskTags from '../../components/DiskTags';
|
||||
import Tags from '../../components/DiskTags';
|
||||
|
||||
const LONGHORN_DRIVER = 'driver.longhorn.io';
|
||||
|
||||
@ -34,7 +34,7 @@ export default {
|
||||
Tab,
|
||||
Tabbed,
|
||||
Loading,
|
||||
DiskTags,
|
||||
Tags,
|
||||
},
|
||||
|
||||
mixins: [CreateEditView],
|
||||
@ -304,7 +304,7 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
<div class="col span-8 value">
|
||||
<DiskTags
|
||||
<Tags
|
||||
v-model:value="scope.row.value.values"
|
||||
:add-label="t('generic.add')"
|
||||
:mode="modeOverride"
|
||||
|
||||
@ -162,6 +162,7 @@ export default {
|
||||
buttonCb(false);
|
||||
}
|
||||
} else {
|
||||
this.value.spec.url = this.value.spec.url?.trim() || '';
|
||||
this.save(buttonCb);
|
||||
}
|
||||
},
|
||||
@ -277,6 +278,7 @@ export default {
|
||||
:can-yaml="showEditAsYaml ? true : false"
|
||||
:apply-hooks="applyHooks"
|
||||
@finish="saveImage"
|
||||
@error="e=>errors=e"
|
||||
>
|
||||
<NameNsDescription
|
||||
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 { AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
|
||||
import { HCI } from '../types';
|
||||
import { RunStrategys } from '../config/harvester-map';
|
||||
import VM_MIXIN from '../mixins/harvester-vm';
|
||||
import Reserved from './kubevirt.io.virtualmachine/VirtualMachineReserved';
|
||||
import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume';
|
||||
@ -74,7 +73,6 @@ export default {
|
||||
description: '',
|
||||
defaultVersion: null,
|
||||
isDefaultVersion: false,
|
||||
RunStrategys,
|
||||
};
|
||||
},
|
||||
|
||||
@ -263,7 +261,7 @@ export default {
|
||||
<Tab
|
||||
name="nodeScheduling"
|
||||
:label="t('workload.container.titles.nodeScheduling')"
|
||||
:weight="-89"
|
||||
:weight="-3"
|
||||
>
|
||||
<template #default="{active}">
|
||||
<NodeScheduling
|
||||
@ -274,7 +272,7 @@ export default {
|
||||
</template>
|
||||
</Tab>
|
||||
|
||||
<Tab :label="t('harvester.tab.vmScheduling')" name="vmScheduling" :weight="-90">
|
||||
<Tab :label="t('harvester.tab.vmScheduling')" name="vmScheduling" :weight="-4">
|
||||
<template #default="{active}">
|
||||
<PodAffinity
|
||||
:mode="mode"
|
||||
@ -287,13 +285,42 @@ export default {
|
||||
</template>
|
||||
</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">
|
||||
<div class="row mb-20">
|
||||
<div class="col span-6">
|
||||
<LabeledSelect
|
||||
v-model:value="runStrategy"
|
||||
label-key="harvester.virtualMachine.runStrategy"
|
||||
:options="RunStrategys"
|
||||
:options="runStrategies"
|
||||
:mode="mode"
|
||||
/>
|
||||
</div>
|
||||
@ -309,12 +336,15 @@ export default {
|
||||
</div>
|
||||
|
||||
<div class="row mb-20">
|
||||
<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" />
|
||||
<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 v-if="showAdvanced">
|
||||
<div class="row mb-20">
|
||||
<div class="col span-6">
|
||||
<Reserved
|
||||
:reserved-memory="reservedMemory"
|
||||
@ -322,6 +352,27 @@ export default {
|
||||
@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">
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
<div v-if="showAdvanced">
|
||||
<div class="row mb-20">
|
||||
<div class="col span-6">
|
||||
<UnitInput
|
||||
v-model:value="terminationGracePeriodSeconds"
|
||||
@ -386,35 +437,6 @@ export default {
|
||||
:mode="mode"
|
||||
/>
|
||||
</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>
|
||||
</CruResource>
|
||||
</template>
|
||||
|
||||
@ -56,6 +56,7 @@ export default {
|
||||
const hash = await allHash(_hash);
|
||||
|
||||
this.snapshots = hash.snapshots;
|
||||
this.images = hash.images;
|
||||
|
||||
const defaultStorage = this.$store.getters[`harvester/all`](STORAGE_CLASS).find( O => O.isDefault);
|
||||
|
||||
@ -77,6 +78,7 @@ export default {
|
||||
storage,
|
||||
imageId,
|
||||
snapshots: [],
|
||||
images: [],
|
||||
};
|
||||
},
|
||||
|
||||
@ -108,10 +110,8 @@ export default {
|
||||
},
|
||||
|
||||
imageOption() {
|
||||
const choices = this.$store.getters['harvester/all'](HCI.IMAGE);
|
||||
|
||||
return sortBy(
|
||||
choices
|
||||
this.images
|
||||
.filter(obj => obj.isReady)
|
||||
.map((obj) => {
|
||||
return {
|
||||
@ -249,7 +249,17 @@ export default {
|
||||
|
||||
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() {
|
||||
const out = saferDump(this.value);
|
||||
|
||||
@ -300,7 +310,7 @@ export default {
|
||||
required
|
||||
:mode="mode"
|
||||
class="mb-20"
|
||||
@update:value="update"
|
||||
@update:value="updateImage"
|
||||
/>
|
||||
|
||||
<LabeledSelect
|
||||
|
||||
@ -140,15 +140,24 @@ export default {
|
||||
|
||||
onImageChange() {
|
||||
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 (/iso$/i.test(imageResource?.imageSuffix)) {
|
||||
if (isIsoImage) {
|
||||
this.value['type'] = 'cd-rom';
|
||||
this.value['bus'] = 'sata';
|
||||
} else {
|
||||
this.value['type'] = 'disk';
|
||||
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();
|
||||
|
||||
@ -28,7 +28,6 @@ import CreateEditView from '@shell/mixins/create-edit-view';
|
||||
|
||||
import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
||||
import VM_MIXIN from '../../mixins/harvester-vm';
|
||||
import { RunStrategys } from '../../config/harvester-map';
|
||||
import { HCI } from '../../types';
|
||||
import RestartVMDialog from '../../dialog/RestartVMDialog';
|
||||
import VirtualMachineVGpuDevices from './VirtualMachineVGpuDevices/index';
|
||||
@ -40,6 +39,8 @@ import Network from './VirtualMachineNetwork';
|
||||
import Volume from './VirtualMachineVolume';
|
||||
import SSHKey from './VirtualMachineSSHKey';
|
||||
import Reserved from './VirtualMachineReserved';
|
||||
import { Banner } from '@components/Banner';
|
||||
import MessageLink from '@shell/components/MessageLink';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterEditVM',
|
||||
@ -68,6 +69,8 @@ export default {
|
||||
UnitInput,
|
||||
VirtualMachineVGpuDevices,
|
||||
KeyValue,
|
||||
Banner,
|
||||
MessageLink
|
||||
},
|
||||
|
||||
mixins: [CreateEditView, VM_MIXIN],
|
||||
@ -96,13 +99,19 @@ export default {
|
||||
isOpen: false,
|
||||
hostname,
|
||||
isRestartImmediately,
|
||||
RunStrategys,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({ t: 'i18n/t' }),
|
||||
|
||||
to() {
|
||||
return {
|
||||
name: 'harvester-c-cluster-resource',
|
||||
params: { cluster: this.$store.getters['clusterId'], resource: HCI.HOST },
|
||||
};
|
||||
},
|
||||
|
||||
machineTypeOptions() {
|
||||
return [{
|
||||
label: 'None',
|
||||
@ -173,6 +182,26 @@ export default {
|
||||
hasStartAction() {
|
||||
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: {
|
||||
@ -273,6 +302,15 @@ export default {
|
||||
},
|
||||
|
||||
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) {
|
||||
clear(this.errors);
|
||||
|
||||
@ -437,12 +475,14 @@ export default {
|
||||
id="vm"
|
||||
:done-route="doneRoute"
|
||||
:resource="value"
|
||||
:cancelEvent="true"
|
||||
:mode="mode"
|
||||
:can-yaml="isSingle ? true : false"
|
||||
:errors="errors"
|
||||
:generate-yaml="generateYaml"
|
||||
:apply-hooks="applyHooks"
|
||||
@finish="saveVM"
|
||||
@cancel="cancelAction"
|
||||
>
|
||||
<RadioGroup
|
||||
v-if="isCreate"
|
||||
@ -606,44 +646,54 @@ export default {
|
||||
/>
|
||||
</Tab>
|
||||
|
||||
<Tab
|
||||
v-if="enabledSriovgpu"
|
||||
:label="t('harvester.tab.vGpuDevices')"
|
||||
name="vGpuDevices"
|
||||
:weight="-6"
|
||||
>
|
||||
<VirtualMachineVGpuDevices
|
||||
:mode="mode"
|
||||
:value="spec.template.spec"
|
||||
:vm="value"
|
||||
/>
|
||||
<Tab v-if="enabledSriovgpu" :label="t('harvester.tab.vGpuDevices')" name="vGpuDevices" :weight="-6">
|
||||
<VirtualMachineVGpuDevices :mode="mode" :value="spec.template.spec" :vm="value" />
|
||||
</Tab>
|
||||
|
||||
<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" />
|
||||
</Tab>
|
||||
|
||||
<Tab
|
||||
v-if="isEdit"
|
||||
:label="t('harvester.tab.accessCredentials')"
|
||||
name="accessCredentials"
|
||||
:weight="-7"
|
||||
name="instanceLabel"
|
||||
:label="t('harvester.tab.instanceLabel')"
|
||||
:weight="-8"
|
||||
>
|
||||
<AccessCredentials
|
||||
v-model:value="accessCredentials"
|
||||
<Labels
|
||||
:default-container-class="'labels-and-annotations-container'"
|
||||
:value="value"
|
||||
:mode="mode"
|
||||
:resource="value"
|
||||
:is-qemu-installed="isQemuInstalled"
|
||||
: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>
|
||||
|
||||
<Tab
|
||||
name="advanced"
|
||||
:label="t('harvester.tab.advanced')"
|
||||
:weight="-8"
|
||||
:weight="-9"
|
||||
>
|
||||
<div class="row mb-20">
|
||||
<div class="col span-6">
|
||||
<LabeledSelect
|
||||
v-model:value="runStrategy"
|
||||
label-key="harvester.virtualMachine.runStrategy"
|
||||
:options="RunStrategys"
|
||||
:options="runStrategies"
|
||||
:mode="mode"
|
||||
/>
|
||||
</div>
|
||||
@ -659,24 +709,30 @@ export default {
|
||||
</div>
|
||||
|
||||
<div class="row mb-20">
|
||||
<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"
|
||||
<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
|
||||
v-if="showAdvanced"
|
||||
class="mb-20"
|
||||
>
|
||||
<div class="row mb-20">
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
<div v-if="showAdvanced" class="mb-20">
|
||||
<div class="row mb-20">
|
||||
<div class="col span-6">
|
||||
<LabeledInput
|
||||
@ -698,13 +754,6 @@ export default {
|
||||
</div>
|
||||
|
||||
<div class="row mb-20">
|
||||
<div class="col span-6">
|
||||
<Reserved
|
||||
:reserved-memory="reservedMemory"
|
||||
:mode="mode"
|
||||
@updateReserved="updateReserved"
|
||||
/>
|
||||
</div>
|
||||
<div class="col span-6">
|
||||
<UnitInput
|
||||
v-model:value="terminationGracePeriodSeconds"
|
||||
@ -730,6 +779,16 @@ export default {
|
||||
@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
|
||||
v-model:value="installUSBTablet"
|
||||
class="check mt-20"
|
||||
@ -773,35 +832,21 @@ export default {
|
||||
:label="t('harvester.virtualMachine.secureBoot')"
|
||||
:mode="mode"
|
||||
/>
|
||||
</Tab>
|
||||
|
||||
<Tab
|
||||
name="instanceLabel"
|
||||
:label="t('harvester.tab.instanceLabel')"
|
||||
:weight="-99"
|
||||
<Banner
|
||||
v-if="showCpuPinningBanner"
|
||||
color="warning"
|
||||
>
|
||||
<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)"
|
||||
<MessageLink
|
||||
v-if="mode === 'create'"
|
||||
:to="to"
|
||||
prefix-label="harvester.virtualMachine.advancedOptions.cpuManager.prefix"
|
||||
middle-label="harvester.virtualMachine.advancedOptions.cpuManager.middle"
|
||||
suffix-label="harvester.virtualMachine.advancedOptions.cpuManager.suffix"
|
||||
/>
|
||||
</template>
|
||||
</Labels>
|
||||
<span v-if="mode==='edit'">
|
||||
{{ t('harvester.virtualMachine.cpuPinning.restartVMMessage') }}
|
||||
</span>
|
||||
</Banner>
|
||||
</Tab>
|
||||
</Tabbed>
|
||||
|
||||
|
||||
@ -314,12 +314,21 @@ export default {
|
||||
} else if (selector[HOSTNAME] && Object.keys(selector).length === 1) {
|
||||
const matchNode = allNodes.find(n => n.id === selector[HOSTNAME]);
|
||||
|
||||
if (matchNode) {
|
||||
this.matchingNodes = {
|
||||
matched: 1,
|
||||
total: allNodes.length,
|
||||
none: false,
|
||||
sample: matchNode ? matchNode.nameDisplay : selector[HOSTNAME],
|
||||
sample: matchNode.nameDisplay,
|
||||
};
|
||||
} else {
|
||||
this.matchingNodes = {
|
||||
matched: 0,
|
||||
total: 0,
|
||||
none: true,
|
||||
sample: null,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
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: ''
|
||||
},
|
||||
|
||||
showReserved: {
|
||||
showAllocated: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
async fetch() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
|
||||
this.longhornSettings = await this.$store.dispatch(`${ inStore }/findAll`, { type: LONGHORN.SETTINGS });
|
||||
},
|
||||
|
||||
data() {
|
||||
return {};
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const longhornSettings = this.$store.getters[`${ inStore }/all`](LONGHORN.SETTINGS) || [];
|
||||
|
||||
return { longhornSettings };
|
||||
},
|
||||
|
||||
computed: {
|
||||
usage() {
|
||||
storageStats() {
|
||||
const stats = {
|
||||
used: 0,
|
||||
scheduled: 0,
|
||||
maximum: 0,
|
||||
reserved: 0,
|
||||
total: 0
|
||||
};
|
||||
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() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const longhornNode = this.$store.getters[`${ inStore }/byId`](LONGHORN.NODES, `longhorn-system/${ this.row.id }`);
|
||||
let reserved = 0;
|
||||
|
||||
const disks = longhornNode?.spec?.disks || {};
|
||||
|
||||
Object.values(disks).map((disk) => {
|
||||
if (disk.allowScheduling) {
|
||||
reserved += disk.storageReserved;
|
||||
}
|
||||
Object.keys(disks).map((key) => {
|
||||
stats.scheduled += node?.spec?.allowScheduling ? (diskStatus[key]?.storageScheduled || 0) : 0;
|
||||
stats.reserved += disks[key]?.storageReserved || 0;
|
||||
});
|
||||
Object.values(diskStatus).map((diskStat) => {
|
||||
stats.maximum += diskStat?.storageMaximum || 0;
|
||||
});
|
||||
|
||||
return reserved;
|
||||
},
|
||||
stats.total = ((stats.maximum - stats.reserved) * Number(storageOverProvisioningPercentageSetting?.value ?? 0)) / 100;
|
||||
|
||||
total() {
|
||||
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;
|
||||
return stats;
|
||||
},
|
||||
|
||||
units() {
|
||||
const exponent = exponentNeeded(this.total, 1024);
|
||||
const exponent = exponentNeeded(this.storageStats.maximum, 1024);
|
||||
|
||||
return `${ UNITS[exponent] }iB`;
|
||||
},
|
||||
|
||||
used() {
|
||||
let out = this.formatter(this.usage || 0);
|
||||
let out = this.formatter(this.storageStats.used);
|
||||
|
||||
if (!Number.parseFloat(out) > 0) {
|
||||
out = this.formatter(this.usage || 0, { canRoundToZero: false });
|
||||
out = this.formatter(this.storageStats.used, { canRoundToZero: false });
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
formatReserved() {
|
||||
let out = this.formatter(this.reserved || 0);
|
||||
formatAllocated() {
|
||||
let out = this.formatter(this.storageStats.scheduled);
|
||||
|
||||
if (!Number.parseFloat(out) > 0) {
|
||||
out = this.formatter(this.reserved || 0, { canRoundToZero: false });
|
||||
out = this.formatter(this.storageStats.scheduled, { canRoundToZero: false });
|
||||
}
|
||||
|
||||
return out;
|
||||
@ -102,15 +101,15 @@ export default {
|
||||
usedAmountTemplateValues() {
|
||||
return {
|
||||
used: this.used,
|
||||
total: this.formatter(this.total || 0),
|
||||
total: this.formatter(this.storageStats.maximum),
|
||||
unit: this.units,
|
||||
};
|
||||
},
|
||||
|
||||
reservedAmountTemplateValues() {
|
||||
allocatedAmountTemplateValues() {
|
||||
return {
|
||||
used: this.formatReserved,
|
||||
total: this.formatter(this.total || 0),
|
||||
used: this.formatAllocated,
|
||||
total: this.formatter(this.storageStats.total),
|
||||
unit: this.units,
|
||||
};
|
||||
},
|
||||
@ -118,7 +117,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
formatter(value, format) {
|
||||
const minExponent = exponentNeeded(this.total, 1024);
|
||||
const minExponent = exponentNeeded(this.storageStats.maximum, 1024);
|
||||
const formatOptions = {
|
||||
addSuffix: false,
|
||||
increment: 1024,
|
||||
@ -137,21 +136,21 @@ export default {
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="showReserved"
|
||||
v-if="showAllocated"
|
||||
>
|
||||
<ConsumptionGauge
|
||||
:capacity="total"
|
||||
:used="reserved"
|
||||
:capacity="storageStats.total"
|
||||
:used="storageStats.scheduled"
|
||||
:units="units"
|
||||
:number-formatter="formatter"
|
||||
:resource-name="resourceName"
|
||||
>
|
||||
<template #title="{formattedPercentage}">
|
||||
<span>
|
||||
{{ t('clusterIndexPage.hardwareResourceGauge.reserved') }}
|
||||
{{ t('clusterIndexPage.hardwareResourceGauge.allocated') }}
|
||||
</span>
|
||||
<span class="precent-data">
|
||||
{{ t('node.detail.glance.consumptionGauge.amount', reservedAmountTemplateValues) }}
|
||||
{{ t('node.detail.glance.consumptionGauge.amount', allocatedAmountTemplateValues) }}
|
||||
<span class="ml-10 percentage">
|
||||
/ {{ formattedPercentage }}
|
||||
</span>
|
||||
@ -160,13 +159,13 @@ export default {
|
||||
</ConsumptionGauge>
|
||||
</div>
|
||||
<ConsumptionGauge
|
||||
:capacity="total"
|
||||
:used="usage"
|
||||
:capacity="storageStats.maximum"
|
||||
:used="storageStats.used"
|
||||
:units="units"
|
||||
:number-formatter="formatter"
|
||||
:resource-name="showReserved ? '' : resourceName"
|
||||
:resource-name="showAllocated ? '' : resourceName"
|
||||
:class="{
|
||||
'mt-10': showReserved,
|
||||
'mt-10': showAllocated,
|
||||
}"
|
||||
>
|
||||
<template #title="{formattedPercentage}">
|
||||
|
||||
@ -6,14 +6,15 @@ generic:
|
||||
labels: Labels
|
||||
inProgress: In Progress
|
||||
basic: Basic
|
||||
loading: Loading...
|
||||
|
||||
nav:
|
||||
group:
|
||||
networks: Networks
|
||||
backupAndSnapshot: Backup & Snapshot
|
||||
backupAndSnapshot: Backup and Snapshots
|
||||
Monitoring: Monitoring
|
||||
Logging: Logging
|
||||
'Monitoring & Logging': Monitoring & Logging
|
||||
'Monitoring and Logging': Monitoring and Logging
|
||||
|
||||
resourceTable:
|
||||
groupBy:
|
||||
@ -28,7 +29,7 @@ members:
|
||||
|
||||
asyncButton:
|
||||
restart:
|
||||
action: Save & Restart
|
||||
action: Save and Restart
|
||||
success: Restarted
|
||||
waiting: Restarting…
|
||||
|
||||
@ -38,9 +39,13 @@ harvester:
|
||||
backup:
|
||||
success: 'Backup { backUpName } has been initiated.'
|
||||
addBackup: Add Backup
|
||||
quota:
|
||||
editVMQuota: Edit VM Quota
|
||||
editQuota: Edit Quota
|
||||
bannerMessage: Set to empty string or 0 to remove total snapshot size quota.
|
||||
restore:
|
||||
success: 'Restore { name } created successfully.'
|
||||
title: Backup and restore
|
||||
title: Backup and Restore
|
||||
selectBackup: Select Backup
|
||||
message:
|
||||
backup: Please select the backup that needs to be restored.
|
||||
@ -53,16 +58,16 @@ harvester:
|
||||
success: 'Template { templateName } created successfully.'
|
||||
failed: 'Failed generated template!'
|
||||
cloneVM:
|
||||
title: Clone VM
|
||||
name: New VM Name
|
||||
title: Clone Virtual Machine
|
||||
name: New Virtual Machine Name
|
||||
type: Clone volume data
|
||||
action:
|
||||
create: Create
|
||||
clone: Clone
|
||||
message:
|
||||
tip: Please enter a VM name!
|
||||
success: 'VM { name } cloned successfully.'
|
||||
failed: 'Failed clone VM!'
|
||||
tip: Please enter a virtual machine name!
|
||||
success: 'Virtual machine { name } cloned successfully.'
|
||||
failed: 'Failed clone virtual machine!'
|
||||
exportImage:
|
||||
title: Export to Image
|
||||
name: Name
|
||||
@ -77,21 +82,21 @@ harvester:
|
||||
label: Target Node
|
||||
placeholder: Choose Target Node
|
||||
ejectCDROM:
|
||||
title: Eject CDROM
|
||||
title: Eject CD-ROM
|
||||
warnTip: Eject volume will restart the virtual machine.
|
||||
operationTip: 'Select the volume you want to delete:'
|
||||
delete: Delete
|
||||
bundle:
|
||||
title: Generate Support Bundle
|
||||
title: Generate a Support Bundle
|
||||
url: Issue URL
|
||||
description: Description
|
||||
requiredDesc: Description is required!
|
||||
titleDescription: Collect system-related log in Harvester, to help with troubleshooting and support.
|
||||
titleDescription: Collect system-related logs in Harvester to help with troubleshooting and support.
|
||||
hotplug:
|
||||
success: 'Volume { diskName } is mounted to the VM { vm }.'
|
||||
success: 'Volume { diskName } is mounted to the virtual machine { vm }.'
|
||||
title: Add Volume
|
||||
hotunplug:
|
||||
success: 'Volume { name } is detach successfully.'
|
||||
success: 'Volume { name } is detached successfully.'
|
||||
snapshot:
|
||||
title: Take Snapshot
|
||||
name: Name
|
||||
@ -109,10 +114,10 @@ harvester:
|
||||
vmSnapshot:
|
||||
title: Take VM Snapshot
|
||||
name: Name
|
||||
success: 'Take VM Snapshot { name } successfully.'
|
||||
success: 'Take virtual machine Snapshot { name } successfully.'
|
||||
restart:
|
||||
title: Restart VM
|
||||
tip: Restart the Virtual Machine now for configuration changes to take effect.
|
||||
title: Restart Virtual Machine
|
||||
tip: Restart the virtual machine for configuration changes to take effect.
|
||||
cancel: Save
|
||||
notification:
|
||||
title:
|
||||
@ -130,11 +135,12 @@ harvester:
|
||||
deepClone: Clone
|
||||
shallowClone: Clone Template
|
||||
unpause: Unpause
|
||||
ejectCDROM: Eject CDROM
|
||||
ejectCDROM: Eject CD-ROM
|
||||
editVMQuota: Edit VM Quota
|
||||
launchFormTemplate: Launch instance from template
|
||||
modifyTemplate: Modify template (Create new version)
|
||||
setDefaultVersion: Set default version
|
||||
addTemplateVersion: Add templateVersion
|
||||
addTemplateVersion: Add template version
|
||||
backup: Take Backup
|
||||
restore: Restore
|
||||
restoreNewVM: Restore New
|
||||
@ -144,6 +150,8 @@ harvester:
|
||||
createTemplate: Generate Template
|
||||
enableMaintenance: Enable Maintenance Mode
|
||||
disableMaintenance: Disable Maintenance Mode
|
||||
enableCPUManager: Enable CPU Manager
|
||||
disableCPUManager: Disable CPU Manager
|
||||
cordon: Cordon
|
||||
uncordon: Uncordon
|
||||
addHotplug: Add Volume
|
||||
@ -152,23 +160,25 @@ harvester:
|
||||
cancelExpand: Cancel Expand
|
||||
snapshot: Take Snapshot
|
||||
pvcClone: Clone Volume
|
||||
vmSnapshot: Take VM Snapshot
|
||||
vmSnapshot: Take Virtual Machine Snapshot
|
||||
shutdown: Shut Down
|
||||
powerOn: Power On
|
||||
reboot: Reboot
|
||||
forceStop: Force Stop
|
||||
tableHeaders:
|
||||
size: Size
|
||||
virtualSize: Virtual Size
|
||||
progress: Progress
|
||||
message: Message
|
||||
phase: Phase
|
||||
attachedVM: Attached VM
|
||||
attachedVM: Attached Virtual Machine
|
||||
cpuManager: CPU Manager
|
||||
fingerprint: Fingerprint
|
||||
value: Value
|
||||
actions: Actions
|
||||
readyToUse: Ready To Use
|
||||
backupTarget: Backup Target
|
||||
targetVm: Target VM
|
||||
targetVm: Target Virtual Machine
|
||||
hostIp: Host IP
|
||||
vm:
|
||||
ipAddress: IP Address
|
||||
@ -176,10 +186,11 @@ harvester:
|
||||
defaultVersion: Default Version
|
||||
network:
|
||||
type: Type
|
||||
vlan: Vlan ID
|
||||
vlan: VLAN ID
|
||||
snapshotTargetVolume: Original Volume
|
||||
volumeSnapshotCounts: Snapshot Counts
|
||||
networkState: Network State
|
||||
totalSnapshotQuota: Total Snapshot Quota
|
||||
storageClass: Storage Class
|
||||
restore: Restore
|
||||
tab:
|
||||
@ -188,8 +199,10 @@ harvester:
|
||||
advanced: Advanced Options
|
||||
accessCredentials: Access Credentials
|
||||
pciDevices: PCI Devices
|
||||
vGpuDevices: VGPU Devices
|
||||
vmScheduling: VM Scheduling
|
||||
vGpuDevices: vGPU Devices
|
||||
vmScheduling: Virtual Machine Scheduling
|
||||
quotas: Quotas
|
||||
snapshots: Snapshots
|
||||
instanceLabel: Instance Labels
|
||||
fields:
|
||||
version: Version
|
||||
@ -200,24 +213,24 @@ harvester:
|
||||
volume: Volume
|
||||
network: Network
|
||||
model: Model
|
||||
macAddress: Mac Address
|
||||
macAddress: MAC address
|
||||
port: Port
|
||||
protocol: Protocol
|
||||
remove: REMOVE
|
||||
remove: Remove
|
||||
PhysicalNic: Physical NIC
|
||||
cpu: Cpu
|
||||
cpu: CPU
|
||||
memory: Memory
|
||||
virtualName: Virtual machine name
|
||||
promiscuous: Promiscuous
|
||||
ipv4Address: IPv4 Address
|
||||
filterLabels: Filter Labels
|
||||
storageClass: Storage Class
|
||||
dockerImage: Docker Image
|
||||
ipv4Address: IPv4 address
|
||||
filterLabels: Filter labels
|
||||
storageClass: Storage class
|
||||
dockerImage: Docker image
|
||||
pci:
|
||||
available: Available Devices
|
||||
compatibleNodes: Compatible Nodes
|
||||
impossibleSelection: 'There are no hosts with all of the selected devices.'
|
||||
howToUseDevice: 'Use the table below to enable PCI passthrough on each device you want to use in this VM.'
|
||||
howToUseDevice: 'Use the table below to enable PCI passthrough on each device you want to use in this virtual machine.'
|
||||
deviceInTheSameHost: 'You can only select devices on the same host.'
|
||||
oldFormatDevices:
|
||||
help: |-
|
||||
@ -228,11 +241,11 @@ harvester:
|
||||
{oldFormatDevicesHTML}
|
||||
</ul>
|
||||
<p>
|
||||
Please use the following instructions to update the VM:
|
||||
Please use the following instructions to update the virtual machine:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Stop the VM, edit the VM YAML, and remove the <Code>hostDevices</Code> section, and save VM the changes to the YAML file.</li>
|
||||
<li>Edit the VM, and add the already enabled PCIDevice from the list of available PCIDevices, and save and start VM.</li>
|
||||
<li>Stop the virtual machine, edit the virtual machine YAML, and remove the <Code>hostDevices</Code> section, and save virtual machine the changes to the YAML file.</li>
|
||||
<li>Edit the virtual machine, and add the already enabled PCI Device from the list of available PCIDevices, and save and start VM.</li>
|
||||
</ol>
|
||||
showCompatibility: Show device compatibility matrix
|
||||
hideCompatibility: Hide device compatibility matrix
|
||||
@ -241,12 +254,12 @@ harvester:
|
||||
cantUnclaim: You cannot disable passthrough on a device claimed by another user.
|
||||
enableGroup: Enable Group
|
||||
disableGroup: Disable Group
|
||||
labelRequired: "This rule should not be manually altered: it ensures that the PCI devices selected for this virtual machine are available on the VM's host."
|
||||
labelRequired: "This rule should not be manually altered: it ensures that the PCI devices selected for this virtual machine are available on the virtual machine's host."
|
||||
goSetting:
|
||||
prefix: The pcidevices-controller addon is not enabled, click
|
||||
prefix: The pcidevices-controller add-on is not enabled, click
|
||||
middle: here
|
||||
suffix: to enable it to manage your PCI devices.
|
||||
noPCIPermission: Please contact system admin to enable the PCI devices first.
|
||||
noPCIPermission: Please contact your system administrator to enable the PCI devices first.
|
||||
enablePassthroughWarning: Please be careful not to use host-owned PCI devices (e.g., management and VLAN NICs). Incorrect device allocation may cause damage to your cluster, including node failure.
|
||||
matrixHostName: Host Name
|
||||
matrixDeviceClaimName: Device Claim Name
|
||||
@ -299,20 +312,21 @@ harvester:
|
||||
events:
|
||||
label: Events
|
||||
vmMetrics:
|
||||
label: VM Metrics
|
||||
label: Virtual Machine Metrics
|
||||
|
||||
version: Version
|
||||
host:
|
||||
console: Console
|
||||
label: Hosts
|
||||
inconsistentIP: "Host IP is inconsistent, current IP: { currentIP }, initial IP: { initIP }"
|
||||
noConsoleUrl: 'Console URL not specified'
|
||||
promote:
|
||||
none: ' '
|
||||
running: promoting
|
||||
failed: promote failed
|
||||
unknown: promote halted
|
||||
promoteRestart: restarting
|
||||
promoteSucceed: promote completed
|
||||
running: Promoting
|
||||
failed: Promote failed
|
||||
unknown: Promote halted
|
||||
promoteRestart: Restarting
|
||||
promoteSucceed: Promote completed
|
||||
tabs:
|
||||
network: Network
|
||||
overview: Overview
|
||||
@ -322,10 +336,10 @@ harvester:
|
||||
storage: Storage
|
||||
labels: Labels
|
||||
ksmtuned: Ksmtuned
|
||||
seeder: Out-of-Band Access
|
||||
seeder: Out-of-band Access
|
||||
detail:
|
||||
kvm:
|
||||
disableMessage: Hardware-based virtualization is disabled or not supported. Hardware-based virtualization must be enabled before creating any Virtual Machines.
|
||||
disableMessage: Hardware-based virtualization is disabled or not supported. Hardware-based virtualization must be enabled before creating any virtual machines.
|
||||
title:
|
||||
network: Network Configuration
|
||||
hostIP: Host IP
|
||||
@ -349,9 +363,11 @@ harvester:
|
||||
serialNumber: Serial Number
|
||||
model: Model
|
||||
etcd: Witness Node
|
||||
cpuManager: CPU Manager
|
||||
enableMaintenance:
|
||||
title: Enable Maintenance Mode
|
||||
protip: The operation will migrate all virtual machines on this node to other nodes.
|
||||
shutDownVMs: Check <b>Force</b> option to shutdown virtual machines which cannot be migrated in live mode.
|
||||
force: Force
|
||||
cordon:
|
||||
title: Cordon
|
||||
@ -360,15 +376,15 @@ harvester:
|
||||
run: Run Strategy
|
||||
configure: Configure
|
||||
mode: Mode
|
||||
modeLink: Mode <a href="https://docs.harvesterhci.io/v1.1/host/#ksmtuned-mode" target="_blank"><i class="icon icon-info" /></a>
|
||||
modeLink: Mode <a href="{url}" target="_blank"><i class="icon icon-info" /></a>
|
||||
thresCoef: Threshold Coefficient
|
||||
enableMergeNodes: Enable Merge Across Nodes
|
||||
enableMergeNodes: Enable merging across nodes
|
||||
enable: Enable
|
||||
disable: Disable
|
||||
ksmStatus: KSM Status
|
||||
modeOption:
|
||||
standard: Standard
|
||||
high: High-Perfomanace
|
||||
high: High-performance
|
||||
customized: Customized
|
||||
parameters:
|
||||
title: Ksmtuned Parameters
|
||||
@ -400,11 +416,12 @@ harvester:
|
||||
label: Storage Reserved
|
||||
allowScheduling:
|
||||
label: Scheduling
|
||||
tooltip: Disk {name} ({path}) scheduling is disabled
|
||||
evictionRequested:
|
||||
label: Eviction Requested
|
||||
forceFormatted:
|
||||
label: Force Formatted
|
||||
toolTip: Force formatted will cleanup disk data, make sure you backup all available data to prevent data loss.
|
||||
toolTip: Force formatted will clean up disk data, make sure you backup all available data to prevent data loss.
|
||||
yes: Yes (Ext4 File System)
|
||||
description:
|
||||
label: Description
|
||||
@ -437,10 +454,8 @@ harvester:
|
||||
tips: You can configure multiple IPv4 addresses or host addresses.
|
||||
placeholder: e.g. IPv4
|
||||
ntpSyncStatus:
|
||||
isDisabled: 'The NTP is disabled. Please check the NTP service is active.'
|
||||
isUnsynced: 'The NTP is not synced with the NTP server <code>{current}</code>'
|
||||
ntpConfigSyncStatus:
|
||||
isUnsynced: 'The NTP config is not synced. Current: <code>{current}</code>, Wanted: <code>{wanted}</code> '
|
||||
isDisabled: 'NTP is disabled. Please check the NTP service is active.'
|
||||
isUnsynced: 'NTP is not synchronized with the NTP server {current}.'
|
||||
|
||||
virtualMachine:
|
||||
label: Virtual Machines
|
||||
@ -453,7 +468,7 @@ harvester:
|
||||
nameLabel: Name
|
||||
host:
|
||||
label: Hostname
|
||||
placeholder: default to the virtual machine name.
|
||||
placeholder: Default to the virtual machine name.
|
||||
multiple:
|
||||
label: Multiple Instance
|
||||
nameNsDescription: Name prefix for each instance
|
||||
@ -462,20 +477,20 @@ harvester:
|
||||
nameLabel: Name Prefix
|
||||
host:
|
||||
label: Host Prefix Name
|
||||
placeholder: default to the virtual machine name.
|
||||
placeholder: Default to the virtual machine name.
|
||||
useTemplate:
|
||||
label: "Use VM Template:"
|
||||
label: "Use the virtual machine template:"
|
||||
template:
|
||||
label: Template
|
||||
version:
|
||||
label: Version
|
||||
console:
|
||||
novnc: Open in Web VNC
|
||||
novnc: Open in WebVNC
|
||||
serial: Open in Serial Console
|
||||
promptRemove:
|
||||
title: 'Select the volume you want to delete:'
|
||||
deleteAll: Delete All
|
||||
tips: "Warn: The snapshots of vm will be deleted with VM and the snapshots of volume will be deleted with volume."
|
||||
tips: "Warn: The snapshots of the virtual machine will be deleted with virtual machine and the snapshots of volume will be deleted with volume."
|
||||
unplug:
|
||||
title: 'Are you sure that you want to detach volume {name} ?'
|
||||
actionLabel: Detach
|
||||
@ -487,6 +502,17 @@ harvester:
|
||||
other {Start}
|
||||
} the virtual machine now to take effect of the configuration changes.
|
||||
runStrategy: Run Strategy
|
||||
maintenanceStrategy:
|
||||
label: Maintenance Strategy
|
||||
options:
|
||||
Migrate: Migrate
|
||||
ShutdownAndRestartAfterEnable: Shutdown and Restart After Enable
|
||||
ShutdownAndRestartAfterDisable: Shutdown and Restart After Disable
|
||||
Shutdown: Shutdown
|
||||
cpuPinning:
|
||||
label: Enable CPU Pinning
|
||||
tooltip: Enable CPU Pinning brings better performance and reduce latency for the virtual machine
|
||||
restartVMMessage: Changing the CPU Pinning setting requires a virtual machine reboot for the change to take effect
|
||||
restartNow: |-
|
||||
{restart, select,
|
||||
true {Restart}
|
||||
@ -497,6 +523,10 @@ harvester:
|
||||
enableUsb: Enable USB Tablet
|
||||
advancedOptions:
|
||||
tpm: Enable TPM
|
||||
cpuManager:
|
||||
prefix: You must enable CPU Manager for at least one node in
|
||||
middle: 'host page'
|
||||
suffix: to enable CPU Pinning for VM
|
||||
usbTip: Provides an absolute pointer device which often helps with getting a consistent mouse cursor position in VNC.
|
||||
sshTitle: Add Public SSH Key
|
||||
imageTip: An external URL to the .iso, .img, .qcow2 or .raw that the virtual machine should be created from.
|
||||
@ -504,7 +534,7 @@ harvester:
|
||||
secureBoot: Secure Boot
|
||||
volume:
|
||||
dragTip: Drag and drop volumes, or use the volume's arrows, to change the boot order.
|
||||
volumeTip: The VM only contains a cd-rom volume. You may want to add additional disk volumes.
|
||||
volumeTip: The virtual machine only contains a CD-ROM volume. You may want to add additional disk volumes.
|
||||
macTip: "MAC address as seen inside the guest system."
|
||||
volumeUpdate: 'Set volume { name } successfully'
|
||||
type: Type
|
||||
@ -516,7 +546,7 @@ harvester:
|
||||
dockerImage: Docker Image
|
||||
addVolume: Add Volume
|
||||
addExistingVolume: Add Existing Volume
|
||||
addVmImage: Add VM Image
|
||||
addVmImage: Add a Virtual Machine Image
|
||||
addContainer: Add Container
|
||||
setFirst: Set as root volume
|
||||
saveVolume: Update Volume
|
||||
@ -546,23 +576,23 @@ harvester:
|
||||
network:
|
||||
label: Network Data Template
|
||||
title: "Network Data:"
|
||||
tip: "The network-data configuration allows you to customize the instance’s networking interfaces by assigning subnet configuration, virtual device creation (bonds, bridges, vlans) routes and DNS configuration. <a href='https://cloudinit.readthedocs.io/en/latest/reference/network-config-format-v1.html' target='_blank'>Learn more</a>"
|
||||
tip: "The network-data configuration allows you to customize the instance’s networking interfaces by assigning subnet configuration, virtual device creation (bonds, bridges, VLANs) routes and DNS configuration. <a href='https://cloudinit.readthedocs.io/en/latest/reference/network-config-format-v1.html' target='_blank'>Learn more</a>"
|
||||
scheduling:
|
||||
affinity:
|
||||
anyNode: 'Run VM on any available node'
|
||||
schedulingRules: 'Run VM on node(s) matching scheduling rules'
|
||||
specificNode: Run VM on specific node - (Live migration is not supported)
|
||||
anyNode: 'Run virtual machine on any available node'
|
||||
schedulingRules: 'Run virtual machine on node(s) matching scheduling rules'
|
||||
specificNode: Run virtual machine on specific node - (Live migration is not supported)
|
||||
networkNotSupport: Network not support schedule
|
||||
accessCredentials:
|
||||
resetPwd:
|
||||
label: Add Basic Auth
|
||||
injectSSH:
|
||||
label: Add SSHKey
|
||||
label: Add SSH Key
|
||||
users: Select Users
|
||||
addUser: Add User
|
||||
tips: qemu-guest-agent must be installed to enable Access Credentials, the VM should be restarted after credentials added. Need to enter the VM to edit password or remove SSH-Key after deleting the credentials.
|
||||
tips: qemu-guest-agent must be installed to enable the accessing of credentials. The virtual machine needs to be restarted after credentials added. You need to be in the virtual machine to edit your password or remove an SSH-Key after deleting the credentials.
|
||||
userTips: The user to be added must already exist; otherwise, the credentials will not take effect.
|
||||
duplicatedUser: User already exist.
|
||||
duplicatedUser: User already exists.
|
||||
invalidUser: Invalid Username.
|
||||
input:
|
||||
name: Name
|
||||
@ -594,7 +624,7 @@ harvester:
|
||||
monitor: Monitor Data
|
||||
keypairs: SSH Keys
|
||||
cloudConfig: Cloud Config
|
||||
metrics: VM Metrics
|
||||
metrics: Virtual Machine Metrics
|
||||
details:
|
||||
title:
|
||||
vmDetails: Virtual Machine Details
|
||||
@ -602,6 +632,7 @@ harvester:
|
||||
services: Services
|
||||
users: Logged in users
|
||||
name: Name
|
||||
totalSnapshotQuota: Total Snapshot Quota
|
||||
namespace: Namespace
|
||||
created: Created
|
||||
hostname: Hostname
|
||||
@ -623,7 +654,7 @@ harvester:
|
||||
flavor: Flavor
|
||||
tolerations: Tolerations
|
||||
dedicatedResources: Dedicated Resources
|
||||
down: VM not running
|
||||
down: Virtual machine not running
|
||||
affinityRules: Affinity Rules
|
||||
sourceNode: Source Node
|
||||
targetNode: Target Node
|
||||
@ -636,7 +667,7 @@ harvester:
|
||||
from: Generated from
|
||||
down: No events in the past hour
|
||||
console:
|
||||
down: This Virtual Machine is down. Please start it to access its console.
|
||||
down: This virtual machine is down. Please start it to access its console.
|
||||
shortcutKeys: Shortcut Keys
|
||||
customShortcutKeys: Custom Shortcut Keys
|
||||
management: Management Shortcut Keys
|
||||
@ -644,13 +675,13 @@ harvester:
|
||||
start: Record
|
||||
recording: Recording
|
||||
stop: Stop Recording
|
||||
tips: Pressing the record button will capture your keyboard inputs.
|
||||
tips: Press the record button to capture your keyboard inputs.
|
||||
send: Send
|
||||
preferredKeys: Preferred Custom Shortcut Keys
|
||||
terminationGracePeriodSeconds:
|
||||
label: Termination Grace Period
|
||||
affinity:
|
||||
thisPodNamespace: This VM's namespace
|
||||
thisPodNamespace: This virtual machine's namespace
|
||||
matchExpressions:
|
||||
inNamespaces: "Workloads in these namespaces"
|
||||
namespaces:
|
||||
@ -668,7 +699,7 @@ harvester:
|
||||
kind: Kind
|
||||
sourceOptions:
|
||||
new: New
|
||||
vmImage: VM Image
|
||||
vmImage: Virtual Machine Image
|
||||
image: Image
|
||||
frontend: Frontend
|
||||
blockdev: Block Device
|
||||
@ -680,7 +711,7 @@ harvester:
|
||||
lastBackupAt: Last Backup At
|
||||
replicasNumber: Replicas Number
|
||||
promptRemove:
|
||||
tips: "Warn: The snapshots of volume will be deleted with volume."
|
||||
tips: "Warn: The volume's snapshots will be deleted with this volume."
|
||||
externalLink:
|
||||
tips: Check volume details
|
||||
rebuildingMessage: 'Rebuilding: {percentage}%'
|
||||
@ -691,7 +722,8 @@ harvester:
|
||||
basics: Basics
|
||||
url: URL
|
||||
size: Size
|
||||
urlTip: 'supports the <code>raw</code> and <code>qcow2</code> image formats which are supported by <a href="https://www.qemu.org/docs/master/system/images.html#disk-image-file-formats" target="_blank">qemu</a>. Bootable ISO images can also be used and are treated like <code>raw</code> images.'
|
||||
virtualSize: Virtual Size
|
||||
urlTip: 'Supports the <code>raw</code> and <code>qcow2</code> image formats which are supported by <a href="https://www.qemu.org/docs/master/system/images.html#disk-image-file-formats" target="_blank">qemu</a>. Bootable ISO images can also be used and are treated like <code>raw</code> images.'
|
||||
fileName: File Name
|
||||
uploadFile: Upload File
|
||||
source: Source
|
||||
@ -716,10 +748,10 @@ harvester:
|
||||
tips:
|
||||
notExistImage:
|
||||
title: Image {name} does not exist!
|
||||
message: Please select a new Image.
|
||||
message: Please select a new image.
|
||||
notExistNode:
|
||||
title: Node {name} does not exist!
|
||||
message: Please select a new Node.
|
||||
message: Please select a new node.
|
||||
|
||||
upgradePage:
|
||||
upgradeApp: Upgrade Software
|
||||
@ -736,8 +768,8 @@ harvester:
|
||||
selectExisting: Select Existing Image
|
||||
createRepository: Creating Upgrade Repository
|
||||
succeeded: Succeeded
|
||||
releaseTip: Please read the upgrade documentation carefully. You can view details on the <a href="{url}" target="_blank">Harvester Release Note</a>.
|
||||
checkReady: I have read and understood the upgrade content related to this Harvester version.
|
||||
releaseTip: Please read the upgrade documentation carefully. You can view details on the <a href="{url}" target="_blank">Harvester Release Notes</a>.
|
||||
checkReady: I have read and understood the upgrade instructions related to this Harvester version.
|
||||
pending: Pending
|
||||
repoInfo:
|
||||
upgradeStatus: Upgrade Status
|
||||
@ -747,29 +779,29 @@ harvester:
|
||||
harvesterChart: Harvester Chart
|
||||
success: Success
|
||||
fail: Fail
|
||||
ongoing: on-going
|
||||
ongoing: On-going
|
||||
downloadLog: Download Log
|
||||
logStatus: Log Download Status
|
||||
dismissMessage: Dismiss it
|
||||
upgradeInfo:
|
||||
warning: WARNING
|
||||
doc: Before you upgrade to the newer Harvester version, you must perform the required <a href="https://docs.harvesterhci.io/v1.0/upgrade/automatic/" target="_blank"> pre-upgrade checks </a>for your cluster. Complete only those tasks that apply to your environment.
|
||||
tip: Failure to perform these checks may result in a failed upgrade or hitting known issues that require a manual workaround fix.
|
||||
doc: Read the <a href="{url}" target="_blank">documentation</a> before starting the upgrade process. Ensure that you complete procedures that are relevant to your environment and the version you are upgrading to.
|
||||
tip: Unmet system requirements and incorrectly performed procedures may cause complete upgrade failure and other issues that require manual workarounds.
|
||||
moreNotes: For more details about the release notes, please visit -
|
||||
|
||||
backup:
|
||||
label: VM Backups
|
||||
label: Virtual Machine Backups
|
||||
createText: Restore Backup
|
||||
title: Restore Virtual Machine
|
||||
backupTargetTip: The endpoint used to access the backupstore. NFS and S3 are supported.
|
||||
message:
|
||||
noSetting:
|
||||
prefix: You must configure the backup target in
|
||||
prefix: You must configure the backup target
|
||||
middle: 'setting'
|
||||
suffix: before creating a new backup.
|
||||
errorTip:
|
||||
prefix: Backup Target value in
|
||||
middle: Setting
|
||||
prefix: Backup target value in
|
||||
middle: setting
|
||||
suffix: "is invalid, error: "
|
||||
viewSetting:
|
||||
prefix: Click
|
||||
@ -804,21 +836,21 @@ harvester:
|
||||
complete: Restore completed
|
||||
|
||||
network:
|
||||
label: VM Networks
|
||||
label: Virtual Machine Networks
|
||||
tabs:
|
||||
basics: Basics
|
||||
layer3Network: Route
|
||||
clusterNetwork:
|
||||
label: Cluster Network
|
||||
create: Create a New Cluster Network
|
||||
create: Create a new cluster network
|
||||
toolTip: Define your custom cluster scope network name
|
||||
createPlaceholder: Input a new Cluster Network name
|
||||
selectOrCreatePlaceholder: Select or Create a new Cluster Network
|
||||
selectPlaceholder: Select a Cluster Network
|
||||
createPlaceholder: Input a new cluster network name
|
||||
selectOrCreatePlaceholder: Select or create a new cluster network
|
||||
selectPlaceholder: Select a cluster network
|
||||
layer3Network:
|
||||
mode:
|
||||
label: Mode
|
||||
auto: Auto(DHCP)
|
||||
auto: Auto (DHCP)
|
||||
manual: Manual
|
||||
serverIPAddr:
|
||||
label: DHCP Server IP
|
||||
@ -842,9 +874,9 @@ harvester:
|
||||
validation:
|
||||
physicalNIC: DefaultPhysicalNIC
|
||||
placeholder:
|
||||
accessKeyId: specify your access key id
|
||||
secretAccessKey: specify your secret access key
|
||||
cert: upload a self-signed SSL certificate
|
||||
accessKeyId: Specify your access key ID
|
||||
secretAccessKey: Specify your secret access key
|
||||
cert: Upload a self-signed SSL certificate
|
||||
vlanChangeTip: The newly modified default network interface only applies to newly added nodes, not existing ones.
|
||||
defaultPhysicalNIC: Default Network Interface
|
||||
percentTip: The value in parentheses represents the distribution percentage of the network interface on all hosts. If an interface less than 100% is selected, the user needs to manually specify the network interface on the host where the vlan network configuration fails.
|
||||
@ -869,10 +901,11 @@ harvester:
|
||||
placeholder: e.g. 172.16.0.1/32
|
||||
invalid: '"Exclude list" is invalid.'
|
||||
addIp: Add Exclude IP
|
||||
warning: 'WARNING: <br/> Any change to storage-network requires shutting down all VMs before applying this setting. <br/> Users have to ensure the Cluster Network is configured and VLAN Config will cover all nodes and ensure the network connectivity is working and expected in all nodes.'
|
||||
tip: 'IP Range should be in IPV4 format. <code>Number of IPs Required = Number of Nodes * 4 + Number of Disks * 2 + Number of Images to Download/Upload </code>. See <a href="https://docs.harvesterhci.io/v1.2/advanced/storagenetwork#configuration-example" target="_blank">doc</a> for more details.'
|
||||
warning: 'WARNING: <br/> Any change to storage-network requires shutting down all virtual machines before applying this setting. <br/> Users have to ensure the cluster network is configured and VLAN Configuration will cover all nodes and ensure the network connectivity is working and expected in all nodes.'
|
||||
tip: 'Specify an IP range in the IPv4 CIDR format. <code>Number of IPs Required = Number of Nodes * 4 + Number of Disks * 2 + Number of Images to Download/Upload </code>. For more information about storage network settings, see the <a href="{url}" target="_blank">documentation</a>.'
|
||||
vmForceDeletionPolicy:
|
||||
period: Period
|
||||
ratio : Ratio
|
||||
autoRotateRKE2Certs:
|
||||
expiringInHours: Expiring in
|
||||
httpProxy:
|
||||
@ -907,7 +940,7 @@ harvester:
|
||||
|
||||
upgrade:
|
||||
selectExitImage: Please select the OS image to upgrade.
|
||||
imageUrl: Please input a valid image url.
|
||||
imageUrl: Please input a valid image URL.
|
||||
chooseFile: Please select to upload an image.
|
||||
checksum: Checksum
|
||||
harvesterMonitoring:
|
||||
@ -923,12 +956,12 @@ harvester:
|
||||
retention: How long to retain metrics
|
||||
retentionSize: Maximum size of metrics
|
||||
clusterRegistrationUrl:
|
||||
message: To completely unset the imported Harvester cluster, please also remove it on the Rancher dashboard UI via the <code> Virtualization Management </code> page.
|
||||
message: To completely unset the imported Harvester cluster, please also remove it on the Rancher Dashboard UI via the <code> Virtualization Management </code> page.
|
||||
ntpServers:
|
||||
isNotIPV4: The address you entered is not IPv4 or host. Please enter a valid IPv4 address or a host address.
|
||||
isDuplicate: There are duplicate NTP server configurations.
|
||||
cloudTemplate:
|
||||
label: Cloud Config Templates
|
||||
label: Cloud Configuration Templates
|
||||
templateType: Template Type
|
||||
userData: User Data
|
||||
networkData: Network Data
|
||||
@ -941,7 +974,7 @@ harvester:
|
||||
internal:
|
||||
rancher:
|
||||
title: Access Embedded Rancher UI
|
||||
titleDescription: We only support to use the embedded Rancher dashboard for debugging and validation purpose. For Rancher's multi-cluster and multi-tenant integration, please refer to the docs <a target="_blank" href="https://docs.harvesterhci.io/v1.2/rancher/index" rel="noopener noreferrer nofollow">here</a>.
|
||||
titleDescription: You can only use the embedded Rancher UI for debugging and validation purposes. For more information about how Harvester integrates with Rancher, see the <a target="_blank" href="{url}" rel="noopener noreferrer nofollow">documentation</a>.
|
||||
longhorn:
|
||||
title: Access Embedded Longhorn UI
|
||||
titleDescription: We only support to use the embedded Longhorn UI for debugging and validation purpose.
|
||||
@ -961,7 +994,7 @@ harvester:
|
||||
cidr:
|
||||
label: CIDR/IP Range
|
||||
invalid: '"CIDR/IP Range" is invalid.'
|
||||
toolTip: "We can apply multiple pools or ranges by seperating them with commas. i.e. 192.168.0.200/30,192.168.0.200/29 or 192.168.0.10-192.168.0.11"
|
||||
toolTip: "We can apply multiple pools or ranges by separating them with commas. For example: 192.168.0.200/30,192.168.0.200/29 or 192.168.0.10-192.168.0.11"
|
||||
add:
|
||||
label: Add IP Pools
|
||||
|
||||
@ -977,15 +1010,15 @@ harvester:
|
||||
alertmanagerConfig:
|
||||
label: Alertmanager Configs
|
||||
diabledMonitoringTips:
|
||||
prefix: 'You must enable'
|
||||
middle: 'Monitoring'
|
||||
suffix: 'addon at first.'
|
||||
prefix: 'Enable the'
|
||||
middle: 'monitoring'
|
||||
suffix: 'add-on first.'
|
||||
diabledAlertingTips:
|
||||
prefix: 'You must enable'
|
||||
prefix: 'Enable'
|
||||
middle: 'Alertmanager'
|
||||
suffix: 'for configs to take effect.'
|
||||
suffix: 'for configuration to take effect.'
|
||||
disabledAddon:
|
||||
prefix: 'Monitoring Addon is disabled now, click'
|
||||
prefix: 'The monitoring add-on is disabled, click'
|
||||
middle: 'here'
|
||||
suffix: 'to enable it.'
|
||||
|
||||
@ -1003,18 +1036,19 @@ harvester:
|
||||
output:
|
||||
label: Output
|
||||
diabledTips:
|
||||
prefix: 'You must enable'
|
||||
middle: 'Logging'
|
||||
suffix: 'for configs to take effect.'
|
||||
prefix: 'Enable'
|
||||
middle: 'logging'
|
||||
suffix: 'for configuration to take effect.'
|
||||
|
||||
snapshot:
|
||||
totalSnapshotSize: Total Snapshot Size
|
||||
label: Volume Snapshots
|
||||
targetVolume: Original Volume
|
||||
size: Size
|
||||
image: Image
|
||||
|
||||
vmSnapshot:
|
||||
label: VM Snapshots
|
||||
label: Virtual Machine Snapshots
|
||||
createText: Restore Snapshot
|
||||
snapshot: Snapshot
|
||||
|
||||
@ -1036,7 +1070,7 @@ harvester:
|
||||
title: Storage Classes
|
||||
customize:
|
||||
volumeBindingMode:
|
||||
later: Bind and provision a persistent volume once a VM using the PersistentVolumeClaim is created
|
||||
later: Bind and provision a persistent volume once a virtual machine using the PersistentVolumeClaim is created
|
||||
parameters:
|
||||
numberOfReplicas:
|
||||
label: Number Of Replicas
|
||||
@ -1052,11 +1086,11 @@ harvester:
|
||||
label: Migratable
|
||||
allowedTopologies:
|
||||
title: Allowed Topologies
|
||||
tooltip: Allowed Topologies helps scheduling VMs on hosts which match all of below expressions.
|
||||
tooltip: Allowed Topologies helps scheduling virtual machines on hosts which match all of below expressions.
|
||||
|
||||
vlanConfig:
|
||||
title: Network Configs
|
||||
createNetworkConfig: Create Network Config
|
||||
title: Network Configuration
|
||||
createNetworkConfig: Create Network Configuration
|
||||
action:
|
||||
migrate: Migrate
|
||||
titles:
|
||||
@ -1097,39 +1131,39 @@ harvester:
|
||||
|
||||
vlanStatus:
|
||||
vlanConfig:
|
||||
label: Network Config
|
||||
label: Network Configuration
|
||||
|
||||
clusterNetwork:
|
||||
title: Cluster Networks/Configs
|
||||
title: Cluster Network Configuration
|
||||
create:
|
||||
button:
|
||||
label: Create Cluster Network
|
||||
clusterNetwork: There are no network configs defined.
|
||||
label: Create a Cluster Network
|
||||
clusterNetwork: There are no network configurations defined.
|
||||
mgmt: mgmt is a built-in cluster management network and does not support any additional network configurations.
|
||||
notExist: 'Cluster Network "{ clusterNetwork }" does not exist'
|
||||
notReady: 'Cluster Network "{ clusterNetwork }" is not ready'
|
||||
|
||||
addons:
|
||||
descriptions:
|
||||
'harvester-system/vm-import-controller': vm-import-controller is an addon to help migrate VM workloads from other source clusters to an existing Harvester cluster.
|
||||
'harvester-system/pcidevices-controller': pcidevices-controller is an addon to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester VM and guest Clusters.
|
||||
'cattle-logging-system/rancher-logging': rancher-logging is an addon to collect versatile logs, events and audits from the Harvester cluster and route them to many kinds of servers based on flows.
|
||||
'harvester-system/rancher-vcluster': rancher-vcluster deploys a vcluster with rancher installed.
|
||||
'cattle-monitoring-system/rancher-monitoring': rancher-monitoring is an addon to collect Harvester cluster and VM metrics, view them on the embedded dashboard, and send alert(s) to remote servers.
|
||||
'vm-import-controller': vm-import-controller is an addon to help migrate VM workloads from other source clusters to an existing Harvester cluster.
|
||||
'pcidevices-controller': pcidevices-controller is an addon to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester VM and guest Clusters.
|
||||
'nvidia-driver-toolkit': 'nvidia-driver-toolkit is an addon to enable vGPU devices and assign them to Harvester VMs.'
|
||||
'rancher-logging': rancher-logging is an addon to collect versatile logs, events and audits from the Harvester cluster and route them to many kinds of servers based on flows.
|
||||
'rancher-monitoring': rancher-monitoring is an addon to collect Harvester cluster and VM metrics, view them on the embedded dashboard, and send alert(s) to remote servers.
|
||||
'rancher-vcluster': rancher-vcluster deploys a vcluster with rancher installed.
|
||||
'harvester-seeder': harvester-seeder is an addon that uses ipmi and redfish to discover hardware information and perform out-of-band operations.
|
||||
'harvester-system/harvester-seeder': harvester-seeder is an addon that uses ipmi and redfish to discover hardware information and perform out-of-band operations.
|
||||
'harvester-system/vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing Harvester cluster.
|
||||
'harvester-system/pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machine and guest clusters.
|
||||
'cattle-logging-system/rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows.
|
||||
'harvester-system/rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed.
|
||||
'cattle-monitoring-system/rancher-monitoring': rancher-monitoring is an add-on that collects Harvester cluster and virtual machine metrics and allows you to view the metrics on an embedded dashboard and send alert(s) to remote servers.
|
||||
'vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing Harvester cluster.
|
||||
'pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machines and guest clusters.
|
||||
'nvidia-driver-toolkit': 'nvidia-driver-toolkit is an add-on to enable vGPU devices and assign them to Harvester virtual machines.'
|
||||
'rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows.
|
||||
'rancher-monitoring': rancher-monitoring is an add-on to collect Harvester cluster and virtual machine metrics, view them on the embedded dashboard, and send alert(s) to remote servers.
|
||||
'rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed.
|
||||
'harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations.
|
||||
'harvester-system/harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations.
|
||||
vmImport:
|
||||
titles:
|
||||
basic: Basic
|
||||
pvc: Volume
|
||||
rancherVcluster:
|
||||
accessRancher: Access Rancher Dashboard
|
||||
accessRancher: Access the Rancher Dashboard
|
||||
hostname: Hostname
|
||||
rancherVersion: Rancher Version
|
||||
password: Bootstrap Password
|
||||
@ -1190,12 +1224,12 @@ harvester:
|
||||
label: Backend Servers
|
||||
healthCheck:
|
||||
warning:
|
||||
portInUse: Warning, Backend Port {port} is in use in Health Check settings; in case of updating the port, update the Health Check settings accordingly.
|
||||
portInUse: Warning, the Backend Port {port} is in use in Health Check settings. If you need to update the port, update the Health Check settings accordingly.
|
||||
|
||||
ipPool:
|
||||
label: IP Pools
|
||||
network:
|
||||
label: VM Network
|
||||
label: Virtual Machine Network
|
||||
tabs:
|
||||
range: Range
|
||||
scope: Scope
|
||||
@ -1231,7 +1265,7 @@ harvester:
|
||||
label: Health Check Port
|
||||
healthCheckSuccessThreshold:
|
||||
label: Health Check Success Threshold
|
||||
description: If the number of times the prober continuously detects an address successfully reaches the success threshold, then the backend server can start to forward traffic.
|
||||
description: If the number of times the probe continuously detects an address successfully reaches the success threshold, then the backend server can start to forward traffic.
|
||||
healthCheckFailureThreshold:
|
||||
label: Health Check Failure Threshold
|
||||
description: The backend server will stop forwarding traffic if the number of health check failures reaches the failure threshold.
|
||||
@ -1256,22 +1290,22 @@ harvester:
|
||||
sriovgpu:
|
||||
label: SR-IOV GPU Devices
|
||||
nodeName: Node
|
||||
numVFs: Number Of Virtual Functions
|
||||
numVFs: Number of Virtual Functions
|
||||
vfAddresses: Virtual Functions Addresses
|
||||
vGpuDevices: vGPU Devices
|
||||
showMore: Show More
|
||||
parentSriov: Filter By Parent SR-IOV GPU
|
||||
noPermission: Please contact system admin to add Harvester addons first
|
||||
noPermission: Please contact your system admiistrator to add Harvester add-ons first.
|
||||
goSetting:
|
||||
prefix: The nvidia-driver-toolkit addon is not enabled, click
|
||||
prefix: The nvidia-driver-toolkit add-on is not enabled, click
|
||||
middle: here
|
||||
suffix: to enable it to manage your SR-IOV GPU devices.
|
||||
|
||||
vgpu:
|
||||
label: vGPU Devices
|
||||
noPermission: Please contact system admin to add Harvester addons first
|
||||
noPermission: Please contact system administrator to add Harvester add-ons first.
|
||||
goSetting:
|
||||
prefix: The nvidia-driver-toolkit addon is not enabled, click
|
||||
prefix: The nvidia-driver-toolkit add-on is not enabled, click
|
||||
middle: here
|
||||
suffix: to enable it to manage your vGPU devices.
|
||||
enableGroup: Enable Group
|
||||
@ -1282,7 +1316,7 @@ harvester:
|
||||
available: Available Devices
|
||||
compatibleNodes: Compatible Nodes
|
||||
impossibleSelection: 'There are no hosts with all of the selected devices.'
|
||||
howToUseDevice: 'Use the table below to enable vGPU devices you want to use in this VM.'
|
||||
howToUseDevice: 'Use the table below to enable vGPU devices you want to use in this virtual machine.'
|
||||
deviceInTheSameHost: 'You can only select devices on the same host.'
|
||||
|
||||
harvesterVlanConfigMigrateDialog:
|
||||
@ -1293,11 +1327,11 @@ harvester:
|
||||
seeder:
|
||||
banner:
|
||||
enable:
|
||||
prefix: Addon "harvester-seeder" is disabled now,
|
||||
middle: click here
|
||||
prefix: The "harvester-seeder" add-on is disabled.
|
||||
middle: Click here
|
||||
suffix: to enable it.
|
||||
noAccess: Please contact system admin to enable the Out-of-Band Access first.
|
||||
noAddon: Addon "harvester-seeder" is not exist, please check if it is installed.
|
||||
noAccess: Please contact your system administrator to enable the Out-of-Band Access first.
|
||||
noAddon: The "harvester-seeder" add-on does not exist, please check if it is installed.
|
||||
noInventory: Waiting for "inventories.metal.harvesterhci.io" to be ready.
|
||||
inventory:
|
||||
host:
|
||||
@ -1318,10 +1352,10 @@ harvester:
|
||||
label: Polling Interval
|
||||
|
||||
affinity:
|
||||
thisPodNamespace: This VM's namespace
|
||||
thisPodNamespace: This virtual machine's namespace
|
||||
matchExpressions:
|
||||
inNamespaces: "Workloads in these namespaces"
|
||||
vmAffinityTitle: VM Scheduling
|
||||
vmAffinityTitle: Virtual Machine Scheduling
|
||||
namespaces:
|
||||
placeholder: e.g. default,system,base
|
||||
label: Namespaces
|
||||
@ -1330,35 +1364,40 @@ harvester:
|
||||
placeholder: 'topology.kubernetes.io/zone'
|
||||
|
||||
advancedSettings:
|
||||
technicalPreview: 'Technical Previews allow users to test and evaluate early-access functionality prior to official supported releases'
|
||||
descriptions:
|
||||
'harv-vlan': Default Network Interface name of the VLAN network.
|
||||
'harv-backup-target': Custom backup target to store VM backups.
|
||||
'branding': Branding allows administrators to globally re-brand the UI by customizing the Harvester product name, logos and color scheme.
|
||||
'harv-csi-driver-config': Configure additional information for csi drivers.
|
||||
'harv-backup-target': Custom backup target to store virtual machine backups.
|
||||
'branding': Branding allows administrators to globally re-brand the UI by customizing the Harvester product name, logos, and color scheme.
|
||||
'harv-csi-driver-config': Configure additional information for CSI drivers.
|
||||
'harv-containerd-registry': Containerd Registry Configuration to connect private registries.
|
||||
'harv-log-level': Configure Harvester server log level. Default to info.
|
||||
'harv-log-level': Configure Harvester server log level. Defaults to Info.
|
||||
'harv-server-version': Harvester server version.
|
||||
'harv-upgrade-checker-enabled': Specify whether to enable Harvester upgrade check or not. Default is true.
|
||||
'harv-upgrade-checker-url': Default Harvester upgrade check url. Only used when the <code>upgrade-checker-enabled</code> is equal to true.
|
||||
'harv-ui-source': Config how to load the UI source.
|
||||
'harv-upgrade-checker-enabled': Specifies whether to enable Harvester upgrade check or not. Default is True.
|
||||
'harv-upgrade-checker-url': Default Harvester upgrade check url. Only used when the <code>upgrade-checker-enabled</code> is equal to True.
|
||||
'harv-ui-source': Configure how to load the UI source.
|
||||
'harv-ui-index': 'HTML index location for the UI.'
|
||||
'harv-ui-plugin-index': 'JS index location for the harvester plugin UI.'
|
||||
'harv-cluster-registration-url': Registration URL for mutil-cluster management.
|
||||
'harv-ui-plugin-index': 'JS index location for the Harvester plugin UI.'
|
||||
'harv-cluster-registration-url': Registration URL for multi-cluster management.
|
||||
'harv-http-proxy': 'HTTP proxy for Harvester to access external services.'
|
||||
'harv-additional-ca': 'Custom CA root certificates for TLS validation.'
|
||||
'harv-overcommit-config': 'Resource overcommit configuration.'
|
||||
'harv-support-bundle-timeout': 'Support Bundle timeout config in minutes, use 0 to disable the timeout.'
|
||||
'harv-support-bundle-expiration': 'Support Bundle expiration config in minutes.'
|
||||
'harv-vm-force-reset-policy': Config the force-reset action when a VM is stuck on a node that is down.
|
||||
'harv-support-bundle-timeout': 'Support bundle timeout configuration in minutes, use 0 to disable the timeout.'
|
||||
'harv-support-bundle-expiration': 'Support bundle expiration configuration in minutes.'
|
||||
'harv-support-bundle-node-collection-timeout': 'Support bundle node collection timeout configuration in minutes.'
|
||||
'harv-vm-force-reset-policy': Configuration for the force-reset action when a virtual machine is stuck on a node that is down.
|
||||
'harv-ssl-parameters': Custom SSL Parameters for TLS validation.
|
||||
'harv-storage-network': 'Longhorn storage-network setting.'
|
||||
'harv-support-bundle-namespaces': Specify resources in other namespaces to be collected by the support package.
|
||||
'harv-auto-disk-provision-paths': Specify the disks(using glob pattern) that Harvester will automatically add as VM storage.
|
||||
'harv-auto-disk-provision-paths': Specify the disks(using glob pattern) that Harvester will automatically add as virtual machine storage.
|
||||
'harv-support-bundle-image': Support bundle image configuration. Find different versions in <a href="https://hub.docker.com/r/rancher/support-bundle-kit/tags" target="_blank">rancher/support-bundle-kit</a>.
|
||||
'harv-release-download-url': This setting allows you to configure the <code>upgrade release download</code> URL address. Harvester will get the ISO URL and checksum value from the (<code>$URL</code>/<code>$VERSION</code>/version.yaml) file hosted by the configured URL.
|
||||
'harv-default-vm-termination-grace-period-seconds': Config the VM termination grace period for VM stop.
|
||||
'harv-default-vm-termination-grace-period-seconds': Configure the virtual machine termination grace period for virtual machine stop.
|
||||
'harv-ntp-servers': Configure NTP server. You can configure multiple IPv4 addresses or host addresses.
|
||||
'harv-auto-rotate-rke2-certs': The certificate rotation mechanism relies on Rancher. Harvester will automatically update certificates generation to trigger rotation.
|
||||
'harv-kubeconfig-default-token-ttl-minutes': 'TTL (in minutes) applied on Harvester administration kubeconfig files. Default is 0, which means to never expire.'
|
||||
'harv-longhorn-v2-data-engine-enabled': 'Enable the Longhorn V2 data engine. Default is false. <ul><li>Changing this setting will restart RKE2 on all nodes. This will not affect running VM workloads.</li><li>If you see "not enough hugepages-2Mi capacity" errors when enabling this setting, wait a minute for the error to clear. If the error remains, reboot the affected node.</li></ul>'
|
||||
'harv-additional-guest-memory-overhead-ratio': 'The ratio for kubevirt to adjust the VM overhead memory. The value could be zero, empty value or floating number between 1.0 and 10.0, default to 1.5.'
|
||||
|
||||
typeLabel:
|
||||
kubevirt.io.virtualmachine: |-
|
||||
@ -1383,8 +1422,8 @@ typeLabel:
|
||||
}
|
||||
harvesterhci.io.networkattachmentdefinition: |-
|
||||
{count, plural,
|
||||
one { VM Network }
|
||||
other { VM Networks }
|
||||
one { Virtual Machines Network }
|
||||
other { Virtual Machines Networks }
|
||||
}
|
||||
harvesterhci.io.volume: |-
|
||||
{count, plural,
|
||||
@ -1408,13 +1447,13 @@ typeLabel:
|
||||
}
|
||||
harvesterhci.io.virtualmachinebackup: |-
|
||||
{count, plural,
|
||||
one { VM Backup }
|
||||
other { VM Backups }
|
||||
one { Virtual Machines Backup }
|
||||
other { Virtual Machines Backups }
|
||||
}
|
||||
harvesterhci.io.cloudtemplate: |-
|
||||
{count, plural,
|
||||
one { Cloud Config Template }
|
||||
other { Cloud Config Templates }
|
||||
one { Cloud Configuration Template }
|
||||
other { Cloud Configuration Templates }
|
||||
}
|
||||
harvesterhci.io.volumesnapshot: |-
|
||||
{count, plural,
|
||||
@ -1423,18 +1462,18 @@ typeLabel:
|
||||
}
|
||||
harvesterhci.io.vmsnapshot: |-
|
||||
{count, plural,
|
||||
one { VM Snapshot }
|
||||
other { VM Snapshots }
|
||||
one { Virtual Machines Snapshot }
|
||||
other { Virtual Machines Snapshots }
|
||||
}
|
||||
network.harvesterhci.io.vlanconfig: |-
|
||||
{count, plural,
|
||||
one { Network Config }
|
||||
other { Network Configs }
|
||||
one { Network Configuration }
|
||||
other { Network Configurations }
|
||||
}
|
||||
harvesterhci.io.monitoring.alertmanagerconfig: |-
|
||||
{count, plural,
|
||||
one { Alertmanager Config }
|
||||
other { Alertmanager Configs }
|
||||
one { Alertmanager Configuration }
|
||||
other { Alertmanager Configurations }
|
||||
}
|
||||
harvesterhci.io.logging.clusterflow: |-
|
||||
{count, plural,
|
||||
@ -1514,30 +1553,3 @@ typeLabel:
|
||||
one { IP Pool }
|
||||
other { IP Pools }
|
||||
}
|
||||
|
||||
harvesterManager:
|
||||
manage: Manage
|
||||
cluster:
|
||||
label: Harvester Clusters
|
||||
none: There are no Harvester Clusters
|
||||
learnMore: Learn more about Harvester from the <a target="_blank" href="https://harvesterhci.io/" rel="noopener noreferrer nofollow">Harvester Web Site</a> or read the the <a target="_blank" href="https://docs.harvesterhci.io/" rel="noopener noreferrer nofollow">Harvester Docs</a>
|
||||
description: Harvester is a modern Hyperconverged infrastructure (HCI) solution built for bare metal servers using enterprise-grade open source technologies including Kubernetes, Kubevirt and Longhorn.
|
||||
plugins:
|
||||
loadError: Error loading harvester plugin
|
||||
rke:
|
||||
templateError: Incorrect template format
|
||||
affinity:
|
||||
thisPodNamespace: This VM's namespace
|
||||
matchExpressions:
|
||||
inNamespaces: "Workloads in these namespaces"
|
||||
vmAffinityTitle: VM Scheduling
|
||||
namespaces:
|
||||
placeholder: e.g. default,system,base
|
||||
label: Namespaces
|
||||
addLabel: Add Workload Selector
|
||||
topologyKey:
|
||||
placeholder: 'topology.kubernetes.io/zone'
|
||||
vGpu:
|
||||
title: VGPUs
|
||||
label: VGPU type
|
||||
placeholder: 'Please select a VGPU'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -141,7 +141,8 @@ export default {
|
||||
settings: this.fetchClusterResources(HCI.SETTING),
|
||||
services: this.fetchClusterResources(SERVICE),
|
||||
metric: this.fetchClusterResources(METRIC.NODE),
|
||||
longhornNode: this.fetchClusterResources(LONGHORN.NODES) || [],
|
||||
longhornNodes: this.fetchClusterResources(LONGHORN.NODES),
|
||||
longhornSettings: this.fetchClusterResources(LONGHORN.SETTINGS),
|
||||
_pods: this.$store.dispatch('harvester/findAll', { type: POD }),
|
||||
};
|
||||
|
||||
@ -155,6 +156,10 @@ export default {
|
||||
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);
|
||||
|
||||
for ( const k in res ) {
|
||||
@ -226,6 +231,7 @@ export default {
|
||||
showClusterMetrics: false,
|
||||
showVmMetrics: false,
|
||||
enabledMonitoringAddon: false,
|
||||
hasLonghornSchema: false,
|
||||
};
|
||||
},
|
||||
|
||||
@ -305,8 +311,7 @@ export default {
|
||||
|
||||
currentVersion() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const settings = this.$store.getters[`${ inStore }/all`](HCI.SETTING);
|
||||
const setting = settings.find( S => S.id === 'server-version');
|
||||
const setting = this.$store.getters[`${ inStore }/byId`](HCI.SETTING, 'server-version');
|
||||
|
||||
return setting?.value || setting?.default;
|
||||
},
|
||||
@ -364,53 +369,46 @@ export default {
|
||||
return out;
|
||||
},
|
||||
|
||||
storageUsage() {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
const longhornNodes = this.$store.getters[`${ inStore }/all`](LONGHORN.NODES) || [];
|
||||
|
||||
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) => {
|
||||
storageStats() {
|
||||
const storageOverProvisioningPercentageSetting = this.longhornSettings.find(s => s.id === 'longhorn-system/storage-over-provisioning-percentage');
|
||||
const stats = this.longhornNodes.reduce((total, node) => {
|
||||
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 || {};
|
||||
|
||||
Object.values(diskStatus).map((disk) => {
|
||||
if (disk?.storageMaximum) {
|
||||
out += disk.storageMaximum;
|
||||
}
|
||||
total.used += node?.spec?.allowScheduling ? node.used : 0;
|
||||
|
||||
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 out;
|
||||
return total;
|
||||
}, {
|
||||
used: 0,
|
||||
scheduled: 0,
|
||||
maximum: 0,
|
||||
reserved: 0,
|
||||
total: 0
|
||||
});
|
||||
|
||||
stats.total = ((stats.maximum - stats.reserved) * Number(storageOverProvisioningPercentageSetting?.value ?? 0)) / 100;
|
||||
|
||||
return stats;
|
||||
},
|
||||
|
||||
storageUsed() {
|
||||
return this.createMemoryValues(this.storageTotal, this.storageUsage);
|
||||
const stats = this.storageStats;
|
||||
|
||||
return this.createMemoryValues(stats.maximum, stats.used);
|
||||
},
|
||||
|
||||
storageReserved() {
|
||||
return this.createMemoryValues(this.storageTotal, this.storageReservedTotal);
|
||||
storageAllocated() {
|
||||
const stats = this.storageStats;
|
||||
|
||||
return this.createMemoryValues(stats.total, stats.scheduled);
|
||||
},
|
||||
|
||||
vmEvents() {
|
||||
@ -644,7 +642,7 @@ export default {
|
||||
<div
|
||||
class="hardware-resource-gauges"
|
||||
:class="{
|
||||
live: !storageTotal,
|
||||
live: !hasLonghornSchema,
|
||||
}"
|
||||
>
|
||||
<HardwareResourceGauge
|
||||
@ -658,10 +656,11 @@ export default {
|
||||
:used="ramUsed"
|
||||
/>
|
||||
<HardwareResourceGauge
|
||||
v-if="storageTotal"
|
||||
v-if="hasLonghornSchema"
|
||||
:name="t('harvester.dashboard.hardwareResourceGauge.storage')"
|
||||
:used="storageUsed"
|
||||
:reserved="storageReserved"
|
||||
:reserved="storageAllocated"
|
||||
:reserved-title="t('clusterIndexPage.hardwareResourceGauge.allocated')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
import { allHash } from '@shell/utils/promise';
|
||||
import metricPoller from '@shell/mixins/metric-poller';
|
||||
import { HCI } from '../types';
|
||||
import { DOC_LINKS } from '../config/doc-links';
|
||||
|
||||
const schema = {
|
||||
id: HCI.HOST,
|
||||
@ -91,6 +92,7 @@ export default {
|
||||
value: 'internalIp',
|
||||
formatter: 'CopyToClipboard',
|
||||
sort: ['internalIp'],
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
@ -98,7 +100,7 @@ export default {
|
||||
const metricCol = [
|
||||
{
|
||||
name: 'cpu',
|
||||
labelKey: 'tableHeaders.cpu',
|
||||
labelKey: 'node.detail.glance.consumptionGauge.cpu',
|
||||
value: 'id',
|
||||
formatter: 'HarvesterCPUUsed',
|
||||
formatterOpts: { showUsed: true },
|
||||
@ -121,12 +123,23 @@ export default {
|
||||
labelKey: 'tableHeaders.storage',
|
||||
value: 'id',
|
||||
formatter: 'HarvesterStorageUsed',
|
||||
formatterOpts: { showReserved: true },
|
||||
formatterOpts: { showAllocated: true },
|
||||
};
|
||||
|
||||
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) {
|
||||
out.push({
|
||||
name: 'diskState',
|
||||
@ -143,7 +156,7 @@ export default {
|
||||
name: 'console',
|
||||
label: ' ',
|
||||
align: 'right',
|
||||
width: 65,
|
||||
width: 80,
|
||||
});
|
||||
|
||||
return out;
|
||||
@ -151,6 +164,10 @@ export default {
|
||||
|
||||
schema() {
|
||||
return schema;
|
||||
},
|
||||
|
||||
consoleDocLink() {
|
||||
return DOC_LINKS.CONSOLE_URL;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -169,7 +186,15 @@ export default {
|
||||
|
||||
goto(row) {
|
||||
window.open(row.consoleUrl, '_blank');
|
||||
},
|
||||
|
||||
consoleTooltip(row) {
|
||||
if (!row.consoleUrl) {
|
||||
return this.t('harvester.host.noConsoleUrl');
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
},
|
||||
|
||||
typeDisplay() {
|
||||
@ -199,10 +224,19 @@ export default {
|
||||
|
||||
>
|
||||
<template #cell:console="{row}">
|
||||
<button type="button" class="btn btn-sm role-primary" :disabled="!row.consoleUrl" @click="goto(row)">
|
||||
<div class="console-button">
|
||||
<button v-clean-tooltip="consoleTooltip(row)" type="button" class="mr-5 btn btn-sm role-primary" :disabled="!row.consoleUrl" @click="goto(row)">
|
||||
{{ t('harvester.host.console') }}
|
||||
</button>
|
||||
<a v-if="!row.consoleUrl" :href="consoleDocLink" target="_blank"><i class="icon icon-info" /></a>
|
||||
</div>
|
||||
</template>
|
||||
</ResourceTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.console-button {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -103,7 +103,6 @@ export default {
|
||||
:schema="schema"
|
||||
:rows="rows"
|
||||
key-field="_key"
|
||||
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -56,8 +56,6 @@ export default {
|
||||
this.$store.dispatch('type-map/configureType', { match: HCI.VOLUME, isCreatable: false });
|
||||
}
|
||||
|
||||
console.log('ppppppppppp', hash);
|
||||
|
||||
this.rows = hash.pvcs;
|
||||
},
|
||||
|
||||
@ -151,23 +149,23 @@ export default {
|
||||
|
||||
>
|
||||
<template
|
||||
cell:state="scope"
|
||||
>
|
||||
cell:state="scope"
|
||||
>
|
||||
<div class="state">
|
||||
<HarvesterVolumeState
|
||||
class="vmstate"
|
||||
:row="scope.row"
|
||||
/>
|
||||
class="vmstate"
|
||||
:row="scope.row"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
cell:AttachedVM="scope"
|
||||
>
|
||||
cell:AttachedVM="scope"
|
||||
>
|
||||
<div>
|
||||
<router-link
|
||||
v-if="getVMName(scope.row)"
|
||||
:to="goTo(scope.row)"
|
||||
>
|
||||
v-if="getVMName(scope.row)"
|
||||
:to="goTo(scope.row)"
|
||||
>
|
||||
{{ getVMName(scope.row) }}
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
@ -81,6 +81,10 @@ export default {
|
||||
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)) {
|
||||
_hash.nodes = this.$store.dispatch(`${ inStore }/findAll`, { type: NODE });
|
||||
this.hasNode = true;
|
||||
@ -116,7 +120,7 @@ export default {
|
||||
headers() {
|
||||
const restoreCol = {
|
||||
name: 'restoreProgress',
|
||||
labelKey: 'tableHeaders.restore',
|
||||
labelKey: 'harvester.tableHeaders.restore',
|
||||
value: 'restoreProgress',
|
||||
align: 'left',
|
||||
formatter: 'HarvesterBackupProgressBar',
|
||||
@ -128,7 +132,7 @@ export default {
|
||||
value: 'nodeName',
|
||||
sort: ['realAttachNodeName'],
|
||||
formatter: 'HarvesterHost',
|
||||
labelKey: 'tableHeaders.node'
|
||||
labelKey: 'harvester.tableHeaders.vm.node'
|
||||
};
|
||||
|
||||
const cols = clone(VM_HEADERS);
|
||||
@ -137,7 +141,7 @@ export default {
|
||||
cols.splice(-1, 0, nodeCol);
|
||||
}
|
||||
|
||||
if (this.hasRestoredVMs) {
|
||||
if (this.hasBackUpRestoreInProgress) {
|
||||
cols.splice(-1, 0, restoreCol);
|
||||
}
|
||||
|
||||
@ -150,8 +154,11 @@ export default {
|
||||
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"
|
||||
|
||||
>
|
||||
<template cell:state="scope">
|
||||
<template cell:state="scope" class="state-col">
|
||||
<div class="state">
|
||||
<HarvesterVmState class="vmstate" :row="scope.row" :all-node-network="allNodeNetworks" :all-cluster-network="allClusterNetworks" />
|
||||
</div>
|
||||
|
||||
@ -144,6 +144,10 @@ export default {
|
||||
return !!spec?.template?.spec?.domain?.firmware?.bootloader?.efi?.secureBoot;
|
||||
},
|
||||
|
||||
isCpuPinning(spec) {
|
||||
return !!spec?.template?.spec?.domain?.cpu?.dedicatedCpuPlacement;
|
||||
},
|
||||
|
||||
getCloudInitNoCloud(spec) {
|
||||
const secret = this.getSecret(spec);
|
||||
let userData = secret?.decodedData?.userdata;
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
import { HOSTNAME } from '@shell/config/labels-annotations';
|
||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||
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 } from '../../types';
|
||||
import { parseVolumeClaimTemplates } from '../../utils/vm';
|
||||
@ -136,6 +136,9 @@ export default {
|
||||
spec: null,
|
||||
osType: 'linux',
|
||||
sshKey: [],
|
||||
maintenanceStrategies,
|
||||
maintenanceStrategy: 'Migrate',
|
||||
runStrategies,
|
||||
runStrategy: 'RerunOnFailure',
|
||||
installAgent: true,
|
||||
hasCreateVolumes: [],
|
||||
@ -164,6 +167,7 @@ export default {
|
||||
enabledSriovgpu: false,
|
||||
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
|
||||
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 machineType = value.machineType;
|
||||
const cpu = spec.template.spec.domain?.cpu?.cores;
|
||||
@ -352,6 +361,7 @@ export default {
|
||||
const efiEnabled = this.isEfiEnabled(spec);
|
||||
const tpmEnabled = this.isTpmEnabled(spec);
|
||||
const secureBoot = this.isSecureBoot(spec);
|
||||
const cpuPinning = this.isCpuPinning(spec);
|
||||
|
||||
const secretRef = this.getSecret(spec);
|
||||
const accessCredentials = this.getAccessCredentials(spec);
|
||||
@ -362,6 +372,7 @@ export default {
|
||||
}
|
||||
|
||||
this['spec'] = spec;
|
||||
this['maintenanceStrategy'] = maintenanceStrategy;
|
||||
this['runStrategy'] = runStrategy;
|
||||
this['secretRef'] = secretRef;
|
||||
this['accessCredentials'] = accessCredentials;
|
||||
@ -382,6 +393,7 @@ export default {
|
||||
this['efiEnabled'] = efiEnabled;
|
||||
this['tpmEnabled'] = tpmEnabled;
|
||||
this['secureBoot'] = secureBoot;
|
||||
this['cpuPinning'] = cpuPinning;
|
||||
|
||||
this['hasCreateVolumes'] = hasCreateVolumes;
|
||||
this['networkRows'] = networkRows;
|
||||
@ -401,15 +413,37 @@ export default {
|
||||
let out = [];
|
||||
|
||||
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({
|
||||
id: randomStr(5),
|
||||
source: SOURCE_TYPE.IMAGE,
|
||||
name: 'disk-0',
|
||||
accessMode: 'ReadWriteMany',
|
||||
bus: 'virtio',
|
||||
bus,
|
||||
volumeName: '',
|
||||
size: '10Gi',
|
||||
type: HARD_DISK,
|
||||
size,
|
||||
type,
|
||||
storageClassName: '',
|
||||
image: this.imageId,
|
||||
volumeMode: 'Block',
|
||||
@ -574,6 +608,12 @@ export default {
|
||||
} else {
|
||||
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) {
|
||||
@ -681,27 +721,24 @@ export default {
|
||||
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.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.OS]: this.osType
|
||||
};
|
||||
[HCI_ANNOTATIONS.OS]: this.osType};
|
||||
|
||||
this.value['spec'] = spec;
|
||||
this['spec'] = spec;
|
||||
} else if (this.resource === HCI.VM_VERSION) {
|
||||
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['labels'] = {
|
||||
...this.value.spec.vm.metadata.labels,
|
||||
[HCI_ANNOTATIONS.OS]: this.osType
|
||||
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['labels'] = {...this.value.spec.vm.metadata.labels,
|
||||
[HCI_ANNOTATIONS.OS]: this.osType,};
|
||||
this['spec'] = spec;
|
||||
}
|
||||
},
|
||||
@ -819,6 +856,10 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
getMaintenanceStrategyOptionLabel(opt) {
|
||||
return this.t(`harvester.virtualMachine.maintenanceStrategy.options.${ opt.label || opt }`);
|
||||
},
|
||||
|
||||
getInitUserData(config) {
|
||||
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) {
|
||||
if (tpmEnabled) {
|
||||
set(this.spec.template.spec.domain.devices, 'tpm', {});
|
||||
@ -1462,6 +1511,10 @@ export default {
|
||||
this.setBootMethod({ efi: this.efiEnabled, secureBoot: val });
|
||||
},
|
||||
|
||||
cpuPinning(value) {
|
||||
this.setCpuPinning(value);
|
||||
},
|
||||
|
||||
tpmEnabled(val) {
|
||||
this.setTPM(val);
|
||||
},
|
||||
|
||||
@ -5,6 +5,7 @@ import SYSTEM_NAMESPACES from '@shell/config/system-namespaces';
|
||||
import { get } from '@shell/utils/object';
|
||||
import { NAMESPACE } from '@shell/config/types';
|
||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
|
||||
import { HCI } from '../../types';
|
||||
|
||||
const OBSCURE_NAMESPACE_PREFIX = [
|
||||
'c-', // cluster namespace
|
||||
@ -37,15 +38,32 @@ export default class HciNamespace extends namespace {
|
||||
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) {
|
||||
out.splice(remove, 1);
|
||||
}
|
||||
|
||||
insertAt(out, out.length - 1, promptRemove);
|
||||
insertAt(out, out.length - 5, editQuotaAction);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
editNSQuota(resources = this) {
|
||||
this.$dispatch('promptModal', {
|
||||
resources,
|
||||
snapshotSizeQuota: this.snapshotSizeQuota,
|
||||
component: 'HarvesterQuotaDialog'
|
||||
});
|
||||
}
|
||||
|
||||
promptRemove(resources = this) {
|
||||
this.$dispatch('promptModal', {
|
||||
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() {
|
||||
const systemNamespaces = ['fleet-default'];
|
||||
|
||||
|
||||
@ -59,6 +59,22 @@ export default class HciNode extends HarvesterResource {
|
||||
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 = {
|
||||
action: 'shutDown',
|
||||
enabled: this.hasAction('powerActionPossible') && this.hasAction('powerAction') && !this.isStopped && !!this.inventory,
|
||||
@ -88,6 +104,8 @@ export default class HciNode extends HarvesterResource {
|
||||
uncordon,
|
||||
enableMaintenance,
|
||||
disableMaintenance,
|
||||
enableCPUManager,
|
||||
disableCPUManager,
|
||||
shutDown,
|
||||
powerOn,
|
||||
reboot,
|
||||
@ -134,15 +152,12 @@ export default class HciNode extends HarvesterResource {
|
||||
|
||||
get consoleUrl() {
|
||||
const url = this.metadata?.annotations?.[HCI_ANNOTATIONS.HOST_CONSOLE_URL];
|
||||
const validator = /^[a-z]+:\/\//;
|
||||
|
||||
if (!url) {
|
||||
if (!url?.match(validator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
return `http://${ url }`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
@ -308,6 +323,14 @@ export default class HciNode extends HarvesterResource {
|
||||
this.doAction('disableMaintenanceMode', {});
|
||||
}
|
||||
|
||||
enableCPUManager() {
|
||||
this.doActionGrowl('enableCPUManager', {});
|
||||
}
|
||||
|
||||
disableCPUManager() {
|
||||
this.doActionGrowl('disableCPUManager', {});
|
||||
}
|
||||
|
||||
get isUnSchedulable() {
|
||||
return (
|
||||
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() {
|
||||
const inStore = this.$rootGetters['currentProduct'].inStore;
|
||||
const longhornNode = this.$rootGetters[`${ inStore }/byId`](
|
||||
|
||||
@ -20,12 +20,10 @@ export default class HciPv extends HarvesterResource {
|
||||
const storageClassName =
|
||||
realMode === _CLONE ? this.spec.storageClassName : '';
|
||||
|
||||
this['spec'] = {
|
||||
accessModes,
|
||||
this['spec'] = {accessModes,
|
||||
storageClassName,
|
||||
volumeName: '',
|
||||
resources: { requests: { storage } }
|
||||
};
|
||||
resources: { requests: { storage } }};
|
||||
}
|
||||
|
||||
get availableActions() {
|
||||
@ -98,7 +96,6 @@ export default class HciPv extends HarvesterResource {
|
||||
}
|
||||
|
||||
get stateDisplay() {
|
||||
const ownedBy = this?.metadata?.annotations?.[HCI_ANNOTATIONS.OWNED_BY];
|
||||
const volumeError = this.relatedPV?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_ERROR];
|
||||
const degradedVolume = volumeError === DEGRADED_ERROR;
|
||||
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') {
|
||||
return 'Resizing';
|
||||
} else if (ownedBy && !volumeError) {
|
||||
} else if (!!this.attachVM && !volumeError) {
|
||||
return 'In-use';
|
||||
} else if (degradedVolume) {
|
||||
return 'Degraded';
|
||||
@ -179,17 +176,19 @@ export default class HciPv extends HarvesterResource {
|
||||
}
|
||||
|
||||
get attachVM() {
|
||||
const allVMs = this.$rootGetters['harvester/all'](HCI.VM);
|
||||
const ownedBy =
|
||||
get(this, `metadata.annotations."${ HCI_ANNOTATIONS.OWNED_BY }"`) || '';
|
||||
const allVMs = this.$rootGetters['harvester/all'](HCI.VM) || [];
|
||||
|
||||
if (!ownedBy) {
|
||||
return null;
|
||||
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);
|
||||
}
|
||||
|
||||
const ownedId = JSON.parse(ownedBy)[0]?.refs?.[0];
|
||||
return null;
|
||||
};
|
||||
|
||||
return allVMs.find(D => D.id === ownedId);
|
||||
return allVMs.find(findAttachVM);
|
||||
}
|
||||
|
||||
get isAvailable() {
|
||||
|
||||
@ -9,6 +9,10 @@ export default class HciUpgrade extends HarvesterResource {
|
||||
return this?.metadata?.labels?.[HCI.LATEST_UPGRADE] === 'true';
|
||||
}
|
||||
|
||||
get isUpgradeFailed() {
|
||||
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Failed';
|
||||
}
|
||||
|
||||
get isUpgradeSucceeded() {
|
||||
return this?.metadata?.labels?.[HCI.UPGRADE_STATE] === 'Succeeded';
|
||||
}
|
||||
@ -117,10 +121,10 @@ export default class HciUpgrade extends HarvesterResource {
|
||||
get upgradeImageMessage() {
|
||||
const conditions = this?.status?.conditions || [];
|
||||
const imageReady = conditions.find( cond => cond.type === 'ImageReady');
|
||||
const hasError = imageReady?.status === 'False';
|
||||
const success = imageReady?.status === 'True';
|
||||
const message = imageReady?.message || imageReady?.reason;
|
||||
|
||||
return hasError ? message : '';
|
||||
return success ? '' : message;
|
||||
}
|
||||
|
||||
get nodeUpgradeMessage() {
|
||||
|
||||
@ -47,7 +47,7 @@ export default class HciVmImage extends HarvesterResource {
|
||||
{
|
||||
action: 'createFromImage',
|
||||
enabled: canCreateVM,
|
||||
icon: 'icon icon-fw icon-spinner',
|
||||
icon: 'icon icon-circle-plus',
|
||||
label: this.t('harvester.action.createVM'),
|
||||
disabled: !this.isReady,
|
||||
},
|
||||
@ -74,7 +74,7 @@ export default class HciVmImage extends HarvesterResource {
|
||||
router.push({
|
||||
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-create`,
|
||||
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');
|
||||
|
||||
if (imported?.status === 'Unknown') {
|
||||
if (this.spec.sourceType === 'restore') {
|
||||
return 'Restoring';
|
||||
}
|
||||
|
||||
if (this.spec.sourceType === 'download') {
|
||||
return 'Downloading';
|
||||
}
|
||||
@ -131,7 +135,8 @@ export default class HciVmImage extends HarvesterResource {
|
||||
const conditions = this?.status?.conditions || [];
|
||||
const initialized = conditions.find( cond => cond.type === 'Initialized');
|
||||
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);
|
||||
}
|
||||
@ -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 = []) {
|
||||
const conditions = Array.isArray(get(this, 'status.conditions')) ? this.status.conditions : defaultValue;
|
||||
|
||||
|
||||
@ -133,7 +133,7 @@ export default class VirtVm extends HarvesterResource {
|
||||
{
|
||||
action: 'softrebootVM',
|
||||
enabled: !!this.actions?.softreboot,
|
||||
icon: 'icon icon-refresh',
|
||||
icon: 'icon icon-pipeline',
|
||||
label: this.t('harvester.action.softreboot')
|
||||
},
|
||||
{
|
||||
@ -152,9 +152,15 @@ export default class VirtVm extends HarvesterResource {
|
||||
{
|
||||
action: 'takeVMSnapshot',
|
||||
enabled: !!this.actions?.backup,
|
||||
icon: 'icon icon-backup',
|
||||
icon: 'icon icon-snapshot',
|
||||
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',
|
||||
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) {
|
||||
const resources = this;
|
||||
|
||||
@ -541,6 +555,17 @@ export default class VirtVm extends HarvesterResource {
|
||||
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() {
|
||||
const inStore = this.productInStore;
|
||||
|
||||
@ -646,6 +671,10 @@ export default class VirtVm extends HarvesterResource {
|
||||
return null;
|
||||
}
|
||||
|
||||
get isTerminating() {
|
||||
return !!this?.metadata?.deletionTimestamp;
|
||||
}
|
||||
|
||||
get otherState() {
|
||||
const state = (this.vmi &&
|
||||
[VMIPhase.Scheduling, VMIPhase.Scheduled].includes(
|
||||
@ -685,18 +714,21 @@ export default class VirtVm extends HarvesterResource {
|
||||
|
||||
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() {
|
||||
const inStore = this.productInStore;
|
||||
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) {
|
||||
if (this.isVMError || this.isTerminating) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -726,7 +758,7 @@ export default class VirtVm extends HarvesterResource {
|
||||
return 'Restoring';
|
||||
}
|
||||
|
||||
if (this?.metadata?.deletionTimestamp) {
|
||||
if (this.isTerminating) {
|
||||
return 'Terminating';
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import CommunityLinks from '@shell/components/CommunityLinks';
|
||||
import { SCHEMA } from '@shell/config/types';
|
||||
import HarvesterSupportBundle from '../../../../dialog/HarvesterSupportBundle';
|
||||
import { HCI } from '../../../../types';
|
||||
import { DOC_LINKS } from '../../../../config/doc-links';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -71,13 +72,18 @@ export default {
|
||||
const { host, params } = this.internalPrefix;
|
||||
|
||||
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: {
|
||||
open() {
|
||||
this.$store.commit('harvester-common/toggleBundleModal', true);
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@ -89,10 +95,7 @@ export default {
|
||||
<IndentedPanel>
|
||||
<div class="content mt-20">
|
||||
<div class="promo">
|
||||
<div
|
||||
v-if="showSupportBundle"
|
||||
class="box mb-20 box-primary"
|
||||
>
|
||||
<div v-if="showSupportBundle" class="box mb-20 box-primary">
|
||||
<h2>
|
||||
{{ t('harvester.modal.bundle.title') }}
|
||||
</h2>
|
||||
@ -109,10 +112,7 @@ export default {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="box box-primary"
|
||||
:class="{'mb-20': dev }"
|
||||
>
|
||||
<div class="box box-primary" :class="{'mb-20': dev }">
|
||||
<h2>
|
||||
{{ t('harvester.support.kubeconfig.title') }}
|
||||
</h2>
|
||||
@ -129,41 +129,24 @@ export default {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="dev"
|
||||
class="row"
|
||||
>
|
||||
<div v-if="dev" class="row">
|
||||
<div class="col span-6 box box-primary">
|
||||
<h2>
|
||||
<a
|
||||
rel="nofollow noopener noreferrer"
|
||||
target="_blank"
|
||||
:href="rancherLink"
|
||||
>{{ t('harvester.support.internal.rancher.title') }} <i class="icon icon-external-link" /></a>
|
||||
<a rel="nofollow noopener noreferrer" target="_blank" :href="rancherLink">{{ t('harvester.support.internal.rancher.title') }} <i class="icon icon-external-link" /></a>
|
||||
</h2>
|
||||
<div>
|
||||
<p class="warning">
|
||||
<t
|
||||
k="harvester.support.internal.rancher.titleDescription"
|
||||
:raw="true"
|
||||
/>
|
||||
<t k="harvester.support.internal.rancher.titleDescription" :raw="true" :url="rancherIntegrationLink" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col span-6 box box-primary">
|
||||
<h2>
|
||||
<a
|
||||
rel="nofollow noopener noreferrer"
|
||||
target="_blank"
|
||||
:href="longhornLink"
|
||||
>{{ t('harvester.support.internal.longhorn.title') }} <i class="icon icon-external-link" /></a>
|
||||
<a rel="nofollow noopener noreferrer" target="_blank" :href="longhornLink">{{ t('harvester.support.internal.longhorn.title') }} <i class="icon icon-external-link" /></a>
|
||||
</h2>
|
||||
<div>
|
||||
<p class="warning">
|
||||
<t
|
||||
k="harvester.support.internal.longhorn.titleDescription"
|
||||
:raw="true"
|
||||
/>
|
||||
<t k="harvester.support.internal.longhorn.titleDescription" :raw="true" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -173,17 +156,9 @@ export default {
|
||||
<CommunityLinks :link-options="options" />
|
||||
</div>
|
||||
<div class="external">
|
||||
<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>
|
||||
<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
|
||||
<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>
|
||||
<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>
|
||||
</IndentedPanel>
|
||||
|
||||
@ -18,16 +18,16 @@ export default {
|
||||
const cluster = await dispatch('management/find', {
|
||||
type: MANAGEMENT.CLUSTER,
|
||||
id,
|
||||
opt: { url: `${MANAGEMENT.CLUSTER}s/${escape(id)}` }
|
||||
opt: { url: `${ MANAGEMENT.CLUSTER }s/${ escape(id) }` }
|
||||
}, { root: true });
|
||||
|
||||
let virtualBase = `/k8s/clusters/${escape(id)}/v1/harvester`;
|
||||
let virtualBase = `/k8s/clusters/${ escape(id) }/v1/harvester`;
|
||||
|
||||
if (id === 'local') {
|
||||
virtualBase = `/v1/harvester`;
|
||||
}
|
||||
|
||||
if (!cluster) {
|
||||
if ( !cluster ) {
|
||||
commit('clusterId', null, { root: true });
|
||||
commit('applyConfig', { baseUrl: null });
|
||||
throw new ClusterNotFoundError(id);
|
||||
@ -45,21 +45,21 @@ export default {
|
||||
const projectArgs = {
|
||||
type: MANAGEMENT.PROJECT,
|
||||
opt: {
|
||||
url: `${MANAGEMENT.PROJECT}/${escape(id)}`,
|
||||
url: `${ MANAGEMENT.PROJECT }/${ escape(id) }`,
|
||||
watchNamespace: id
|
||||
}
|
||||
};
|
||||
|
||||
const fetchProjects = async () => {
|
||||
const fetchProjects = async() => {
|
||||
let limit = 30000;
|
||||
const sleep = 100;
|
||||
|
||||
while (limit > 0 && !rootState.managementReady) {
|
||||
await setTimeout(() => { }, sleep);
|
||||
while ( limit > 0 && !rootState.managementReady ) {
|
||||
await setTimeout(() => {}, sleep);
|
||||
limit -= sleep;
|
||||
}
|
||||
|
||||
if (rootGetters['management/schemaFor'](MANAGEMENT.PROJECT)) {
|
||||
if ( rootGetters['management/schemaFor'](MANAGEMENT.PROJECT) ) {
|
||||
return dispatch('management/findAll', projectArgs, { root: true });
|
||||
}
|
||||
};
|
||||
@ -68,7 +68,7 @@ export default {
|
||||
const settings = await dispatch('findAll', {
|
||||
type: MANAGEMENT.SETTING,
|
||||
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);
|
||||
@ -80,7 +80,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
const hash: { [key: string]: Promise<any> } = {
|
||||
const hash: { [key: string]: Promise<any>} = {
|
||||
projects: fetchProjects(),
|
||||
virtualCount: dispatch('findAll', { type: COUNT }),
|
||||
virtualNamespaces: dispatch('findAll', { type: NAMESPACE }),
|
||||
@ -91,6 +91,10 @@ export default {
|
||||
}, { root: true }),
|
||||
};
|
||||
|
||||
if (getters['schemaFor'](HCI.RESOURCE_QUOTA)) {
|
||||
hash.resourceQuota = dispatch('findAll', { type: HCI.RESOURCE_QUOTA });
|
||||
}
|
||||
|
||||
if (getters['schemaFor'](HCI.UPGRADE)) {
|
||||
hash.upgrades = dispatch('findAll', { type: HCI.UPGRADE });
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ export default {
|
||||
divider,
|
||||
notFilterNamespaces
|
||||
}: any) => {
|
||||
const out: { id: string, kind: string, label: string }[] = [{
|
||||
const out = [{
|
||||
id: ALL,
|
||||
kind: NAMESPACE_FILTER_KINDS.SPECIAL,
|
||||
label: rootGetters['i18n/t']('nav.ns.all'),
|
||||
@ -30,11 +30,9 @@ export default {
|
||||
MANAGEMENT.PROJECT
|
||||
);
|
||||
|
||||
projects = sortBy(
|
||||
filterBy(projects, 'spec.clusterName', cluster.id),
|
||||
['nameDisplay'],
|
||||
null
|
||||
).filter((project: any) => project.nameDisplay !== 'System');
|
||||
projects = sortBy(filterBy(projects, 'spec.clusterName', cluster.id), [
|
||||
'nameDisplay',
|
||||
]).filter((project: any) => project.nameDisplay !== 'System');
|
||||
|
||||
const projectsById: any = {};
|
||||
const namespacesByProject: any = {};
|
||||
@ -72,7 +70,7 @@ export default {
|
||||
}
|
||||
|
||||
out.push({
|
||||
id: `project://${id}`,
|
||||
id: `project://${ id }`,
|
||||
kind: 'project',
|
||||
label: project.nameDisplay,
|
||||
});
|
||||
|
||||
@ -31,6 +31,7 @@ export const HCI = {
|
||||
FLOW: 'harvesterhci.io.logging.flow',
|
||||
OUTPUT: 'harvesterhci.io.logging.output',
|
||||
STORAGE: 'harvesterhci.io.storage',
|
||||
RESOURCE_QUOTA: 'harvesterhci.io.resourcequota',
|
||||
KSTUNED: 'node.harvesterhci.io.ksmtuned',
|
||||
PCI_DEVICE: 'devices.harvesterhci.io.pcidevice',
|
||||
PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user