mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-04 05:06:05 +08:00
Compare commits
79
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78482285b3 | ||
|
|
4656976b91 | ||
|
|
aeb3588b83 | ||
|
|
d14b0cb95c | ||
|
|
2f0884d7bd | ||
|
|
037ce6132b | ||
|
|
47b54b4f06 | ||
|
|
9dd8ae479a | ||
|
|
5682bf4c3a | ||
|
|
960df31667 | ||
|
|
63451e7d2b | ||
|
|
55aaa8440d | ||
|
|
da4121da69 | ||
|
|
30e49a6f2c | ||
|
|
2255bd4f69 | ||
|
|
7e5ee04637 | ||
|
|
8863de8874 | ||
|
|
73952a6b4e | ||
|
|
0496fae623 | ||
|
|
d5cd97aafd | ||
|
|
31c6a794be | ||
|
|
9d9383ba90 | ||
|
|
1e02dbffe3 | ||
|
|
b0609020cc | ||
|
|
feca476e36 | ||
|
|
e6e36e94f1 | ||
|
|
54d48ce396 | ||
|
|
404395c7d9 | ||
|
|
dc2437db99 | ||
|
|
e8c8a54b9f | ||
|
|
f3e4a8afe8 | ||
|
|
e73c62739f | ||
|
|
696af4e04f | ||
|
|
009f384a73 | ||
|
|
37766454bd | ||
|
|
7943913ef7 | ||
|
|
56a0fefd40 | ||
|
|
ff1d5925a8 | ||
|
|
63b5b980b7 | ||
|
|
1f7fd1246f | ||
|
|
34a25e9fc8 | ||
|
|
fb8d81d953 | ||
|
|
ba810a52b2 | ||
|
|
3f4f30ba0b | ||
|
|
b428aaecf8 | ||
|
|
6b07a03451 | ||
|
|
2c756582e3 | ||
|
|
1c15ed8003 | ||
|
|
9f3e689727 | ||
|
|
6644651fe5 | ||
|
|
2d6bc65b26 | ||
|
|
7b6efa624f | ||
|
|
d9db1263d1 | ||
|
|
10a8f18784 | ||
|
|
1098ef8f6a | ||
|
|
4480dbdeed | ||
|
|
a7b4366d7c | ||
|
|
d72857f374 | ||
|
|
57ab9e2830 | ||
|
|
8116358011 | ||
|
|
5bd8ad2a93 | ||
|
|
345fe636ec | ||
|
|
7e32ea453b | ||
|
|
8f3b5265de | ||
|
|
4ca6caefb9 | ||
|
|
fb7d604ed6 | ||
|
|
9a60a621de | ||
|
|
7645d6161f | ||
|
|
c3f2665912 | ||
|
|
2a58745550 | ||
|
|
69d7046bc7 | ||
|
|
692c775b51 | ||
|
|
8b43c200b2 | ||
|
|
ab71cb377c | ||
|
|
814d296d75 | ||
|
|
eb667fd56b | ||
|
|
e4b3f84905 | ||
|
|
ea68b95c26 | ||
|
|
da77762b7f |
@@ -11,24 +11,6 @@ defaults:
|
|||||||
working-directory: ./
|
working-directory: ./
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
setup-release-tag:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
release_tag: ${{ steps.determine_tag.outputs.release_tag }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Determine release tag
|
|
||||||
id: determine_tag
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.ref_name }}" =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then
|
|
||||||
RELEASE_TAG="harvester-${BASH_REMATCH[1]}"
|
|
||||||
echo "${RELEASE_TAG}"
|
|
||||||
echo "release_tag=${RELEASE_TAG}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "Error: invalid tag format." && exit 1
|
|
||||||
fi
|
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -40,7 +22,6 @@ jobs:
|
|||||||
uses: ./.github/actions/lint
|
uses: ./.github/actions/lint
|
||||||
build-extension-charts:
|
build-extension-charts:
|
||||||
needs:
|
needs:
|
||||||
- setup-release-tag
|
|
||||||
- lint
|
- lint
|
||||||
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
|
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
|
||||||
permissions:
|
permissions:
|
||||||
@@ -50,4 +31,4 @@ jobs:
|
|||||||
pages: write
|
pages: write
|
||||||
with:
|
with:
|
||||||
target_branch: gh-pages
|
target_branch: gh-pages
|
||||||
tagged_release: '${{ needs.setup-release-tag.outputs.release_tag }}'
|
tagged_release: ${{ github.ref_name }}
|
||||||
@@ -8,7 +8,20 @@ From Rancher 2.10.0, Harvester integration is provided by this new Harvester UI
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
To install the Harvester UI Extension in Rancher 2.10.0 and newer, please refer to [installation](https://docs.harvesterhci.io/v1.5/rancher/harvester-ui-extension#installation-on-rancher-210) on harvester doc.
|
To install the Harvester UI Extension in Rancher 2.10.0 and newer:
|
||||||
|
|
||||||
|
- Go to 'Extensions' in the Rancher UI (click on the jigsaw icon in the side bar)
|
||||||
|
- From the three-dots menu in the upper right of the page, open the menu and select 'Manage Repositories'
|
||||||
|
- From the Repositories page, click 'Create'
|
||||||
|
- For name, enter 'harvester-ui-extension'
|
||||||
|
- For Target, choose 'Git repository'
|
||||||
|
- For Git Repo URL, enter `https://github.com/harvester/harvester-ui-extension`
|
||||||
|
- For Git Branch, enter `gh-pages`
|
||||||
|
- Click 'Create`
|
||||||
|
- From the Repositories list, wait until the new Repository added updates to the 'Active' state
|
||||||
|
- Go back to 'Extensions'
|
||||||
|
- Click on the 'Available' tab if not already selected
|
||||||
|
- Click on 'Install' for the Harvester UI Extension
|
||||||
|
|
||||||
## Running for Development
|
## Running for Development
|
||||||
|
|
||||||
@@ -57,7 +70,7 @@ For developers, after reading through the introduction on this page, head over t
|
|||||||
|
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
Copyright (c) 2014-2025 [SUSE, LLC.](https://www.suse.com/)
|
Copyright (c) 2014-2023 [Rancher Labs, Inc.](http://rancher.com)
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester-ui-extension",
|
"name": "harvester-ui-extension",
|
||||||
"version": "1.5.0-rc4",
|
"version": "1.0.6-rc3",
|
||||||
"private": false,
|
"private": false,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rancher/shell": "3.0.2-rc.6",
|
"@rancher/shell": "3.0.1-rc.4",
|
||||||
"cache-loader": "^4.1.0",
|
"cache-loader": "^4.1.0",
|
||||||
"color": "4.2.3",
|
"color": "4.2.3",
|
||||||
"ip": "2.0.1",
|
"ip": "2.0.1",
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
"@types/node": "~20.10.0",
|
"@types/node": "~20.10.0",
|
||||||
"cronstrue": "2.50.0",
|
"cronstrue": "2.50.0",
|
||||||
"d3-color": "3.1.0",
|
"d3-color": "3.1.0",
|
||||||
"ejs": "3.1.9",
|
"ejs": "3.1.10",
|
||||||
"follow-redirects": "1.15.2",
|
"follow-redirects": "1.15.6",
|
||||||
"glob": "7.2.3",
|
"glob": "7.2.3",
|
||||||
"glob-parent": "6.0.2",
|
"glob-parent": "6.0.2",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import { Checkbox } from '@components/Form/Checkbox';
|
|||||||
import ModalWithCard from '@shell/components/ModalWithCard';
|
import ModalWithCard from '@shell/components/ModalWithCard';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import UpgradeInfo from './UpgradeInfo';
|
import UpgradeInfo from './UpgradeInfo';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterUpgrade',
|
name: 'HarvesterUpgrade',
|
||||||
|
|
||||||
@@ -30,15 +28,14 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
upgrade: [],
|
upgrade: [],
|
||||||
upgradeMessage: [],
|
upgradeMessage: [],
|
||||||
errors: '',
|
errors: '',
|
||||||
selectMode: true,
|
selectMode: true,
|
||||||
version: '',
|
version: '',
|
||||||
enableLogging: true,
|
enableLogging: true,
|
||||||
skipSingleReplicaDetachedVol: false,
|
readyReleaseNote: false,
|
||||||
readyReleaseNote: false,
|
isOpen: false
|
||||||
isOpen: false
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -71,10 +68,6 @@ export default {
|
|||||||
return this.$store.getters['harvester/schemaFor'](HCI.UPGRADE_LOG);
|
return this.$store.getters['harvester/schemaFor'](HCI.UPGRADE_LOG);
|
||||||
},
|
},
|
||||||
|
|
||||||
skipSingleReplicaDetachedVolFeatureEnabled() {
|
|
||||||
return this.$store.getters['harvester-common/getFeatureEnabled']('skipSingleReplicaDetachedVol');
|
|
||||||
},
|
|
||||||
|
|
||||||
releaseLink() {
|
releaseLink() {
|
||||||
return `https://github.com/harvester/harvester/releases/tag/${ this.version }`;
|
return `https://github.com/harvester/harvester/releases/tag/${ this.version }`;
|
||||||
}
|
}
|
||||||
@@ -111,11 +104,6 @@ export default {
|
|||||||
spec: { version: this.version }
|
spec: { version: this.version }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.skipSingleReplicaDetachedVolFeatureEnabled && this.skipSingleReplicaDetachedVol) {
|
|
||||||
upgradeValue.metadata.annotations =
|
|
||||||
{ [HCI_ANNOTATIONS.SKIP_SINGLE_REPLICA_DETACHED_VOL]: JSON.stringify(this.skipSingleReplicaDetachedVol) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.canEnableLogging) {
|
if (this.canEnableLogging) {
|
||||||
upgradeValue.spec.logEnabled = this.enableLogging;
|
upgradeValue.spec.logEnabled = this.enableLogging;
|
||||||
}
|
}
|
||||||
@@ -202,21 +190,6 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="skipSingleReplicaDetachedVolFeatureEnabled"
|
|
||||||
class="mb-5"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
v-model:value="skipSingleReplicaDetachedVol"
|
|
||||||
class="check"
|
|
||||||
type="checkbox"
|
|
||||||
:label="t('harvester.upgradePage.skipSingleReplicaDetachedVol')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<hr
|
|
||||||
v-if="version"
|
|
||||||
class="divider"
|
|
||||||
/>
|
|
||||||
<div v-if="version">
|
<div v-if="version">
|
||||||
<p
|
<p
|
||||||
v-clean-html="t('harvester.upgradePage.releaseTip', {url: releaseLink}, true)"
|
v-clean-html="t('harvester.upgradePage.releaseTip', {url: releaseLink}, true)"
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ export default {
|
|||||||
return driver === LONGHORN_DRIVER || this.allowEmptySnapshotClassNameFeatureEnabled;
|
return driver === LONGHORN_DRIVER || this.allowEmptySnapshotClassNameFeatureEnabled;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isBackupVolumeSnapshotClassNameSelectable(driver) {
|
||||||
|
return driver === LONGHORN_DRIVER;
|
||||||
|
},
|
||||||
|
|
||||||
add() {
|
add() {
|
||||||
this.configArr.push({
|
this.configArr.push({
|
||||||
key: '',
|
key: '',
|
||||||
@@ -243,6 +247,7 @@ export default {
|
|||||||
:options="getVolumeSnapshotOptions(driver.key)"
|
:options="getVolumeSnapshotOptions(driver.key)"
|
||||||
:required="isBackupVolumeSnapshotRequired(driver.key)"
|
:required="isBackupVolumeSnapshotRequired(driver.key)"
|
||||||
:label="t('harvester.setting.csiDriverConfig.backupVolumeSnapshotClassName')"
|
:label="t('harvester.setting.csiDriverConfig.backupVolumeSnapshotClassName')"
|
||||||
|
@selectable="isBackupVolumeSnapshotClassNameSelectable(driver.key)"
|
||||||
@keydown.native.enter.prevent="()=>{}"
|
@keydown.native.enter.prevent="()=>{}"
|
||||||
@update:value="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ import { NODE } from '@shell/config/types';
|
|||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import { DOC } from '../../config/doc-links';
|
import { DOC } from '../../config/doc-links';
|
||||||
import { docLink } from '../../utils/feature-flags';
|
import { docLink } from '../../utils/feature-flags';
|
||||||
import { NETWORK_TYPE } from '../../config/types';
|
|
||||||
|
|
||||||
const { L2VLAN, UNTAGGED } = NETWORK_TYPE;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditStorageNetwork',
|
name: 'HarvesterEditStorageNetwork',
|
||||||
@@ -60,14 +57,11 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
let parsedDefaultValue = {};
|
let parsedDefaultValue = {};
|
||||||
let openVlan = false;
|
let openVlan = false;
|
||||||
let networkType = L2VLAN;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parsedDefaultValue = JSON.parse(this.value.value);
|
parsedDefaultValue = JSON.parse(this.value.value);
|
||||||
networkType = 'vlan' in parsedDefaultValue ? L2VLAN : UNTAGGED; // backend doesn't provide networkType, so we check if vlan is provided instead
|
|
||||||
openVlan = true;
|
openVlan = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
networkType = L2VLAN;
|
|
||||||
parsedDefaultValue = {
|
parsedDefaultValue = {
|
||||||
vlan: '',
|
vlan: '',
|
||||||
clusterNetwork: '',
|
clusterNetwork: '',
|
||||||
@@ -79,7 +73,6 @@ export default {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
openVlan,
|
openVlan,
|
||||||
networkType,
|
|
||||||
errors: [],
|
errors: [],
|
||||||
exclude,
|
exclude,
|
||||||
parsedDefaultValue,
|
parsedDefaultValue,
|
||||||
@@ -94,35 +87,16 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
showVlan() {
|
|
||||||
return this.networkType === L2VLAN;
|
|
||||||
},
|
|
||||||
networkTypes() {
|
|
||||||
const types = [L2VLAN];
|
|
||||||
|
|
||||||
if (this.untaggedNetworkSettingEnabled) {
|
|
||||||
types.push(UNTAGGED);
|
|
||||||
}
|
|
||||||
|
|
||||||
return types;
|
|
||||||
},
|
|
||||||
storageNetworkExampleLink() {
|
storageNetworkExampleLink() {
|
||||||
const version = this.$store.getters['harvester-common/getServerVersion']();
|
const version = this.$store.getters['harvester-common/getServerVersion']();
|
||||||
|
|
||||||
return docLink(DOC.STORAGE_NETWORK_EXAMPLE, version);
|
return docLink(DOC.STORAGE_NETWORK_EXAMPLE, version);
|
||||||
},
|
},
|
||||||
|
|
||||||
untaggedNetworkSettingEnabled() {
|
|
||||||
return this.$store.getters['harvester-common/getFeatureEnabled']('untaggedNetworkSetting');
|
|
||||||
},
|
|
||||||
|
|
||||||
clusterNetworkOptions() {
|
clusterNetworkOptions() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
|
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
|
||||||
// untaggedNetwork filter out mgmt cluster network
|
|
||||||
const clusterNetworksOptions = this.networkType === UNTAGGED ? clusterNetworks.filter((net) => net.id !== 'mgmt') : clusterNetworks;
|
|
||||||
|
|
||||||
return clusterNetworksOptions.map((n) => {
|
return clusterNetworks.map((n) => {
|
||||||
const disabled = !n.isReadyForStorageNetwork;
|
const disabled = !n.isReadyForStorageNetwork;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -134,50 +108,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
|
||||||
networkType: {
|
|
||||||
handler(neu) {
|
|
||||||
this.parsedDefaultValue.clusterNetwork = '';
|
|
||||||
|
|
||||||
if (neu === L2VLAN) {
|
|
||||||
this.parsedDefaultValue.vlan = '';
|
|
||||||
} else {
|
|
||||||
delete this.parsedDefaultValue.vlan;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
inputVlan(neu) {
|
|
||||||
if (neu === '') {
|
|
||||||
this.parsedDefaultValue.vlan = '';
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newValue = Number(neu);
|
|
||||||
|
|
||||||
if (newValue > 4094) {
|
|
||||||
this.parsedDefaultValue.vlan = 4094;
|
|
||||||
} else if (newValue < 1) {
|
|
||||||
this.parsedDefaultValue.vlan = 1;
|
|
||||||
} else {
|
|
||||||
this.parsedDefaultValue.vlan = newValue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
useDefault() {
|
|
||||||
this.openVlan = false;
|
|
||||||
this.networkType = L2VLAN;
|
|
||||||
this.parsedDefaultValue = {
|
|
||||||
vlan: '',
|
|
||||||
clusterNetwork: '',
|
|
||||||
range: '',
|
|
||||||
exclude: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
const exclude = this.exclude.filter((ip) => ip);
|
const exclude = this.exclude.filter((ip) => ip);
|
||||||
|
|
||||||
@@ -207,7 +138,7 @@ export default {
|
|||||||
errors.push(this.t('harvester.setting.storageNetwork.range.invalid', null, true));
|
errors.push(this.t('harvester.setting.storageNetwork.range.invalid', null, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.networkType === L2VLAN && !this.parsedDefaultValue.vlan) {
|
if (!this.parsedDefaultValue.vlan) {
|
||||||
errors.push(this.t('validation.required', { key: this.t('harvester.setting.storageNetwork.vlan') }, true));
|
errors.push(this.t('validation.required', { key: this.t('harvester.setting.storageNetwork.vlan') }, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,25 +191,13 @@ export default {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="openVlan">
|
<div v-if="openVlan">
|
||||||
<LabeledSelect
|
|
||||||
v-model:value="networkType"
|
|
||||||
class="mb-20"
|
|
||||||
:options="networkTypes"
|
|
||||||
:mode="mode"
|
|
||||||
:label="t('harvester.fields.type')"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<LabeledInput
|
<LabeledInput
|
||||||
v-if="showVlan"
|
|
||||||
v-model:value.number="parsedDefaultValue.vlan"
|
v-model:value.number="parsedDefaultValue.vlan"
|
||||||
type="number"
|
type="number"
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
required
|
required
|
||||||
placeholder="e.g. 1 - 4094"
|
|
||||||
label-key="harvester.setting.storageNetwork.vlan"
|
label-key="harvester.setting.storageNetwork.vlan"
|
||||||
@update:value="inputVlan"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default {
|
|||||||
name="model"
|
name="model"
|
||||||
:options="[true,false]"
|
:options="[true,false]"
|
||||||
:labels="[t('generic.enabled'), t('generic.disabled')]"
|
:labels="[t('generic.enabled'), t('generic.disabled')]"
|
||||||
@input="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LabeledInput
|
<LabeledInput
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const featuresV133 = [
|
|||||||
|
|
||||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0
|
// https://github.com/harvester/dashboard/releases/tag/v1.4.0
|
||||||
const featuresV140 = [
|
const featuresV140 = [
|
||||||
...featuresV133,
|
...featuresV132,
|
||||||
'cpuPinning',
|
'cpuPinning',
|
||||||
'usbPassthrough',
|
'usbPassthrough',
|
||||||
'volumeEncryption',
|
'volumeEncryption',
|
||||||
@@ -40,21 +40,21 @@ const featuresV141 = [
|
|||||||
...featuresV140
|
...featuresV140
|
||||||
];
|
];
|
||||||
|
|
||||||
// TODO: add v1.4.2 official release note
|
// https://github.com/harvester/dashboard/releases/tag/v1.4.2
|
||||||
const featuresV142 = [
|
const featuresV142 = [
|
||||||
...featuresV141,
|
...featuresV141,
|
||||||
'refreshIntervalInSecond',
|
'refreshIntervalInSecond',
|
||||||
'allowEmptySnapshotClassName'
|
'allowEmptySnapshotClassName'
|
||||||
];
|
];
|
||||||
|
|
||||||
// TODO: add v1.5.0 official release note
|
// https://github.com/harvester/dashboard/releases/tag/v1.4.3-rc1
|
||||||
const featuresV150 = [
|
const featuresV143 = [
|
||||||
...featuresV142,
|
...featuresV142,
|
||||||
'tpmPersistentState',
|
];
|
||||||
'efiPersistentState',
|
|
||||||
'untaggedNetworkSetting',
|
// https://github.com/harvester/harvester-ui-extension/releases/tag/v1.5.0
|
||||||
'skipSingleReplicaDetachedVol',
|
const featuresV150 = [
|
||||||
'thirdPartyStorage'
|
...featuresV142
|
||||||
];
|
];
|
||||||
|
|
||||||
export const RELEASE_FEATURES = {
|
export const RELEASE_FEATURES = {
|
||||||
@@ -65,5 +65,6 @@ export const RELEASE_FEATURES = {
|
|||||||
'v1.4.0': featuresV140,
|
'v1.4.0': featuresV140,
|
||||||
'v1.4.1': featuresV141,
|
'v1.4.1': featuresV141,
|
||||||
'v1.4.2': featuresV142,
|
'v1.4.2': featuresV142,
|
||||||
|
'v1.4.3': featuresV143,
|
||||||
'v1.5.0': featuresV150
|
'v1.5.0': featuresV150
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ import {
|
|||||||
FINGERPRINT,
|
FINGERPRINT,
|
||||||
IMAGE_PROGRESS,
|
IMAGE_PROGRESS,
|
||||||
SNAPSHOT_TARGET_VOLUME,
|
SNAPSHOT_TARGET_VOLUME,
|
||||||
IMAGE_VIRTUAL_SIZE,
|
IMAGE_VIRTUAL_SIZE
|
||||||
IMAGE_STORAGE_CLASS
|
|
||||||
} from './table-headers';
|
} from './table-headers';
|
||||||
|
|
||||||
const TEMPLATE = HCI.VM_VERSION;
|
const TEMPLATE = HCI.VM_VERSION;
|
||||||
@@ -222,7 +221,6 @@ export function init($plugin, store) {
|
|||||||
STATE,
|
STATE,
|
||||||
NAME_COL,
|
NAME_COL,
|
||||||
NAMESPACE_COL,
|
NAMESPACE_COL,
|
||||||
IMAGE_STORAGE_CLASS,
|
|
||||||
IMAGE_PROGRESS,
|
IMAGE_PROGRESS,
|
||||||
IMAGE_DOWNLOAD_SIZE,
|
IMAGE_DOWNLOAD_SIZE,
|
||||||
IMAGE_VIRTUAL_SIZE,
|
IMAGE_VIRTUAL_SIZE,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// TODO: delete this not used variable
|
|
||||||
export const MemoryUnit = [{
|
export const MemoryUnit = [{
|
||||||
label: 'Mi',
|
label: 'Mi',
|
||||||
value: 'Mi'
|
value: 'Mi'
|
||||||
|
|||||||
@@ -7,63 +7,60 @@ export const LABELS_TO_IGNORE_REGEX = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const HCI = {
|
export const HCI = {
|
||||||
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
|
CLOUD_INIT: 'harvesterhci.io/cloud-init-template',
|
||||||
CURRENT_IP: 'rke2.io/internal-ip',
|
CURRENT_IP: 'rke2.io/internal-ip',
|
||||||
IMAGE_ID: 'harvesterhci.io/imageId',
|
IMAGE_ID: 'harvesterhci.io/imageId',
|
||||||
SSH_NAMES: 'harvesterhci.io/sshNames',
|
SSH_NAMES: 'harvesterhci.io/sshNames',
|
||||||
NETWORK_IPS: 'network.harvesterhci.io/ips',
|
NETWORK_IPS: 'network.harvesterhci.io/ips',
|
||||||
TEMPLATE_VERSION_CUSTOM_NAME: 'template-version.harvesterhci.io/customName',
|
TEMPLATE_VERSION_CUSTOM_NAME: 'template-version.harvesterhci.io/customName',
|
||||||
CREATOR: 'harvesterhci.io/creator',
|
CREATOR: 'harvesterhci.io/creator',
|
||||||
OS: 'harvesterhci.io/os',
|
OS: 'harvesterhci.io/os',
|
||||||
GOLDEN_IMAGE: 'harvesterhci.io/goldenImage',
|
NETWORK_TYPE: 'network.harvesterhci.io/type',
|
||||||
NETWORK_TYPE: 'network.harvesterhci.io/type',
|
VM_NAME: 'harvesterhci.io/vmName',
|
||||||
VM_NAME: 'harvesterhci.io/vmName',
|
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
|
||||||
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
|
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
|
||||||
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
|
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
|
||||||
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
|
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
|
||||||
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
|
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
|
||||||
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
|
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
||||||
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
|
||||||
NODE_ROLE_MASTER: 'node-role.kubernetes.io/master',
|
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
|
||||||
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
|
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
|
||||||
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
|
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
|
||||||
PROMOTE_STATUS: 'harvesterhci.io/promote-status',
|
MIGRATION_STATE: 'harvesterhci.io/migrationState',
|
||||||
MIGRATION_STATE: 'harvesterhci.io/migrationState',
|
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
|
||||||
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
|
IMAGE_NAME: 'harvesterhci.io/image-name',
|
||||||
IMAGE_NAME: 'harvesterhci.io/image-name',
|
INIT_IP: 'etcd.rke2.cattle.io/node-address',
|
||||||
INIT_IP: 'etcd.rke2.cattle.io/node-address',
|
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
|
||||||
NODE_SCHEDULABLE: 'kubevirt.io/schedulable',
|
NETWORK_ROUTE: 'network.harvesterhci.io/route',
|
||||||
NETWORK_ROUTE: 'network.harvesterhci.io/route',
|
MATCHED_NODES: 'network.harvesterhci.io/matched-nodes',
|
||||||
MATCHED_NODES: 'network.harvesterhci.io/matched-nodes',
|
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
|
||||||
OS_UPGRADE_IMAGE: 'harvesterhci.io/os-upgrade-image',
|
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
|
||||||
LATEST_UPGRADE: 'harvesterhci.io/latestUpgrade',
|
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
|
||||||
UPGRADE_STATE: 'harvesterhci.io/upgradeState',
|
REAY_MESSAGE: 'harvesterhci.io/read-message',
|
||||||
REAY_MESSAGE: 'harvesterhci.io/read-message',
|
DYNAMIC_SSHKEYS_NAMES: 'harvesterhci.io/dynamic-ssh-key-names',
|
||||||
DYNAMIC_SSHKEYS_NAMES: 'harvesterhci.io/dynamic-ssh-key-names',
|
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
|
||||||
DYNAMIC_SSHKEYS_USERS: 'harvesterhci.io/dynamic-ssh-key-users',
|
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
|
||||||
IMAGE_SUFFIX: 'harvesterhci.io/image-type',
|
OS_TYPE: 'harvesterhci.io/os-type',
|
||||||
OS_TYPE: 'harvesterhci.io/os-type',
|
STORAGE_PROVISIONER: 'harvesterhci.io/storageProvisioner',
|
||||||
STORAGE_PROVISIONER: 'harvesterhci.io/storageProvisioner',
|
HOST_REQUEST: 'management.cattle.io/pod-requests',
|
||||||
SKIP_SINGLE_REPLICA_DETACHED_VOL: 'harvesterhci.io/skipSingleReplicaDetachedVol',
|
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
|
||||||
HOST_REQUEST: 'management.cattle.io/pod-requests',
|
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
|
||||||
STORAGE_CLASS: 'harvesterhci.io/storageClassName',
|
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
|
||||||
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
|
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
|
||||||
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
|
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
|
||||||
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
|
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
|
||||||
VOLUME_FOR_VM: 'harvesterhci.io/volumeForVirtualMachine',
|
NODE_MANUFACTURER: 'manufacturer',
|
||||||
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
|
NODE_MODEL: 'model',
|
||||||
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
|
NODE_SERIAL_NUMBER: 'serialNumber',
|
||||||
NODE_MANUFACTURER: 'manufacturer',
|
VM_INSUFFICIENT: 'harvesterhci.io/insufficient-resource-quota',
|
||||||
NODE_MODEL: 'model',
|
NODE_NTP_SYNC_STATUS: 'node.harvesterhci.io/ntp-service',
|
||||||
NODE_SERIAL_NUMBER: 'serialNumber',
|
PARENT_SRIOV: 'harvesterhci.io/parent-sriov-network-device',
|
||||||
VM_INSUFFICIENT: 'harvesterhci.io/insufficient-resource-quota',
|
PARENT_SRIOV_GPU: 'harvesterhci.io/parentSRIOVGPUDevice',
|
||||||
NODE_NTP_SYNC_STATUS: 'node.harvesterhci.io/ntp-service',
|
VM_MAINTENANCE_MODE_STRATEGY: 'harvesterhci.io/maintain-mode-strategy',
|
||||||
PARENT_SRIOV: 'harvesterhci.io/parent-sriov-network-device',
|
NODE_CPU_MANAGER_UPDATE_STATUS: 'harvesterhci.io/cpu-manager-update-status',
|
||||||
PARENT_SRIOV_GPU: 'harvesterhci.io/parentSRIOVGPUDevice',
|
CPU_MANAGER: 'cpumanager',
|
||||||
VM_MAINTENANCE_MODE_STRATEGY: 'harvesterhci.io/maintain-mode-strategy',
|
VM_DEVICE_ALLOCATION_DETAILS: 'harvesterhci.io/deviceAllocationDetails',
|
||||||
NODE_CPU_MANAGER_UPDATE_STATUS: 'harvesterhci.io/cpu-manager-update-status',
|
SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId',
|
||||||
CPU_MANAGER: 'cpumanager',
|
DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine',
|
||||||
VM_DEVICE_ALLOCATION_DETAILS: 'harvesterhci.io/deviceAllocationDetails',
|
|
||||||
SVM_BACKUP_ID: 'harvesterhci.io/svmbackupId',
|
|
||||||
DISABLE_LONGHORN_V2_ENGINE: 'node.longhorn.io/disable-v2-data-engine',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,9 +65,7 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
[HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: {},
|
[HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: {},
|
||||||
[HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: { featureFlag: 'supportBundleNodeCollectionTimeoutSetting' },
|
[HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: { featureFlag: 'supportBundleNodeCollectionTimeoutSetting' },
|
||||||
[HCI_SETTING.SUPPORT_BUNDLE_IMAGE]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.SUPPORT_BUNDLE_IMAGE]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.STORAGE_NETWORK]: {
|
[HCI_SETTING.STORAGE_NETWORK]: { kind: 'custom', from: 'import' },
|
||||||
kind: 'custom', from: 'import', canReset: true
|
|
||||||
},
|
|
||||||
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.SSL_CERTIFICATES]: { kind: 'json', from: 'import' },
|
[HCI_SETTING.SSL_CERTIFICATES]: { kind: 'json', from: 'import' },
|
||||||
[HCI_SETTING.SSL_PARAMETERS]: {
|
[HCI_SETTING.SSL_PARAMETERS]: {
|
||||||
|
|||||||
@@ -88,13 +88,3 @@ export const MACHINE_POOLS = {
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: 100,
|
width: 100,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The STORAGE_CLASS column in VM image list page
|
|
||||||
export const IMAGE_STORAGE_CLASS = {
|
|
||||||
name: 'imageStorageClass',
|
|
||||||
labelKey: 'harvester.tableHeaders.storageClass',
|
|
||||||
sort: 'imageStorageClass',
|
|
||||||
value: 'imageStorageClass',
|
|
||||||
align: 'left',
|
|
||||||
width: 200,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -2,13 +2,3 @@ export const BACKUP_TYPE = {
|
|||||||
BACKUP: 'backup',
|
BACKUP: 'backup',
|
||||||
SNAPSHOT: 'snapshot'
|
SNAPSHOT: 'snapshot'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NETWORK_TYPE = {
|
|
||||||
L2VLAN: 'L2VlanNetwork',
|
|
||||||
UNTAGGED: 'UntaggedNetwork'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const VOLUME_MODE = {
|
|
||||||
BLOCK: 'Block',
|
|
||||||
FILE_SYSTEM: 'Filesystem'
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { formatSi, exponentNeeded, UNITS } from '@shell/utils/units';
|
|||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
import { LONGHORN, METRIC } from '@shell/config/types';
|
import { LONGHORN, METRIC } from '@shell/config/types';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
import { UNIT_SUFFIX } from '../../utils/unit';
|
|
||||||
import HarvesterCPUUsed from '../../formatters/HarvesterCPUUsed';
|
import HarvesterCPUUsed from '../../formatters/HarvesterCPUUsed';
|
||||||
import HarvesterMemoryUsed from '../../formatters/HarvesterMemoryUsed';
|
import HarvesterMemoryUsed from '../../formatters/HarvesterMemoryUsed';
|
||||||
import HarvesterStorageUsed from '../../formatters/HarvesterStorageUsed';
|
import HarvesterStorageUsed from '../../formatters/HarvesterStorageUsed';
|
||||||
@@ -122,7 +121,7 @@ export default {
|
|||||||
memoryUnits() {
|
memoryUnits() {
|
||||||
const exponent = exponentNeeded(this.memoryTotal, 1024);
|
const exponent = exponentNeeded(this.memoryTotal, 1024);
|
||||||
|
|
||||||
return `${ UNITS[exponent] }${ UNIT_SUFFIX }`;
|
return `${ UNITS[exponent] }iB`;
|
||||||
},
|
},
|
||||||
|
|
||||||
nodeType() {
|
nodeType() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import ArrayListGrouped from '@shell/components/form/ArrayListGrouped';
|
|||||||
import Loading from '@shell/components/Loading.vue';
|
import Loading from '@shell/components/Loading.vue';
|
||||||
import SortableTable from '@shell/components/SortableTable';
|
import SortableTable from '@shell/components/SortableTable';
|
||||||
import Banner from '@components/Banner/Banner.vue';
|
import Banner from '@components/Banner/Banner.vue';
|
||||||
import { UNIT_SUFFIX } from '../../utils/unit';
|
|
||||||
import metricPoller from '@shell/mixins/metric-poller';
|
import metricPoller from '@shell/mixins/metric-poller';
|
||||||
import {
|
import {
|
||||||
METRIC, NODE, LONGHORN, POD, EVENT
|
METRIC, NODE, LONGHORN, POD, EVENT
|
||||||
@@ -178,7 +178,7 @@ export default {
|
|||||||
minExponent: 3,
|
minExponent: 3,
|
||||||
maxExponent: 3,
|
maxExponent: 3,
|
||||||
maxPrecision: 2,
|
maxPrecision: 2,
|
||||||
suffix: UNIT_SUFFIX,
|
suffix: 'iB',
|
||||||
};
|
};
|
||||||
|
|
||||||
const longhornDisks = Object.keys(diskStatus).map((key) => {
|
const longhornDisks = Object.keys(diskStatus).map((key) => {
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { mapState } from 'vuex';
|
|
||||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
||||||
import { alternateLabel } from '@shell/utils/platform';
|
|
||||||
import AsyncButton from '@shell/components/AsyncButton';
|
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import { Card } from '@components/Card';
|
|
||||||
import { escapeHtml } from '@shell/utils/string';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name ConfirmExecutionDialog
|
|
||||||
* @description Dialog component to confirm the related resources before executing the action.
|
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
name: 'ConfirmExecutionDialog',
|
|
||||||
|
|
||||||
emits: ['close'],
|
|
||||||
|
|
||||||
components: {
|
|
||||||
AsyncButton,
|
|
||||||
Banner,
|
|
||||||
Card,
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
/**
|
|
||||||
* @property resources to be deleted.
|
|
||||||
* @type {Resource[]} Array of the resource model's instance
|
|
||||||
*/
|
|
||||||
resources: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return { errors: [] };
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapState('action-menu', ['modalData']),
|
|
||||||
|
|
||||||
warningMessageKey() {
|
|
||||||
return this.modalData.warningMessageKey;
|
|
||||||
},
|
|
||||||
|
|
||||||
names() {
|
|
||||||
return this.resources.map((obj) => obj.nameDisplay).slice(0, 5);
|
|
||||||
},
|
|
||||||
|
|
||||||
resourceNames() {
|
|
||||||
return this.names.reduce((res, name, i) => {
|
|
||||||
if (i >= 5) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
res += `<b>${ escapeHtml(name) }</b>`;
|
|
||||||
if (i === this.names.length - 1) {
|
|
||||||
res += this.plusMore;
|
|
||||||
} else {
|
|
||||||
res += i === this.resources.length - 2 ? ' and ' : ', ';
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}, '');
|
|
||||||
},
|
|
||||||
|
|
||||||
plusMore() {
|
|
||||||
const remaining = this.resources.length - this.names.length;
|
|
||||||
|
|
||||||
return this.t('dialog.confirmExecution.andOthers', { count: remaining });
|
|
||||||
},
|
|
||||||
|
|
||||||
type() {
|
|
||||||
const types = new Set(this.resources.reduce((array, each) => {
|
|
||||||
array.push(each.type);
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}, []));
|
|
||||||
|
|
||||||
if (types.size > 1) {
|
|
||||||
return this.t('generic.resource', { count: this.resources.length });
|
|
||||||
}
|
|
||||||
|
|
||||||
const schema = this.resources[0]?.schema;
|
|
||||||
|
|
||||||
if ( !schema ) {
|
|
||||||
return `resource${ this.resources.length === 1 ? '' : 's' }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.$store.getters['type-map/labelFor'](schema, this.resources.length);
|
|
||||||
},
|
|
||||||
|
|
||||||
protip() {
|
|
||||||
return this.t('dialog.confirmExecution.protip', { alternateLabel });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
escapeHtml,
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.errors = [];
|
|
||||||
this.$emit('close');
|
|
||||||
},
|
|
||||||
|
|
||||||
async apply(buttonDone) {
|
|
||||||
try {
|
|
||||||
for (const resource of this.resources) {
|
|
||||||
await resource.doActionGrowl(this.modalData.action, {});
|
|
||||||
}
|
|
||||||
buttonDone(true);
|
|
||||||
this.close();
|
|
||||||
} catch (e) {
|
|
||||||
this.errors = exceptionToErrorsArray(e);
|
|
||||||
buttonDone(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Card :show-highlight-border="false">
|
|
||||||
<template #title>
|
|
||||||
<h4 class="text-default-text">
|
|
||||||
{{ t('dialog.confirmExecution.title') }}
|
|
||||||
</h4>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #body>
|
|
||||||
<div class="pl-10 pr-10">
|
|
||||||
<span
|
|
||||||
v-clean-html="t(warningMessageKey, { type, names: resourceNames }, true)"
|
|
||||||
></span>
|
|
||||||
<div class="text-info mt-20">
|
|
||||||
{{ protip }}
|
|
||||||
</div>
|
|
||||||
<Banner
|
|
||||||
v-for="(error, i) in errors"
|
|
||||||
:key="i"
|
|
||||||
:label="error"
|
|
||||||
color="error"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #actions>
|
|
||||||
<div class="actions">
|
|
||||||
<button
|
|
||||||
class="btn role-secondary"
|
|
||||||
@click="close"
|
|
||||||
>
|
|
||||||
{{ t('generic.cancel') }}
|
|
||||||
</button>
|
|
||||||
<AsyncButton
|
|
||||||
mode="apply"
|
|
||||||
class="btn bg-primary ml-10"
|
|
||||||
:disabled="applyDisabled"
|
|
||||||
@click="apply"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
.modal-container {
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
width: 100%;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -34,14 +34,9 @@ export default {
|
|||||||
|
|
||||||
await allHash(hash);
|
await allHash(hash);
|
||||||
|
|
||||||
const allStorages = this.allStorageClasses;
|
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
|
||||||
const defaultStorage = allStorages.find((s) => s.isDefault);
|
|
||||||
|
|
||||||
if (this.isLonghornV1Volume) {
|
this['storageClassName'] = defaultStorage?.metadata?.name || 'longhorn';
|
||||||
this['storageClassName'] = defaultStorage?.metadata?.name || 'longhorn';
|
|
||||||
} else {
|
|
||||||
this['storageClassName'] = this.nonLonghornV1StorageClasses[0]?.metadata?.name || '';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -59,7 +54,7 @@ export default {
|
|||||||
...mapGetters({ t: 'i18n/t' }),
|
...mapGetters({ t: 'i18n/t' }),
|
||||||
|
|
||||||
actionResource() {
|
actionResource() {
|
||||||
return this.resources[0] || {};
|
return this.resources[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
namespaces() {
|
namespaces() {
|
||||||
@@ -78,33 +73,15 @@ export default {
|
|||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
isLonghornV1Volume() {
|
|
||||||
return this.actionResource?.isLonghornV1 === true;
|
|
||||||
},
|
|
||||||
|
|
||||||
disableSave() {
|
disableSave() {
|
||||||
return !(this.name && this.namespace && this.storageClassName);
|
return !(this.name && this.namespace && this.storageClassName);
|
||||||
},
|
},
|
||||||
|
|
||||||
allStorageClasses() {
|
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
|
|
||||||
return this.$store.getters[`${ inStore }/all`](STORAGE_CLASS) || [];
|
|
||||||
},
|
|
||||||
|
|
||||||
nonLonghornV1StorageClasses() {
|
|
||||||
return this.allStorageClasses.filter((s) => s.isLonghornV1 === false) || [];
|
|
||||||
},
|
|
||||||
|
|
||||||
storageClassOptions() {
|
storageClassOptions() {
|
||||||
let storages = this.allStorageClasses;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
||||||
|
|
||||||
// Volume with non-longhorn v1 sc can't be exported to longhorn v1 sc image
|
const out = storages.filter((s) => !s.parameters?.backingImage).map((s) => {
|
||||||
if (!this.isLonghornV1Volume) {
|
|
||||||
storages = this.allStorageClasses.filter((s) => s.isLonghornV1 === false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = storages.filter((s) => !s.parameters?.backingImage).map((s) => {
|
|
||||||
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -113,7 +90,7 @@ export default {
|
|||||||
};
|
};
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
return options;
|
return out;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
|
||||||
import { HCI } from '../types';
|
|
||||||
import { Card } from '@components/Card';
|
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import AsyncButton from '@shell/components/AsyncButton';
|
|
||||||
import AppModal from '@shell/components/AppModal';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HarvesterImageDownloaderDialog',
|
|
||||||
|
|
||||||
emits: ['close'],
|
|
||||||
|
|
||||||
components: {
|
|
||||||
AsyncButton, Banner, Card, AppModal
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
resources: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return { errors: [], isOpen: false };
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters({ t: 'i18n/t' }),
|
|
||||||
|
|
||||||
downloadImageInProgress() {
|
|
||||||
return this.$store.getters['harvester-common/isDownloadImageInProgress'];
|
|
||||||
},
|
|
||||||
|
|
||||||
image() {
|
|
||||||
return this.resources[0] || {};
|
|
||||||
},
|
|
||||||
|
|
||||||
imageName() {
|
|
||||||
return this.image?.name || '';
|
|
||||||
},
|
|
||||||
|
|
||||||
imageVirtualSize() {
|
|
||||||
return this.image?.virtualSize || this.image?.downSize || '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
async cancelDownload() {
|
|
||||||
const url = this.image?.links?.downloadcancel;
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
await this.$store.dispatch('harvester/request', { url });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async close() {
|
|
||||||
if (this.downloadImageInProgress) {
|
|
||||||
this.$store.commit('harvester-common/setDownloadImageCancel', true);
|
|
||||||
this.$store.commit('harvester-common/setDownloadImageInProgress', false);
|
|
||||||
await this.cancelDownload();
|
|
||||||
}
|
|
||||||
this.$emit('close');
|
|
||||||
},
|
|
||||||
|
|
||||||
async startDownload(buttonCb) {
|
|
||||||
// clean the download image CRD first.
|
|
||||||
await this.cancelDownload();
|
|
||||||
this.$store.commit('harvester-common/setDownloadImageCancel', false);
|
|
||||||
this.$store.commit('harvester-common/setDownloadImageInProgress', false);
|
|
||||||
this.errors = [];
|
|
||||||
|
|
||||||
const name = this.image?.name || '';
|
|
||||||
const namespace = this.image?.namespace || '';
|
|
||||||
|
|
||||||
const imageCrd = {
|
|
||||||
apiVersion: 'harvesterhci.io/v1beta1',
|
|
||||||
type: HCI.VM_IMAGE_DOWNLOADER,
|
|
||||||
kind: 'VirtualMachineImageDownloader',
|
|
||||||
metadata: {
|
|
||||||
name,
|
|
||||||
namespace
|
|
||||||
},
|
|
||||||
spec: { imageName: name }
|
|
||||||
};
|
|
||||||
|
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
const imageCreate = await this.$store.dispatch(`${ inStore }/create`, imageCrd);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await imageCreate.save();
|
|
||||||
this.$store.commit('harvester-common/setDownloadImageId', `${ namespace }/${ name }`, { root: true });
|
|
||||||
this.$store.dispatch('harvester-common/downloadImageProgress', { root: true });
|
|
||||||
} catch (err) {
|
|
||||||
this.errors = exceptionToErrorsArray(err);
|
|
||||||
buttonCb(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<app-modal
|
|
||||||
class="image-downloader-modal"
|
|
||||||
name="image-download-dialog"
|
|
||||||
height="auto"
|
|
||||||
:width="600"
|
|
||||||
:click-to-close="false"
|
|
||||||
@close="close"
|
|
||||||
>
|
|
||||||
<Card :show-highlight-border="false">
|
|
||||||
<template #title>
|
|
||||||
{{ t('harvester.modal.downloadImage.title') }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #body>
|
|
||||||
<Banner color="info">
|
|
||||||
{{ t('harvester.modal.downloadImage.banner', { size: imageVirtualSize }) }}
|
|
||||||
</Banner>
|
|
||||||
{{ t('harvester.modal.downloadImage.startMessage') }}
|
|
||||||
<br /><br />
|
|
||||||
<Banner
|
|
||||||
v-for="(err, i) in errors"
|
|
||||||
:key="i"
|
|
||||||
color="error"
|
|
||||||
:label="err"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #actions>
|
|
||||||
<div class="actions">
|
|
||||||
<div class="buttons">
|
|
||||||
<button
|
|
||||||
class="btn role-secondary mr-10"
|
|
||||||
@click="close"
|
|
||||||
>
|
|
||||||
{{ t('generic.cancel') }}
|
|
||||||
</button>
|
|
||||||
<AsyncButton
|
|
||||||
type="submit"
|
|
||||||
mode="download"
|
|
||||||
:disabled="downloadImageInProgress"
|
|
||||||
@click="startDownload"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Card>
|
|
||||||
</app-modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.actions {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,7 +5,6 @@ import { Banner } from '@components/Banner';
|
|||||||
import AsyncButton from '@shell/components/AsyncButton';
|
import AsyncButton from '@shell/components/AsyncButton';
|
||||||
import UnitInput from '@shell/components/form/UnitInput';
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||||
import { GIBIBYTE } from '../utils/unit';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterVMQuotaDialog',
|
name: 'HarvesterVMQuotaDialog',
|
||||||
@@ -30,7 +29,6 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
GIBIBYTE,
|
|
||||||
totalSnapshotSize: '',
|
totalSnapshotSize: '',
|
||||||
errors: []
|
errors: []
|
||||||
};
|
};
|
||||||
@@ -46,6 +44,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.totalSnapshotSize = '';
|
this.totalSnapshotSize = '';
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
@@ -93,7 +92,7 @@ export default {
|
|||||||
:input-exponent="3"
|
:input-exponent="3"
|
||||||
:increment="1024"
|
:increment="1024"
|
||||||
:output-modifier="true"
|
:output-modifier="true"
|
||||||
:suffix="GIBIBYTE"
|
suffix="GiB"
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
/>
|
/>
|
||||||
<Banner
|
<Banner
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import Loading from '@shell/components/Loading.vue';
|
|||||||
import MessageLink from '@shell/components/MessageLink';
|
import MessageLink from '@shell/components/MessageLink';
|
||||||
import { ADD_ONS } from '@pkg/harvester/config/harvester-map';
|
import { ADD_ONS } from '@pkg/harvester/config/harvester-map';
|
||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
|
||||||
import { UNIT_SUFFIX } from '../../utils/unit';
|
|
||||||
import { _EDIT } from '@shell/config/query-params';
|
import { _EDIT } from '@shell/config/query-params';
|
||||||
import { sortBy } from '@shell/utils/sort';
|
import { sortBy } from '@shell/utils/sort';
|
||||||
import { Banner } from '@components/Banner';
|
import { Banner } from '@components/Banner';
|
||||||
@@ -185,7 +185,7 @@ export default {
|
|||||||
minExponent: 3,
|
minExponent: 3,
|
||||||
maxExponent: 3,
|
maxExponent: 3,
|
||||||
maxPrecision: 2,
|
maxPrecision: 2,
|
||||||
suffix: UNIT_SUFFIX,
|
suffix: 'iB',
|
||||||
};
|
};
|
||||||
|
|
||||||
const longhornDisks = Object.keys(diskStatus).map((key) => {
|
const longhornDisks = Object.keys(diskStatus).map((key) => {
|
||||||
@@ -457,7 +457,7 @@ export default {
|
|||||||
const devPath = d.spec?.devPath;
|
const devPath = d.spec?.devPath;
|
||||||
const deviceType = d.status?.deviceStatus?.details?.deviceType;
|
const deviceType = d.status?.deviceStatus?.details?.deviceType;
|
||||||
const sizeBytes = d.status?.deviceStatus?.capacity?.sizeBytes;
|
const sizeBytes = d.status?.deviceStatus?.capacity?.sizeBytes;
|
||||||
const size = formatSi(sizeBytes, { increment: 1024, suffix: UNIT_SUFFIX });
|
const size = formatSi(sizeBytes, { increment: 1024 });
|
||||||
const parentDevice = d.status?.deviceStatus?.parentDevice;
|
const parentDevice = d.status?.deviceStatus?.parentDevice;
|
||||||
const isChildAdded = this.newDisks.find((newDisk) => newDisk.blockDevice?.status?.deviceStatus?.parentDevice === devPath);
|
const isChildAdded = this.newDisks.find((newDisk) => newDisk.blockDevice?.status?.deviceStatus?.parentDevice === devPath);
|
||||||
const name = d.displayName;
|
const name = d.displayName;
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import { HCI as HCI_LABELS_ANNOTATIONS } from '@pkg/harvester/config/labels-anno
|
|||||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import { NETWORK_TYPE } from '../config/types';
|
|
||||||
|
|
||||||
const { L2VLAN, UNTAGGED } = NETWORK_TYPE;
|
|
||||||
|
|
||||||
const AUTO = 'auto';
|
const AUTO = 'auto';
|
||||||
const MANUAL = 'manual';
|
const MANUAL = 'manual';
|
||||||
@@ -51,7 +48,7 @@ export default {
|
|||||||
config.bridge = config.bridge.slice(0, -3);
|
config.bridge = config.bridge.slice(0, -3);
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = this.value.vlanType || L2VLAN ;
|
const type = this.value.vlanType || 'L2VlanNetwork' ;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
config,
|
config,
|
||||||
@@ -104,15 +101,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
networkType() {
|
networkType() {
|
||||||
return [L2VLAN, UNTAGGED];
|
return ['L2VlanNetwork', 'UntaggedNetwork'];
|
||||||
},
|
},
|
||||||
|
|
||||||
isUntaggedNetwork() {
|
isUntaggedNetwork() {
|
||||||
if (this.isView) {
|
if (this.isView) {
|
||||||
return this.value.vlanType === UNTAGGED;
|
return this.value.vlanType === 'UntaggedNetwork';
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.type === UNTAGGED;
|
return this.type === 'UntaggedNetwork';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export default {
|
|||||||
:disabled="isEdit || isView"
|
:disabled="isEdit || isView"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
row
|
row
|
||||||
@input="onTypeChange"
|
@update:value="onTypeChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-30">
|
<div class="row mb-30">
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ import Tags from '../../components/DiskTags';
|
|||||||
import { DATA_ENGINE_V1, DATA_ENGINE_V2 } from '../../models/harvester/persistentvolumeclaim';
|
import { DATA_ENGINE_V1, DATA_ENGINE_V2 } from '../../models/harvester/persistentvolumeclaim';
|
||||||
import { LVM_DRIVER } from '../../models/harvester/storage.k8s.io.storageclass';
|
import { LVM_DRIVER } from '../../models/harvester/storage.k8s.io.storageclass';
|
||||||
|
|
||||||
|
const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine';
|
||||||
|
|
||||||
export const LVM_TOPOLOGY_LABEL = 'topology.lvm.csi/node';
|
export const LVM_TOPOLOGY_LABEL = 'topology.lvm.csi/node';
|
||||||
|
|
||||||
const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine';
|
|
||||||
const VOLUME_BINDING_MODE_IMMEDIATE = 'Immediate';
|
const VOLUME_BINDING_MODE_IMMEDIATE = 'Immediate';
|
||||||
const VOLUME_BINDING_MODE_WAIT = 'WaitForFirstConsumer';
|
const VOLUME_BINDING_MODE_WAIT = 'WaitForFirstConsumer';
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default {
|
|||||||
diskSelector: null,
|
diskSelector: null,
|
||||||
nodeSelector: null,
|
nodeSelector: null,
|
||||||
encrypted: 'false',
|
encrypted: 'false',
|
||||||
migratable: this.value.thirdPartyStorageFeatureEnabled ? 'true' : 'false',
|
migratable: 'false',
|
||||||
dataEngine: DATA_ENGINE_V2
|
dataEngine: DATA_ENGINE_V2
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -318,7 +318,7 @@ export default {
|
|||||||
:label="t('harvester.storage.parameters.migratable.label')"
|
:label="t('harvester.storage.parameters.migratable.label')"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:options="migratableOptions"
|
:options="migratableOptions"
|
||||||
:disabled="!value.thirdPartyStorageFeatureEnabled"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-20">
|
<div class="row mt-20">
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ const CLONE = 'clone';
|
|||||||
const DOWNLOAD = 'download';
|
const DOWNLOAD = 'download';
|
||||||
const UPLOAD = 'upload';
|
const UPLOAD = 'upload';
|
||||||
const rawORqcow2 = 'raw_qcow2';
|
const rawORqcow2 = 'raw_qcow2';
|
||||||
const LONGHORN = 'longhorn';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'EditImage',
|
name: 'EditImage',
|
||||||
@@ -62,10 +61,10 @@ export default {
|
|||||||
storageClasses: this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS }),
|
storageClasses: this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS }),
|
||||||
});
|
});
|
||||||
|
|
||||||
this['storageClassName'] = this.storageClassName || this.defaultStorageClassName();
|
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
|
||||||
this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE);
|
|
||||||
|
|
||||||
this.storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
this['storageClassName'] = this.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
|
||||||
|
this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE);
|
||||||
|
|
||||||
const { securityParameters } = this.value.spec;
|
const { securityParameters } = this.value.spec;
|
||||||
|
|
||||||
@@ -101,15 +100,14 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectedImage: null,
|
selectedImage: null,
|
||||||
storageClasses: [],
|
images: [],
|
||||||
images: [],
|
url: this.value.spec.url,
|
||||||
url: this.value.spec.url,
|
files: [],
|
||||||
files: [],
|
resource: '',
|
||||||
resource: '',
|
headers: {},
|
||||||
headers: {},
|
fileUrl: '',
|
||||||
fileUrl: '',
|
file: '',
|
||||||
file: '',
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -146,26 +144,12 @@ export default {
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
encryptedStorageClasses() {
|
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
|
||||||
|
|
||||||
return storages.filter((s) => s.isEncrypted);
|
|
||||||
},
|
|
||||||
|
|
||||||
nonEncryptedStorageClasses() {
|
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
|
||||||
|
|
||||||
return storages.filter((s) => !s.isEncrypted);
|
|
||||||
},
|
|
||||||
|
|
||||||
storageClassOptions() {
|
storageClassOptions() {
|
||||||
const storages = this.value.spec?.securityParameters?.cryptoOperation === ENCRYPT ? this.encryptedStorageClasses : this.nonEncryptedStorageClasses;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
const filteredStorages = this.value.thirdPartyStorageFeatureEnabled ? storages.filter((s) => !s.parameters?.backingImage) : storages
|
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
||||||
.filter((s) => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) ;
|
|
||||||
|
|
||||||
return filteredStorages
|
return storages
|
||||||
|
.filter((s) => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) // Lvm storage is not supported.
|
||||||
.map((s) => {
|
.map((s) => {
|
||||||
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
||||||
|
|
||||||
@@ -183,9 +167,6 @@ export default {
|
|||||||
|
|
||||||
set(nue) {
|
set(nue) {
|
||||||
this.value.metadata.annotations[HCI_ANNOTATIONS.STORAGE_CLASS] = nue;
|
this.value.metadata.annotations[HCI_ANNOTATIONS.STORAGE_CLASS] = nue;
|
||||||
if (this.value.thirdPartyStorageFeatureEnabled) {
|
|
||||||
this.value.spec.targetStorageClassName = nue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sourceImageOptions() {
|
sourceImageOptions() {
|
||||||
@@ -258,20 +239,6 @@ export default {
|
|||||||
this.$refs.file.value = null;
|
this.$refs.file.value = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'value.spec.securityParameters.cryptoOperation'() {
|
|
||||||
if (this.value.spec?.securityParameters?.cryptoOperation === ENCRYPT) {
|
|
||||||
this.storageClassName = this.encryptedStorageClasses[0]?.name || '';
|
|
||||||
} else { // URL / FILE / DECRYPT should use default storage class
|
|
||||||
this.storageClassName = this.defaultStorageClassName();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'storageClassName'(neu) {
|
|
||||||
const storageClass = this.storages.find((s) => s.id === neu);
|
|
||||||
|
|
||||||
if (storageClass && this.value.thirdPartyStorageFeatureEnabled) {
|
|
||||||
this.value.spec.backend = storageClass.isLonghornV1 ? 'backingimage' : 'cdi';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -418,18 +385,6 @@ export default {
|
|||||||
return str.toLowerCase().includes(os.value.toLowerCase()) ? os.value : false;
|
return str.toLowerCase().includes(os.value.toLowerCase()) ? os.value : false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
defaultStorageClassName() {
|
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
||||||
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find((s) => s.isDefault);
|
|
||||||
|
|
||||||
if (!defaultStorage) {
|
|
||||||
return LONGHORN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if default sc is encrypted, use longhorn as default
|
|
||||||
return defaultStorage.isEncrypted ? LONGHORN : defaultStorage?.metadata?.name;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import { Checkbox } from '@components/Form/Checkbox';
|
|||||||
import CruResource from '@shell/components/CruResource';
|
import CruResource from '@shell/components/CruResource';
|
||||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import { Banner } from '@components/Banner';
|
import Labels from '@shell/components/form/Labels';
|
||||||
import KeyValue from '@shell/components/form/KeyValue';
|
import KeyValue from '@shell/components/form/KeyValue';
|
||||||
import NodeScheduling from '@shell/components/form/NodeScheduling';
|
import NodeScheduling from '@shell/components/form/NodeScheduling';
|
||||||
import PodAffinity from '@shell/components/form/PodAffinity';
|
import PodAffinity from '@shell/components/form/PodAffinity';
|
||||||
import UnitInput from '@shell/components/form/UnitInput';
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
|
|
||||||
import { randomStr } from '@shell/utils/string';
|
import { randomStr } from '@shell/utils/string';
|
||||||
import { _CONFIG, _EDIT, _VIEW } from '@shell/config/query-params';
|
import { _CONFIG, _EDIT, _VIEW } from '@shell/config/query-params';
|
||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
@@ -46,7 +47,7 @@ export default {
|
|||||||
PodAffinity,
|
PodAffinity,
|
||||||
Reserved,
|
Reserved,
|
||||||
UnitInput,
|
UnitInput,
|
||||||
Banner,
|
Labels,
|
||||||
KeyValue,
|
KeyValue,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -320,44 +321,34 @@ export default {
|
|||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
name="labels"
|
:name="t('generic.labels')"
|
||||||
:label="t('generic.labels')"
|
:label="t('harvester.tab.instanceLabel')"
|
||||||
:weight="-9"
|
:weight="-5"
|
||||||
>
|
>
|
||||||
<Banner color="info">
|
<Labels
|
||||||
<t k="harvester.virtualMachine.labels.banner" />
|
:default-container-class="'labels-and-annotations-container'"
|
||||||
</Banner>
|
:value="value"
|
||||||
<KeyValue
|
|
||||||
key="labels"
|
|
||||||
:value="value.labels"
|
|
||||||
:add-label="t('labels.addLabel')"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:read-allowed="false"
|
:display-side-by-side="false"
|
||||||
:value-can-be-empty="true"
|
:show-annotations="false"
|
||||||
@update:value="value.setLabels($event)"
|
: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>
|
||||||
|
|
||||||
<Tab
|
|
||||||
name="instanceLabel"
|
|
||||||
:label="t('harvester.tab.instanceLabel')"
|
|
||||||
:weight="-10"
|
|
||||||
>
|
|
||||||
<Banner color="info">
|
|
||||||
<t k="harvester.virtualMachine.instanceLabels.banner" />
|
|
||||||
</Banner>
|
|
||||||
<KeyValue
|
|
||||||
key="instance-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)"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
<Tab
|
<Tab
|
||||||
name="advanced"
|
name="advanced"
|
||||||
:label="t('harvester.tab.advanced')"
|
:label="t('harvester.tab.advanced')"
|
||||||
@@ -443,7 +434,7 @@ export default {
|
|||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-if="value.cpuPinningFeatureEnabled"
|
v-if="value.cpuPinningFeatureEnabled"
|
||||||
v-model:value="cpuPinning"
|
v-model="cpuPinning"
|
||||||
class="check"
|
class="check"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
tooltip-key="harvester.virtualMachine.cpuPinning.tooltip"
|
tooltip-key="harvester.virtualMachine.cpuPinning.tooltip"
|
||||||
@@ -474,29 +465,11 @@ export default {
|
|||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Checkbox
|
|
||||||
v-if="value.tpmPersistentStateFeatureEnabled && tpmEnabled"
|
|
||||||
v-model:value="tpmPersistentStateEnabled"
|
|
||||||
class="check"
|
|
||||||
type="checkbox"
|
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.tpmPersistentState')"
|
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model:value="efiEnabled"
|
v-model:value="efiEnabled"
|
||||||
class="check"
|
class="check"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.efiEnabled')"
|
:label="t('harvester.virtualMachine.efiEnabled')"
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Checkbox
|
|
||||||
v-if="value.efiPersistentStateFeatureEnabled && efiEnabled"
|
|
||||||
v-model:value="efiPersistentStateEnabled"
|
|
||||||
class="check"
|
|
||||||
type="checkbox"
|
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.efiPersistentState')"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -505,7 +478,7 @@ export default {
|
|||||||
v-model:value="secureBoot"
|
v-model:value="secureBoot"
|
||||||
class="check"
|
class="check"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.secureBoot')"
|
:label="t('harvester.virtualMachine.secureBoot')"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import { InterfaceOption, VOLUME_DATA_SOURCE_KIND } from '../config/harvester-ma
|
|||||||
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
||||||
import { LVM_DRIVER } from '../models/harvester/storage.k8s.io.storageclass';
|
import { LVM_DRIVER } from '../models/harvester/storage.k8s.io.storageclass';
|
||||||
import { DATA_ENGINE_V2 } from '../models/harvester/persistentvolumeclaim';
|
import { DATA_ENGINE_V2 } from '../models/harvester/persistentvolumeclaim';
|
||||||
import { GIBIBYTE } from '../utils/unit';
|
|
||||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterVolume',
|
name: 'HarvesterVolume',
|
||||||
@@ -74,8 +72,7 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
if (this.mode === _CREATE) {
|
if (this.mode === _CREATE) {
|
||||||
// default volumeMode to Block
|
this.value.spec.volumeMode = 'Block';
|
||||||
this.value.spec.volumeMode = VOLUME_MODE.BLOCK;
|
|
||||||
this.value.spec.accessModes = ['ReadWriteMany'];
|
this.value.spec.accessModes = ['ReadWriteMany'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +86,6 @@ export default {
|
|||||||
imageId,
|
imageId,
|
||||||
snapshots: [],
|
snapshots: [],
|
||||||
images: [],
|
images: [],
|
||||||
GIBIBYTE
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -128,10 +124,6 @@ export default {
|
|||||||
return InterfaceOption;
|
return InterfaceOption;
|
||||||
},
|
},
|
||||||
|
|
||||||
volumeModeOptions() {
|
|
||||||
return Object.values(VOLUME_MODE);
|
|
||||||
},
|
|
||||||
|
|
||||||
imageOption() {
|
imageOption() {
|
||||||
return sortBy(
|
return sortBy(
|
||||||
this.images
|
this.images
|
||||||
@@ -185,25 +177,6 @@ export default {
|
|||||||
return this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
return this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
|
||||||
},
|
},
|
||||||
|
|
||||||
isLonghornStorageClass() {
|
|
||||||
const selectedSC = this.storageClasses.find((sc) => sc.name === this.value?.spec?.storageClassName) || {};
|
|
||||||
|
|
||||||
return selectedSC && selectedSC.isLonghorn;
|
|
||||||
},
|
|
||||||
|
|
||||||
showVolumeMode() {
|
|
||||||
// we won't let user choose volumeMode if source = vmimage
|
|
||||||
if (!this.value.thirdPartyStorageFeatureEnabled || this.isVMImage || !!this.value?.spec?.storageClassName === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isLonghornStorageClass) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
storageClassOptions() {
|
storageClassOptions() {
|
||||||
return this.storageClasses.filter((s) => !s.parameters?.backingImage).map((s) => {
|
return this.storageClasses.filter((s) => !s.parameters?.backingImage).map((s) => {
|
||||||
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
||||||
@@ -262,43 +235,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
|
||||||
'source'(neu) {
|
|
||||||
if (neu === 'url') {
|
|
||||||
this.setBlockVolumeMode();
|
|
||||||
this.deleteVolumeForVmAnnotation();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'value.spec.storageClassName'() {
|
|
||||||
if (this.isLonghornStorageClass) {
|
|
||||||
this.setBlockVolumeMode();
|
|
||||||
this.deleteVolumeForVmAnnotation();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'value.spec.volumeMode'(neu) {
|
|
||||||
if (neu === VOLUME_MODE.FILE_SYSTEM) {
|
|
||||||
this.setVolumeForVmAnnotation();
|
|
||||||
} else if (neu === VOLUME_MODE.BLOCK ) {
|
|
||||||
this.deleteVolumeForVmAnnotation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setBlockVolumeMode() {
|
|
||||||
this.value.spec.volumeMode = VOLUME_MODE.BLOCK;
|
|
||||||
},
|
|
||||||
|
|
||||||
setVolumeForVmAnnotation() {
|
|
||||||
this.value.setAnnotation(HCI_ANNOTATIONS.VOLUME_FOR_VM, 'true');
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteVolumeForVmAnnotation() {
|
|
||||||
if (this.value?.metadata?.annotations?.[HCI_ANNOTATIONS.VOLUME_FOR_VM]) {
|
|
||||||
delete this.value.metadata.annotations[HCI_ANNOTATIONS.VOLUME_FOR_VM];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getAccessMode() {
|
getAccessMode() {
|
||||||
if (!this.longhornV2LVMSupport) {
|
if (!this.longhornV2LVMSupport) {
|
||||||
return ['ReadWriteMany'];
|
return ['ReadWriteMany'];
|
||||||
@@ -307,10 +244,10 @@ export default {
|
|||||||
const storageClassName = this.value.spec.storageClassName;
|
const storageClassName = this.value.spec.storageClassName;
|
||||||
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
||||||
|
|
||||||
let readWriteOnce = this.value.isLvm || (!this.value.thirdPartyStorageFeatureEnabled && this.value.isLonghornV2);
|
let readWriteOnce = this.value.isLvm || this.value.isLonghornV2;
|
||||||
|
|
||||||
if (storageClass) {
|
if (storageClass) {
|
||||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.value.thirdPartyStorageFeatureEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
readWriteOnce = storageClass.provisioner === LVM_DRIVER || storageClass.parameters?.dataEngine === DATA_ENGINE_V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return readWriteOnce ? ['ReadWriteOnce'] : ['ReadWriteMany'];
|
return readWriteOnce ? ['ReadWriteOnce'] : ['ReadWriteMany'];
|
||||||
@@ -351,7 +288,7 @@ export default {
|
|||||||
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
|
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
|
||||||
|
|
||||||
if (imageSize) {
|
if (imageSize) {
|
||||||
this.storage = `${ Math.ceil(imageSize / 1024 / 1024 / 1024) }${ GIBIBYTE }`;
|
this.storage = `${ Math.ceil(imageSize / 1024 / 1024 / 1024) }Gi`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.update();
|
this.update();
|
||||||
@@ -420,7 +357,7 @@ export default {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-if="isBlank"
|
v-if="source === 'blank'"
|
||||||
v-model:value="value.spec.storageClassName"
|
v-model:value="value.spec.storageClassName"
|
||||||
:options="storageClassOptions"
|
:options="storageClassOptions"
|
||||||
:label="t('harvester.storage.storageClass.label')"
|
:label="t('harvester.storage.storageClass.label')"
|
||||||
@@ -430,18 +367,6 @@ export default {
|
|||||||
@update:value="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LabeledSelect
|
|
||||||
v-if="showVolumeMode"
|
|
||||||
v-model:value="value.spec.volumeMode"
|
|
||||||
:label="t('harvester.volume.volumeMode')"
|
|
||||||
:options="volumeModeOptions"
|
|
||||||
required
|
|
||||||
:disabled="!isCreate"
|
|
||||||
:mode="mode"
|
|
||||||
class="mb-20"
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UnitInput
|
<UnitInput
|
||||||
v-model:value="storage"
|
v-model:value="storage"
|
||||||
:label="t('harvester.volume.size')"
|
:label="t('harvester.volume.size')"
|
||||||
@@ -452,7 +377,6 @@ export default {
|
|||||||
:disabled="value?.isLonghornV2 && isEdit"
|
:disabled="value?.isLonghornV2 && isEdit"
|
||||||
required
|
required
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
:suffix="GIBIBYTE"
|
|
||||||
@update:value="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import UnitInput from '@shell/components/form/UnitInput';
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
import InputOrDisplay from '@shell/components/InputOrDisplay';
|
import InputOrDisplay from '@shell/components/InputOrDisplay';
|
||||||
import { GIBIBYTE } from '../../utils/unit';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditCpuMemory',
|
name: 'HarvesterEditCpuMemory',
|
||||||
@@ -31,7 +30,6 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
GIBIBYTE,
|
|
||||||
localCpu: this.cpu,
|
localCpu: this.cpu,
|
||||||
localMemory: this.memory
|
localMemory: this.memory
|
||||||
};
|
};
|
||||||
@@ -65,7 +63,7 @@ export default {
|
|||||||
if (String(this.localMemory).includes('Gi')) {
|
if (String(this.localMemory).includes('Gi')) {
|
||||||
memory = this.localMemory;
|
memory = this.localMemory;
|
||||||
} else {
|
} else {
|
||||||
memory = `${ this.localMemory }${ GIBIBYTE }`;
|
memory = `${ this.localMemory }Gi`;
|
||||||
}
|
}
|
||||||
if (memory.includes('null')) {
|
if (memory.includes('null')) {
|
||||||
memory = null;
|
memory = null;
|
||||||
@@ -115,7 +113,6 @@ export default {
|
|||||||
:output-modifier="true"
|
:output-modifier="true"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
required
|
required
|
||||||
:suffix="GIBIBYTE"
|
|
||||||
class="mb-20"
|
class="mb-20"
|
||||||
@update:value="change"
|
@update:value="change"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -97,13 +97,11 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const devices = [
|
const devices = [
|
||||||
...this.otherDevices(this.value?.domain?.devices?.hostDevices || []),
|
...this.otherDevices(this.value.domain.devices.hostDevices || []),
|
||||||
...formatted,
|
...formatted,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (devices.length > 0) {
|
set(this.value.domain.devices, 'hostDevices', devices);
|
||||||
set(this.value.domain.devices, 'hostDevices', devices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -123,9 +121,7 @@ export default {
|
|||||||
return inUse;
|
return inUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hostDevices = vm?.hostDevices || [];
|
vm.hostDevices.forEach((device) => {
|
||||||
|
|
||||||
hostDevices.forEach((device) => {
|
|
||||||
inUse[device.name] = { usedBy: [vm.metadata.name] };
|
inUse[device.name] = { usedBy: [vm.metadata.name] };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import UnitInput from '@shell/components/form/UnitInput';
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
import { MEBIBYTE } from '../../utils/unit';
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterReserved',
|
name: 'HarvesterReserved',
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return { MEBIBYTE, memory: this.reservedMemory };
|
return { memory: this.reservedMemory };
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@@ -47,7 +47,6 @@ export default {
|
|||||||
:input-exponent="2"
|
:input-exponent="2"
|
||||||
:increment="1024"
|
:increment="1024"
|
||||||
:output-modifier="true"
|
:output-modifier="true"
|
||||||
:suffix="MEBIBYTE"
|
|
||||||
@update:value="change"
|
@update:value="change"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { randomStr } from '@shell/utils/string';
|
import { randomStr } from '@shell/utils/string';
|
||||||
|
import { clone } from '@shell/utils/object';
|
||||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import ModalWithCard from '@shell/components/ModalWithCard';
|
import ModalWithCard from '@shell/components/ModalWithCard';
|
||||||
|
import { _VIEW } from '@shell/config/query-params';
|
||||||
import { _VIEW, _EDIT } from '@shell/config/query-params';
|
|
||||||
|
|
||||||
import { NAMESPACE } from '@shell/config/types';
|
import { NAMESPACE } from '@shell/config/types';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
|
|
||||||
@@ -90,9 +88,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
sshOption() {
|
sshOption() {
|
||||||
if (this.mode === _VIEW || this.mode === _EDIT) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const out = this.$store.getters['harvester/all'](HCI.SSH).map( (O) => {
|
const out = this.$store.getters['harvester/all'](HCI.SSH).map( (O) => {
|
||||||
return {
|
return {
|
||||||
label: O.id,
|
label: O.id,
|
||||||
@@ -131,6 +126,7 @@ export default {
|
|||||||
checkedSsh(val) {
|
checkedSsh(val) {
|
||||||
// if click on Create a New...
|
// if click on Create a New...
|
||||||
if (val.includes(_NEW)) {
|
if (val.includes(_NEW)) {
|
||||||
|
this.checkedSsh = this.checkedSsh.filter((key) => key !== _NEW);
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,9 +230,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
const sshKeys = this.checkedSsh.filter((key) => key !== _NEW);
|
this.$emit('update:sshKey', clone(this.checkedSsh));
|
||||||
|
|
||||||
this.$emit('update:sshKey', sshKeys);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,13 +80,11 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const devices = [
|
const devices = [
|
||||||
...this.otherDevices(this.value?.domain?.devices?.hostDevices || []),
|
...this.otherDevices(this.value.domain.devices.hostDevices || []),
|
||||||
...formatted,
|
...formatted,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (devices.length > 0) {
|
set(this.value.domain.devices, 'hostDevices', devices);
|
||||||
set(this.value.domain.devices, 'hostDevices', devices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -123,9 +121,7 @@ export default {
|
|||||||
return inUse;
|
return inUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hostDevices = vm?.hostDevices || [];
|
vm.hostDevices.forEach((device) => {
|
||||||
|
|
||||||
hostDevices.forEach((device) => {
|
|
||||||
inUse[device.name] = { usedBy: [vm.metadata.name] };
|
inUse[device.name] = { usedBy: [vm.metadata.name] };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import UnitInput from '@shell/components/form/UnitInput';
|
|||||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import ModalWithCard from '@shell/components/ModalWithCard';
|
import ModalWithCard from '@shell/components/ModalWithCard';
|
||||||
import { PVC, STORAGE_CLASS } from '@shell/config/types';
|
|
||||||
|
import { PVC } from '@shell/config/types';
|
||||||
import { clone } from '@shell/utils/object';
|
import { clone } from '@shell/utils/object';
|
||||||
import { ucFirst, randomStr } from '@shell/utils/string';
|
import { ucFirst, randomStr } from '@shell/utils/string';
|
||||||
import { removeObject } from '@shell/utils/array';
|
import { removeObject } from '@shell/utils/array';
|
||||||
@@ -16,7 +17,6 @@ import { PLUGIN_DEVELOPER, DEV } from '@shell/store/prefs';
|
|||||||
import { SOURCE_TYPE } from '../../../config/harvester-map';
|
import { SOURCE_TYPE } from '../../../config/harvester-map';
|
||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../config/harvester';
|
||||||
import { HCI } from '../../../types';
|
import { HCI } from '../../../types';
|
||||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['update:value'],
|
emits: ['update:value'],
|
||||||
@@ -62,7 +62,7 @@ export default {
|
|||||||
|
|
||||||
customVolumeMode: {
|
customVolumeMode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: VOLUME_MODE.Block
|
default: 'Block'
|
||||||
},
|
},
|
||||||
|
|
||||||
customAccessMode: {
|
customAccessMode: {
|
||||||
@@ -116,12 +116,6 @@ export default {
|
|||||||
return this.mode === _CREATE;
|
return this.mode === _CREATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
defaultStorageClass() {
|
|
||||||
const defaultStorage = this.$store.getters['harvester/all'](STORAGE_CLASS).find((sc) => sc.isDefault);
|
|
||||||
|
|
||||||
return defaultStorage;
|
|
||||||
},
|
|
||||||
|
|
||||||
showVolumeTip() {
|
showVolumeTip() {
|
||||||
const imageName = this.getImageDisplayName(this.rows[0]?.image);
|
const imageName = this.getImageDisplayName(this.rows[0]?.image);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { _CREATE } from '@shell/config/query-params';
|
|||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
import { HCI } from '../../../../types';
|
import { HCI } from '../../../../types';
|
||||||
import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
|
import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
|
||||||
import { GIBIBYTE } from '../../../../utils/unit';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditExisting',
|
name: 'HarvesterEditExisting',
|
||||||
@@ -60,7 +59,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
GIBIBYTE,
|
|
||||||
VOLUME_TYPE,
|
VOLUME_TYPE,
|
||||||
InterfaceOption,
|
InterfaceOption,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -111,10 +109,6 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pvc.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] === 'true') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pvc.attachVM && isAvailable && pvc.attachVM?.id === this.vm?.id && this.isEdit) {
|
if (pvc.attachVM && isAvailable && pvc.attachVM?.id === this.vm?.id && this.isEdit) {
|
||||||
isBeingUsed = false;
|
isBeingUsed = false;
|
||||||
} else if (pvc.attachVM) {
|
} else if (pvc.attachVM) {
|
||||||
@@ -256,7 +250,6 @@ export default {
|
|||||||
:label="t('harvester.fields.size')"
|
:label="t('harvester.fields.size')"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
:suffix="GIBIBYTE"
|
|
||||||
@update:value="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
</InputOrDisplay>
|
</InputOrDisplay>
|
||||||
|
|||||||
+4
-59
@@ -12,7 +12,6 @@ import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
|
|||||||
import { _VIEW } from '@shell/config/query-params';
|
import { _VIEW } from '@shell/config/query-params';
|
||||||
import LabelValue from '@shell/components/LabelValue';
|
import LabelValue from '@shell/components/LabelValue';
|
||||||
import { ucFirst } from '@shell/utils/string';
|
import { ucFirst } from '@shell/utils/string';
|
||||||
import { GIBIBYTE } from '../../../../utils/unit';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditVMImage',
|
name: 'HarvesterEditVMImage',
|
||||||
@@ -68,7 +67,6 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
GIBIBYTE,
|
|
||||||
VOLUME_TYPE,
|
VOLUME_TYPE,
|
||||||
InterfaceOption,
|
InterfaceOption,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -92,7 +90,7 @@ export default {
|
|||||||
imagesOption() {
|
imagesOption() {
|
||||||
return this.images.filter((c) => c.isReady).sort((a, b) => a.creationTimestamp > b.creationTimestamp ? -1 : 1).map( (I) => {
|
return this.images.filter((c) => c.isReady).sort((a, b) => a.creationTimestamp > b.creationTimestamp ? -1 : 1).map( (I) => {
|
||||||
return {
|
return {
|
||||||
label: this.imageOptionLabel(I),
|
label: `${ I.metadata.namespace }/${ I.spec.displayName }`,
|
||||||
value: I.id
|
value: I.id
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -118,46 +116,8 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
thirdPartyStorageEnabled() {
|
|
||||||
return this.$store.getters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
|
||||||
},
|
|
||||||
|
|
||||||
isLonghornV2() {
|
isLonghornV2() {
|
||||||
return this.value.pvc?.isLonghornV2 || this.value.pvc?.storageClass?.isLonghornV2;
|
return this.value.pvc?.isLonghornV2 || this.value.pvc?.storageClass?.isLonghornV2;
|
||||||
},
|
|
||||||
|
|
||||||
selectedImage() {
|
|
||||||
return this.$store.getters['harvester/all'](HCI.IMAGE)?.find( (I) => this.value.image === I.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
imageVirtualSize() {
|
|
||||||
if (this.selectedImage?.virtualSize) {
|
|
||||||
return this.selectedImage.virtualSize.replace(' ', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return '0';
|
|
||||||
},
|
|
||||||
|
|
||||||
diskSize() {
|
|
||||||
const size = this.value?.size || '0';
|
|
||||||
|
|
||||||
return size;
|
|
||||||
},
|
|
||||||
|
|
||||||
imageVirtualSizeInByte() {
|
|
||||||
return Math.max(this.selectedImage?.status?.size, this.selectedImage?.status?.virtualSize);
|
|
||||||
},
|
|
||||||
|
|
||||||
diskSizeInByte() {
|
|
||||||
return parseSi(this.value?.size || '0');
|
|
||||||
},
|
|
||||||
|
|
||||||
showDiskTooSmallError() {
|
|
||||||
if (!this.thirdPartyStorageEnabled ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.imageVirtualSizeInByte > this.diskSizeInByte;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -189,7 +149,7 @@ export default {
|
|||||||
minExponent: 3,
|
minExponent: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.value.size = `${ formatSize }${ GIBIBYTE }`;
|
this.value.size = `${ formatSize }Gi`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
@@ -198,17 +158,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
imageOptionLabel(image) {
|
|
||||||
let label = `${ image.metadata.namespace }/${ image.spec.displayName }`;
|
|
||||||
|
|
||||||
if (this.thirdPartyStorageEnabled) {
|
|
||||||
label += ` (${ image.imageStorageClass } / ${ image.virtualSize })`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return label;
|
|
||||||
},
|
|
||||||
update() {
|
update() {
|
||||||
this.value.hasDiskError = this.showDiskTooSmallError;
|
|
||||||
this.$emit('update');
|
this.$emit('update');
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -231,7 +181,7 @@ export default {
|
|||||||
if (!isIsoImage) {
|
if (!isIsoImage) {
|
||||||
imageSizeGiB = Math.max(imageSizeGiB, 10);
|
imageSizeGiB = Math.max(imageSizeGiB, 10);
|
||||||
}
|
}
|
||||||
this.value['size'] = `${ imageSizeGiB }${ GIBIBYTE }`;
|
this.value['size'] = `${ imageSizeGiB }Gi`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
@@ -338,7 +288,7 @@ export default {
|
|||||||
:mode="mode"
|
:mode="mode"
|
||||||
:required="validateRequired"
|
:required="validateRequired"
|
||||||
:disable="isLonghornV2"
|
:disable="isLonghornV2"
|
||||||
:suffix="GIBIBYTE"
|
suffix="GiB"
|
||||||
@update:value="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
</InputOrDisplay>
|
</InputOrDisplay>
|
||||||
@@ -391,10 +341,5 @@ export default {
|
|||||||
class="mb-20"
|
class="mb-20"
|
||||||
:label="value.volumeBackups.error.message"
|
:label="value.volumeBackups.error.message"
|
||||||
/>
|
/>
|
||||||
<Banner
|
|
||||||
v-if="!isView && showDiskTooSmallError"
|
|
||||||
color="error"
|
|
||||||
:label="t('harvester.virtualMachine.volume.vmImageVolumeTip', {diskSize: diskSize, imageVirtualSize: imageVirtualSize})"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import LabelValue from '@shell/components/LabelValue';
|
|||||||
import { ucFirst } from '@shell/utils/string';
|
import { ucFirst } from '@shell/utils/string';
|
||||||
import { LVM_DRIVER } from '../../../../models/harvester/storage.k8s.io.storageclass';
|
import { LVM_DRIVER } from '../../../../models/harvester/storage.k8s.io.storageclass';
|
||||||
import { DATA_ENGINE_V2 } from '../../../../models/harvester/persistentvolumeclaim';
|
import { DATA_ENGINE_V2 } from '../../../../models/harvester/persistentvolumeclaim';
|
||||||
import { GIBIBYTE } from '../../../../utils/unit';
|
|
||||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditVolume',
|
name: 'HarvesterEditVolume',
|
||||||
@@ -60,7 +58,6 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
GIBIBYTE,
|
|
||||||
VOLUME_TYPE,
|
VOLUME_TYPE,
|
||||||
InterfaceOption,
|
InterfaceOption,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -72,10 +69,6 @@ export default {
|
|||||||
return this.$store.getters['harvester-common/getFeatureEnabled']('longhornV2LVMSupport');
|
return this.$store.getters['harvester-common/getFeatureEnabled']('longhornV2LVMSupport');
|
||||||
},
|
},
|
||||||
|
|
||||||
thirdPartyStorageClassEnabled() {
|
|
||||||
return this.$store.getters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
|
||||||
},
|
|
||||||
|
|
||||||
encryptionValue() {
|
encryptionValue() {
|
||||||
return ucFirst(String(this.value.isEncrypted));
|
return ucFirst(String(this.value.isEncrypted));
|
||||||
},
|
},
|
||||||
@@ -96,18 +89,6 @@ export default {
|
|||||||
return allPVCs.find((P) => P.id === `${ this.namespace }/${ this.value.volumeName }`);
|
return allPVCs.find((P) => P.id === `${ this.namespace }/${ this.value.volumeName }`);
|
||||||
},
|
},
|
||||||
|
|
||||||
showVolumeMode() {
|
|
||||||
if (!this.thirdPartyStorageClassEnabled || !!this.value?.storageClassName === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isLonghornStorageClass) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
isDisabled() {
|
isDisabled() {
|
||||||
return !this.value.newCreateId && this.isEdit && this.isVirtualType;
|
return !this.value.newCreateId && this.isEdit && this.isVirtualType;
|
||||||
},
|
},
|
||||||
@@ -129,17 +110,7 @@ export default {
|
|||||||
|
|
||||||
isLonghornV2() {
|
isLonghornV2() {
|
||||||
return this.value.pvc?.isLonghornV2 || this.value.pvc?.storageClass?.isLonghornV2;
|
return this.value.pvc?.isLonghornV2 || this.value.pvc?.storageClass?.isLonghornV2;
|
||||||
},
|
}
|
||||||
|
|
||||||
isLonghornStorageClass() {
|
|
||||||
const selectedSC = this.storageClasses.find((sc) => sc.name === this.value?.storageClassName) || {};
|
|
||||||
|
|
||||||
return selectedSC && selectedSC.isLonghorn;
|
|
||||||
},
|
|
||||||
|
|
||||||
volumeModeOptions() {
|
|
||||||
return Object.values(VOLUME_MODE);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@@ -147,8 +118,6 @@ export default {
|
|||||||
immediate: true,
|
immediate: true,
|
||||||
handler(neu) {
|
handler(neu) {
|
||||||
this.value.accessMode = this.getAccessMode(neu);
|
this.value.accessMode = this.getAccessMode(neu);
|
||||||
this.value.volumeMode = this.getVolumeMode(neu, this.value.volumeMode);
|
|
||||||
this.update();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -171,7 +140,7 @@ export default {
|
|||||||
minExponent: 3,
|
minExponent: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.value.size = `${ formatSize }${ GIBIBYTE }`;
|
this.value.size = `${ formatSize }Gi`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
@@ -180,20 +149,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
getVolumeMode(storageClassName, originalVolumeMode) {
|
|
||||||
if (!this.thirdPartyStorageClassEnabled) {
|
|
||||||
return VOLUME_MODE.BLOCK;
|
|
||||||
}
|
|
||||||
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
|
||||||
|
|
||||||
// longhorn v1, v2 use block volumeMode
|
|
||||||
if (storageClass && storageClass.isLonghorn) {
|
|
||||||
return VOLUME_MODE.BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return originalVolumeMode;
|
|
||||||
},
|
|
||||||
|
|
||||||
getAccessMode(storageClassName) {
|
getAccessMode(storageClassName) {
|
||||||
if (!this.longhornV2LVMSupport) {
|
if (!this.longhornV2LVMSupport) {
|
||||||
return 'ReadWriteMany';
|
return 'ReadWriteMany';
|
||||||
@@ -201,10 +156,10 @@ export default {
|
|||||||
|
|
||||||
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
||||||
|
|
||||||
let readWriteOnce = this.value.pvc?.isLvm || (!this.thirdPartyStorageClassEnabled && this.value.pvc?.isLonghornV2);
|
let readWriteOnce = this.value.pvc?.isLvm || this.value.pvc?.isLonghornV2;
|
||||||
|
|
||||||
if (storageClass) {
|
if (storageClass) {
|
||||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.thirdPartyStorageClassEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
readWriteOnce = storageClass.provisioner === LVM_DRIVER || storageClass.parameters?.dataEngine === DATA_ENGINE_V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return readWriteOnce ? 'ReadWriteOnce' : 'ReadWriteMany';
|
return readWriteOnce ? 'ReadWriteOnce' : 'ReadWriteMany';
|
||||||
@@ -303,7 +258,6 @@ export default {
|
|||||||
:required="validateRequired"
|
:required="validateRequired"
|
||||||
:label="t('harvester.fields.size')"
|
:label="t('harvester.fields.size')"
|
||||||
:disabled="isLonghornV2"
|
:disabled="isLonghornV2"
|
||||||
:suffix="GIBIBYTE"
|
|
||||||
@update:value="update"
|
@update:value="update"
|
||||||
/>
|
/>
|
||||||
</InputOrDisplay>
|
</InputOrDisplay>
|
||||||
@@ -329,29 +283,6 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</InputOrDisplay>
|
</InputOrDisplay>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="showVolumeMode"
|
|
||||||
data-testid="input-volume-mode"
|
|
||||||
class="col span-6"
|
|
||||||
>
|
|
||||||
<InputOrDisplay
|
|
||||||
:name="t('harvester.volume.volumeMode')"
|
|
||||||
:value="value.volumeMode"
|
|
||||||
:mode="mode"
|
|
||||||
>
|
|
||||||
<LabeledSelect
|
|
||||||
v-model:value="value.volumeMode"
|
|
||||||
:label="t('harvester.volume.volumeMode')"
|
|
||||||
:mode="mode"
|
|
||||||
:options="volumeModeOptions"
|
|
||||||
:disabled="isEdit"
|
|
||||||
required
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
</InputOrDisplay>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-20">
|
|
||||||
<div
|
<div
|
||||||
v-if="value.volumeEncryptionFeatureEnabled && isView"
|
v-if="value.volumeEncryptionFeatureEnabled && isView"
|
||||||
class="col span-6"
|
class="col span-6"
|
||||||
@@ -361,9 +292,11 @@ export default {
|
|||||||
:value="encryptionValue"
|
:value="encryptionValue"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-20">
|
||||||
<div
|
<div
|
||||||
v-if="value.volumeBackups && isView"
|
v-if="value.volumeBackups && isView"
|
||||||
class="col span-6"
|
class="col span-3"
|
||||||
>
|
>
|
||||||
<LabelValue
|
<LabelValue
|
||||||
:name="t('harvester.virtualMachine.volume.readyToUse')"
|
:name="t('harvester.virtualMachine.volume.readyToUse')"
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import Tabbed from '@shell/components/Tabbed';
|
import Tabbed from '@shell/components/Tabbed';
|
||||||
import { clone } from '@shell/utils/object';
|
|
||||||
import Tab from '@shell/components/Tabbed/Tab';
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
import CruResource from '@shell/components/CruResource';
|
import CruResource from '@shell/components/CruResource';
|
||||||
@@ -11,6 +10,8 @@ import { LabeledInput } from '@components/Form/LabeledInput';
|
|||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
import UnitInput from '@shell/components/form/UnitInput';
|
import UnitInput from '@shell/components/form/UnitInput';
|
||||||
|
import Labels from '@shell/components/form/Labels';
|
||||||
|
|
||||||
import NodeScheduling from '@shell/components/form/NodeScheduling';
|
import NodeScheduling from '@shell/components/form/NodeScheduling';
|
||||||
import PodAffinity from '@shell/components/form/PodAffinity';
|
import PodAffinity from '@shell/components/form/PodAffinity';
|
||||||
import VGpuDevices from './VirtualMachineVGpuDevices/index';
|
import VGpuDevices from './VirtualMachineVGpuDevices/index';
|
||||||
@@ -18,6 +19,7 @@ import UsbDevices from './VirtualMachineUSBDevices/index';
|
|||||||
import KeyValue from '@shell/components/form/KeyValue';
|
import KeyValue from '@shell/components/form/KeyValue';
|
||||||
|
|
||||||
import { clear } from '@shell/utils/array';
|
import { clear } from '@shell/utils/array';
|
||||||
|
import { clone } from '@shell/utils/object';
|
||||||
import { saferDump } from '@shell/utils/create-yaml';
|
import { saferDump } from '@shell/utils/create-yaml';
|
||||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
@@ -63,6 +65,7 @@ export default {
|
|||||||
PodAffinity,
|
PodAffinity,
|
||||||
AccessCredentials,
|
AccessCredentials,
|
||||||
Reserved,
|
Reserved,
|
||||||
|
Labels,
|
||||||
PciDevices,
|
PciDevices,
|
||||||
RestartVMDialog,
|
RestartVMDialog,
|
||||||
UnitInput,
|
UnitInput,
|
||||||
@@ -192,12 +195,7 @@ export default {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
isValidationPassed() {
|
|
||||||
// check if any disk hasDiskError is true
|
|
||||||
const hasError = this.diskRows.some((disk) => disk.hasDiskError === true);
|
|
||||||
|
|
||||||
return !hasError;
|
|
||||||
},
|
|
||||||
showCpuPinningBanner() {
|
showCpuPinningBanner() {
|
||||||
if (!this.value.cpuPinningFeatureEnabled) {
|
if (!this.value.cpuPinningFeatureEnabled) {
|
||||||
return false;
|
return false;
|
||||||
@@ -212,10 +210,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
usbPassthroughEnabled() {
|
|
||||||
return this.$store.getters['harvester-common/getFeatureEnabled']('usbPassthrough');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@@ -413,16 +408,12 @@ export default {
|
|||||||
const cloneDeepNewVM = clone(this.value);
|
const cloneDeepNewVM = clone(this.value);
|
||||||
|
|
||||||
delete cloneDeepNewVM?.metadata;
|
delete cloneDeepNewVM?.metadata;
|
||||||
delete cloneDeepNewVM?.__clone;
|
|
||||||
|
|
||||||
delete this.cloneVM?.metadata;
|
delete this.cloneVM?.metadata;
|
||||||
delete this.cloneVM?.__clone;
|
delete this.cloneVM?.__clone;
|
||||||
|
|
||||||
const oldVM = JSON.parse(JSON.stringify(this.cloneVM));
|
const oldVM = JSON.parse(JSON.stringify(this.cloneVM));
|
||||||
const newVM = JSON.parse(JSON.stringify(cloneDeepNewVM));
|
const newVM = JSON.parse(JSON.stringify(cloneDeepNewVM));
|
||||||
|
|
||||||
// we won't show restart dialog in yaml page as we don't have a way to detect change in yaml editor.
|
|
||||||
// only check VM is changed in form page.
|
|
||||||
if (isEqual(oldVM, newVM)) {
|
if (isEqual(oldVM, newVM)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -431,11 +422,7 @@ export default {
|
|||||||
this.isOpen = true;
|
this.isOpen = true;
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this?.$refs?.restartDialog) {
|
this.$refs.restartDialog.resolve = resolve;
|
||||||
this.$refs.restartDialog.resolve = resolve;
|
|
||||||
} else {
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -502,7 +489,6 @@ export default {
|
|||||||
:resource="value"
|
:resource="value"
|
||||||
:cancel-event="true"
|
:cancel-event="true"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:validation-passed="isValidationPassed"
|
|
||||||
:can-yaml="isSingle ? true : false"
|
:can-yaml="isSingle ? true : false"
|
||||||
:errors="errors"
|
:errors="errors"
|
||||||
:generate-yaml="generateYaml"
|
:generate-yaml="generateYaml"
|
||||||
@@ -685,7 +671,7 @@ export default {
|
|||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
v-if="enabledPCI && usbPassthroughEnabled"
|
v-if="enabledPCI"
|
||||||
:label="t('harvester.tab.usbDevices')"
|
:label="t('harvester.tab.usbDevices')"
|
||||||
name="usbDevices"
|
name="usbDevices"
|
||||||
:weight="-7"
|
:weight="-7"
|
||||||
@@ -711,50 +697,39 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
|
||||||
name="labels"
|
|
||||||
:label="t('generic.labels')"
|
|
||||||
:weight="-9"
|
|
||||||
>
|
|
||||||
<Banner color="info">
|
|
||||||
<t k="harvester.virtualMachine.labels.banner" />
|
|
||||||
</Banner>
|
|
||||||
<KeyValue
|
|
||||||
key="labels"
|
|
||||||
:value="value.labels"
|
|
||||||
:add-label="t('labels.addLabel')"
|
|
||||||
:mode="mode"
|
|
||||||
:read-allowed="false"
|
|
||||||
:value-can-be-empty="true"
|
|
||||||
@update:value="value.setLabels($event)"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
name="instanceLabel"
|
name="instanceLabel"
|
||||||
:label="t('harvester.tab.instanceLabel')"
|
:label="t('harvester.tab.instanceLabel')"
|
||||||
:weight="-10"
|
:weight="-8"
|
||||||
>
|
>
|
||||||
<Banner color="info">
|
<Labels
|
||||||
<t k="harvester.virtualMachine.instanceLabels.banner" />
|
:default-container-class="'labels-and-annotations-container'"
|
||||||
</Banner>
|
:value="value"
|
||||||
<KeyValue
|
|
||||||
key="instance-labels"
|
|
||||||
:value="value.instanceLabels"
|
|
||||||
:protected-keys="value.systemLabels || []"
|
|
||||||
:toggle-filter="toggler"
|
|
||||||
:add-label="t('labels.addLabel')"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:read-allowed="false"
|
:display-side-by-side="false"
|
||||||
:value-can-be-empty="true"
|
:show-annotations="false"
|
||||||
@update:value="value.setInstanceLabels($event)"
|
:show-label-title="false"
|
||||||
/>
|
>
|
||||||
|
<template #labels="{toggler}">
|
||||||
|
<KeyValue
|
||||||
|
key="labels"
|
||||||
|
:value="value.instanceLabels"
|
||||||
|
:protected-keys="value.systemLabels || []"
|
||||||
|
:toggle-filter="toggler"
|
||||||
|
:add-label="t('labels.addLabel')"
|
||||||
|
:mode="mode"
|
||||||
|
:read-allowed="false"
|
||||||
|
:value-can-be-empty="true"
|
||||||
|
@update:value="value.setInstanceLabels($event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Labels>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
name="advanced"
|
name="advanced"
|
||||||
:label="t('harvester.tab.advanced')"
|
:label="t('harvester.tab.advanced')"
|
||||||
:weight="-11"
|
:weight="-9"
|
||||||
>
|
>
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
@@ -898,29 +873,11 @@ export default {
|
|||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Checkbox
|
|
||||||
v-if="value.tpmPersistentStateFeatureEnabled && tpmEnabled"
|
|
||||||
v-model:value="tpmPersistentStateEnabled"
|
|
||||||
class="check"
|
|
||||||
type="checkbox"
|
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.tpmPersistentState')"
|
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model:value="efiEnabled"
|
v-model:value="efiEnabled"
|
||||||
class="check"
|
class="check"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.efiEnabled')"
|
:label="t('harvester.virtualMachine.efiEnabled')"
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Checkbox
|
|
||||||
v-if="value.efiPersistentStateFeatureEnabled && efiEnabled"
|
|
||||||
v-model:value="efiPersistentStateEnabled"
|
|
||||||
class="check"
|
|
||||||
type="checkbox"
|
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.efiPersistentState')"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -929,10 +886,9 @@ export default {
|
|||||||
v-model:value="secureBoot"
|
v-model:value="secureBoot"
|
||||||
class="check"
|
class="check"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:label="t('harvester.virtualMachine.advancedOptions.secureBoot')"
|
:label="t('harvester.virtualMachine.secureBoot')"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Banner
|
<Banner
|
||||||
v-if="showCpuPinningBanner"
|
v-if="showCpuPinningBanner"
|
||||||
color="warning"
|
color="warning"
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default {
|
|||||||
:labels="[t('generic.disabled'),t('generic.enabled')]"
|
:labels="[t('generic.disabled'),t('generic.enabled')]"
|
||||||
:options="[false, true]"
|
:options="[false, true]"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@input="onToggle"
|
@update:value="onToggle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import ConsumptionGauge from '@shell/components/ConsumptionGauge';
|
import ConsumptionGauge from '@shell/components/ConsumptionGauge';
|
||||||
import { METRIC, NODE } from '@shell/config/types';
|
import { METRIC, NODE } from '@shell/config/types';
|
||||||
import { formatSi, exponentNeeded, UNITS, parseSi } from '@shell/utils/units';
|
import { formatSi, exponentNeeded, UNITS, parseSi } from '@shell/utils/units';
|
||||||
import { UNIT_SUFFIX } from '../utils/unit';
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterMemoryUsed',
|
name: 'HarvesterMemoryUsed',
|
||||||
components: { ConsumptionGauge },
|
components: { ConsumptionGauge },
|
||||||
@@ -51,7 +51,7 @@ export default {
|
|||||||
memoryUnits() {
|
memoryUnits() {
|
||||||
const exponent = exponentNeeded(this.memoryTotal, 1024);
|
const exponent = exponentNeeded(this.memoryTotal, 1024);
|
||||||
|
|
||||||
return `${ UNITS[exponent] }${ UNIT_SUFFIX }`;
|
return `${ UNITS[exponent] }iB`;
|
||||||
},
|
},
|
||||||
|
|
||||||
node() {
|
node() {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import ConsumptionGauge from '@shell/components/ConsumptionGauge';
|
import ConsumptionGauge from '@shell/components/ConsumptionGauge';
|
||||||
import { LONGHORN } from '@shell/config/types';
|
import { LONGHORN } from '@shell/config/types';
|
||||||
import { formatSi, exponentNeeded, UNITS } from '@shell/utils/units';
|
import { formatSi, exponentNeeded, UNITS } from '@shell/utils/units';
|
||||||
import { UNIT_SUFFIX } from '../utils/unit';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterStorageUsed',
|
name: 'HarvesterStorageUsed',
|
||||||
@@ -76,13 +75,13 @@ export default {
|
|||||||
allocatedUnits() {
|
allocatedUnits() {
|
||||||
const exponent = exponentNeeded(this.storageStats.total, 1024);
|
const exponent = exponentNeeded(this.storageStats.total, 1024);
|
||||||
|
|
||||||
return `${ UNITS[exponent] }${ UNIT_SUFFIX }`;
|
return `${ UNITS[exponent] }iB`;
|
||||||
},
|
},
|
||||||
|
|
||||||
usedUnits() {
|
usedUnits() {
|
||||||
const exponent = exponentNeeded(this.storageStats.maximum, 1024);
|
const exponent = exponentNeeded(this.storageStats.maximum, 1024);
|
||||||
|
|
||||||
return `${ UNITS[exponent] }${ UNIT_SUFFIX }`;
|
return `${ UNITS[exponent] }iB`;
|
||||||
},
|
},
|
||||||
|
|
||||||
formatUsed() {
|
formatUsed() {
|
||||||
|
|||||||
@@ -38,21 +38,6 @@ asyncButton:
|
|||||||
success: Restarted
|
success: Restarted
|
||||||
waiting: Restarting…
|
waiting: Restarting…
|
||||||
|
|
||||||
dialog:
|
|
||||||
confirmExecution:
|
|
||||||
title: Are you sure?
|
|
||||||
andOthers: |-
|
|
||||||
{count, plural,
|
|
||||||
=0 {}
|
|
||||||
=1 { and <b>one other </b>}
|
|
||||||
other { and <b>{count} other </b>}
|
|
||||||
}
|
|
||||||
protip: "Tip: Hold the {alternateLabel} key while clicking action to bypass this confirmation"
|
|
||||||
stop:
|
|
||||||
message: "Are you sure you want to continue stop the {type} {names}?"
|
|
||||||
pause:
|
|
||||||
message: "Are you sure you want to continue pause the {type} {names}?"
|
|
||||||
|
|
||||||
harvester:
|
harvester:
|
||||||
branding:
|
branding:
|
||||||
logos:
|
logos:
|
||||||
@@ -99,11 +84,6 @@ harvester:
|
|||||||
tip: Please enter a virtual machine name!
|
tip: Please enter a virtual machine name!
|
||||||
success: 'Virtual machine { name } cloned successfully.'
|
success: 'Virtual machine { name } cloned successfully.'
|
||||||
failed: 'Failed clone virtual machine!'
|
failed: 'Failed clone virtual machine!'
|
||||||
downloadImage:
|
|
||||||
title: Download Image
|
|
||||||
banner: 'This action takes a while depending on the image size ({ size }). Please be patient.'
|
|
||||||
startMessage : 'The download process will auto start once the conversion is complete.'
|
|
||||||
download: Download
|
|
||||||
exportImage:
|
exportImage:
|
||||||
title: Export to Image
|
title: Export to Image
|
||||||
name: Name
|
name: Name
|
||||||
@@ -604,17 +584,15 @@ harvester:
|
|||||||
enableUsb: Enable USB Tablet
|
enableUsb: Enable USB Tablet
|
||||||
advancedOptions:
|
advancedOptions:
|
||||||
tpm: Enable TPM
|
tpm: Enable TPM
|
||||||
tpmPersistentState: TPM Persistent State
|
|
||||||
cpuManager:
|
cpuManager:
|
||||||
prefix: You must enable CPU Manager for at least one node in
|
prefix: You must enable CPU Manager for at least one node in
|
||||||
middle: 'host page'
|
middle: 'host page'
|
||||||
suffix: to enable CPU Pinning for VM
|
suffix: to enable CPU Pinning for VM
|
||||||
efiEnabled: Booting in EFI mode
|
|
||||||
efiPersistentState: EFI Persistent State
|
|
||||||
secureBoot: Secure Boot
|
|
||||||
usbTip: Provides an absolute pointer device which often helps with getting a consistent mouse cursor position in VNC.
|
usbTip: Provides an absolute pointer device which often helps with getting a consistent mouse cursor position in VNC.
|
||||||
sshTitle: Add Public SSH Key
|
sshTitle: Add Public SSH Key
|
||||||
imageTip: An external URL to the .iso, .img, .qcow2 or .raw that the virtual machine should be created from.
|
imageTip: An external URL to the .iso, .img, .qcow2 or .raw that the virtual machine should be created from.
|
||||||
|
efiEnabled: Booting in EFI mode
|
||||||
|
secureBoot: Secure Boot
|
||||||
volume:
|
volume:
|
||||||
dragTip: Drag and drop volumes, or use the volume's arrows, to change the boot order.
|
dragTip: Drag and drop volumes, or use the volume's arrows, to change the boot order.
|
||||||
volumeTip: The virtual machine 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.
|
||||||
@@ -635,7 +613,6 @@ harvester:
|
|||||||
setFirst: Set as root volume
|
setFirst: Set as root volume
|
||||||
saveVolume: Update Volume
|
saveVolume: Update Volume
|
||||||
encryption: Encryption
|
encryption: Encryption
|
||||||
vmImageVolumeTip: Disk size ({diskSize}) should greater than selected image virtual size ({imageVirtualSize})
|
|
||||||
lockTooltip:
|
lockTooltip:
|
||||||
all: All volumes are encrypted.
|
all: All volumes are encrypted.
|
||||||
partial: Some volumes are encrypted.
|
partial: Some volumes are encrypted.
|
||||||
@@ -778,10 +755,6 @@ harvester:
|
|||||||
inNamespaces: "Workloads in these namespaces"
|
inNamespaces: "Workloads in these namespaces"
|
||||||
namespaces:
|
namespaces:
|
||||||
label: Namespaces
|
label: Namespaces
|
||||||
instanceLabels:
|
|
||||||
banner: These labels are automatically synchronized to the virtual machine instance.
|
|
||||||
labels:
|
|
||||||
banner: These key values are added as labels to the virtual machine.
|
|
||||||
|
|
||||||
volume:
|
volume:
|
||||||
label: Volumes
|
label: Volumes
|
||||||
@@ -791,7 +764,6 @@ harvester:
|
|||||||
datasource: Data Source
|
datasource: Data Source
|
||||||
details: Details
|
details: Details
|
||||||
size: Size
|
size: Size
|
||||||
volumeMode: Volume Mode
|
|
||||||
source: Source
|
source: Source
|
||||||
kind: Kind
|
kind: Kind
|
||||||
sourceOptions:
|
sourceOptions:
|
||||||
@@ -866,7 +838,6 @@ harvester:
|
|||||||
currentVersion: CURRENT VERSION
|
currentVersion: CURRENT VERSION
|
||||||
versionLabel: AVAILABLE COMPATIBLE VERSIONS
|
versionLabel: AVAILABLE COMPATIBLE VERSIONS
|
||||||
enableLogging: Enable Logging
|
enableLogging: Enable Logging
|
||||||
skipSingleReplicaDetachedVol: Skip checking single-replica detached volumes
|
|
||||||
upgradeNode: Upgrading Node
|
upgradeNode: Upgrading Node
|
||||||
upgradeSysService: Upgrading System Service
|
upgradeSysService: Upgrading System Service
|
||||||
upgradeImage: Download Upgrade Image
|
upgradeImage: Download Upgrade Image
|
||||||
@@ -895,14 +866,14 @@ harvester:
|
|||||||
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.
|
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.
|
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 -
|
moreNotes: For more details about the release notes, please visit -
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
label: Virtual Machine Schedules
|
label: Virtual Machine Schedules
|
||||||
createTitle: Create Schedule
|
createTitle: Create Schedule
|
||||||
createButtonText: Create Schedule
|
createButtonText: Create Schedule
|
||||||
scheduleType: Virtual Machine Schedule Type
|
scheduleType: Virtual Machine Schedule Type
|
||||||
cron: Cron Schedule
|
cron: Cron Schedule
|
||||||
detail:
|
detail:
|
||||||
namespace: Namespace
|
namespace: Namespace
|
||||||
sourceVM: Source Virtual Machine
|
sourceVM: Source Virtual Machine
|
||||||
tabs:
|
tabs:
|
||||||
@@ -912,12 +883,12 @@ harvester:
|
|||||||
message:
|
message:
|
||||||
noSetting:
|
noSetting:
|
||||||
suffix: before creating a backup schedule
|
suffix: before creating a backup schedule
|
||||||
retain:
|
retain:
|
||||||
label: Retain
|
label: Retain
|
||||||
count: Count
|
count: Count
|
||||||
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
|
tooltip: Number of up-to-date VM backups to retain. Maximum to 250, minimum to 2.
|
||||||
maxFailure:
|
maxFailure:
|
||||||
label: Max Failure
|
label: Max Failure
|
||||||
count: Count
|
count: Count
|
||||||
tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, Harvester controller will suspend the schedule job. This value should less than retain count
|
tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, Harvester controller will suspend the schedule job. This value should less than retain count
|
||||||
virtualMachine:
|
virtualMachine:
|
||||||
@@ -1043,7 +1014,7 @@ harvester:
|
|||||||
invalid: '"Exclude list" is invalid.'
|
invalid: '"Exclude list" is invalid.'
|
||||||
addIp: Add Exclude IP
|
addIp: Add Exclude IP
|
||||||
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.'
|
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 * 2 + 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>.'
|
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:
|
vmForceDeletionPolicy:
|
||||||
period: Period
|
period: Period
|
||||||
ratio : Ratio
|
ratio : Ratio
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import Banner from '@components/Banner/Banner.vue';
|
|||||||
import MessageLink from '@shell/components/MessageLink';
|
import MessageLink from '@shell/components/MessageLink';
|
||||||
import SortableTable from '@shell/components/SortableTable';
|
import SortableTable from '@shell/components/SortableTable';
|
||||||
import { allHash, setPromiseResult } from '@shell/utils/promise';
|
import { allHash, setPromiseResult } from '@shell/utils/promise';
|
||||||
import { parseSi, formatSi, exponentNeeded, UNITS } from '@shell/utils/units';
|
import {
|
||||||
|
parseSi, formatSi, exponentNeeded, UNITS, createMemoryValues
|
||||||
|
} from '@shell/utils/units';
|
||||||
import { REASON } from '@shell/config/table-headers';
|
import { REASON } from '@shell/config/table-headers';
|
||||||
import {
|
import {
|
||||||
EVENT, METRIC, NODE, SERVICE, PVC, LONGHORN, POD, COUNT, NETWORK_ATTACHMENT
|
EVENT, METRIC, NODE, SERVICE, PVC, LONGHORN, POD, COUNT, NETWORK_ATTACHMENT
|
||||||
@@ -25,21 +27,22 @@ import { isEmpty } from '@shell/utils/object';
|
|||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import HarvesterUpgrade from '../components/HarvesterUpgrade';
|
import HarvesterUpgrade from '../components/HarvesterUpgrade';
|
||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
||||||
import { UNIT_SUFFIX } from '../utils/unit';
|
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(minMax);
|
dayjs.extend(minMax);
|
||||||
|
|
||||||
const PARSE_RULES = {
|
const PARSE_RULES = {
|
||||||
format: {
|
memory: {
|
||||||
addSuffix: true,
|
format: {
|
||||||
firstSuffix: UNIT_SUFFIX,
|
addSuffix: true,
|
||||||
increment: 1024,
|
firstSuffix: 'B',
|
||||||
maxExponent: 99,
|
increment: 1024,
|
||||||
maxPrecision: 2,
|
maxExponent: 99,
|
||||||
minExponent: 0,
|
maxPrecision: 2,
|
||||||
startingExponent: 0,
|
minExponent: 0,
|
||||||
suffix: UNIT_SUFFIX,
|
startingExponent: 0,
|
||||||
|
suffix: 'iB',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -399,13 +402,13 @@ export default {
|
|||||||
storageUsed() {
|
storageUsed() {
|
||||||
const stats = this.storageStats;
|
const stats = this.storageStats;
|
||||||
|
|
||||||
return this.createDisplayValues(stats.maximum, stats.used);
|
return this.createMemoryValues(stats.maximum, stats.used);
|
||||||
},
|
},
|
||||||
|
|
||||||
storageAllocated() {
|
storageAllocated() {
|
||||||
const stats = this.storageStats;
|
const stats = this.storageStats;
|
||||||
|
|
||||||
return this.createDisplayValues(stats.total, stats.scheduled);
|
return this.createMemoryValues(stats.total, stats.scheduled);
|
||||||
},
|
},
|
||||||
|
|
||||||
vmEvents() {
|
vmEvents() {
|
||||||
@@ -451,7 +454,7 @@ export default {
|
|||||||
return total + node.memoryReserved;
|
return total + node.memoryReserved;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return this.createDisplayValues(this.memoryTotal, useful);
|
return createMemoryValues(this.memoryTotal, useful);
|
||||||
},
|
},
|
||||||
|
|
||||||
availableNodes() {
|
availableNodes() {
|
||||||
@@ -491,7 +494,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ramUsed() {
|
ramUsed() {
|
||||||
return this.createDisplayValues(this.memoryTotal, this.metricAggregations?.memory);
|
return createMemoryValues(this.memoryTotal, this.metricAggregations?.memory);
|
||||||
},
|
},
|
||||||
|
|
||||||
hasMetricNodeSchema() {
|
hasMetricNodeSchema() {
|
||||||
@@ -513,12 +516,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
createDisplayValues(total, useful) {
|
createMemoryValues(total, useful) {
|
||||||
const parsedTotal = parseSi((total || '0').toString());
|
const parsedTotal = parseSi((total || '0').toString());
|
||||||
|
|
||||||
const parsedUseful = parseSi((useful || '0').toString());
|
const parsedUseful = parseSi((useful || '0').toString());
|
||||||
const format = this.createFormat(parsedTotal);
|
const format = this.createMemoryFormat(parsedTotal);
|
||||||
|
|
||||||
const formattedTotal = formatSi(parsedTotal, format);
|
const formattedTotal = formatSi(parsedTotal, format);
|
||||||
let formattedUseful = formatSi(parsedUseful, {
|
let formattedUseful = formatSi(parsedUseful, {
|
||||||
...format,
|
...format,
|
||||||
@@ -537,24 +538,24 @@ export default {
|
|||||||
useful: Number(parsedUseful),
|
useful: Number(parsedUseful),
|
||||||
formattedTotal,
|
formattedTotal,
|
||||||
formattedUseful,
|
formattedUseful,
|
||||||
units: this.createUnits(parsedTotal),
|
units: this.createMemoryUnits(parsedTotal),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
createFormat(n) {
|
createMemoryFormat(n) {
|
||||||
const exponent = exponentNeeded(n, PARSE_RULES.format.increment);
|
const exponent = exponentNeeded(n, PARSE_RULES.memory.format.increment);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...PARSE_RULES.format,
|
...PARSE_RULES.memory.format,
|
||||||
maxExponent: exponent,
|
maxExponent: exponent,
|
||||||
minExponent: exponent,
|
minExponent: exponent,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
createUnits(n) {
|
createMemoryUnits(n) {
|
||||||
const exponent = exponentNeeded(n, PARSE_RULES.format.increment);
|
const exponent = exponentNeeded(n, PARSE_RULES.memory.format.increment);
|
||||||
|
|
||||||
return `${ UNITS[exponent] }${ PARSE_RULES.format.suffix }`;
|
return `${ UNITS[exponent] }${ PARSE_RULES.memory.format.suffix }`;
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchClusterResources(type, opt = {}, store) {
|
async fetchClusterResources(type, opt = {}, store) {
|
||||||
|
|||||||
@@ -101,14 +101,14 @@ export default {
|
|||||||
const metricCol = [
|
const metricCol = [
|
||||||
{
|
{
|
||||||
name: 'cpu',
|
name: 'cpu',
|
||||||
labelKey: 'harvester.dashboard.hardwareResourceGauge.cpu',
|
labelKey: 'node.detail.glance.consumptionGauge.cpu',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
formatter: 'HarvesterCPUUsed',
|
formatter: 'HarvesterCPUUsed',
|
||||||
formatterOpts: { showUsed: true },
|
formatterOpts: { showUsed: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'memory',
|
name: 'memory',
|
||||||
labelKey: 'harvester.dashboard.hardwareResourceGauge.memory',
|
labelKey: 'node.detail.glance.consumptionGauge.memory',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
formatter: 'HarvesterMemoryUsed',
|
formatter: 'HarvesterMemoryUsed',
|
||||||
formatterOpts: { showUsed: true },
|
formatterOpts: { showUsed: true },
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import HarvesterVolumeState from '../formatters/HarvesterVolumeState';
|
|||||||
|
|
||||||
import { allSettled } from '../utils/promise';
|
import { allSettled } from '../utils/promise';
|
||||||
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
id: HCI.VOLUME,
|
id: HCI.VOLUME,
|
||||||
@@ -59,6 +58,7 @@ export default {
|
|||||||
if (!pvcSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
|
if (!pvcSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
|
||||||
this.$store.dispatch('type-map/configureType', { match: HCI.VOLUME, isCreatable: false });
|
this.$store.dispatch('type-map/configureType', { match: HCI.VOLUME, isCreatable: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rows = hash.pvcs;
|
this.rows = hash.pvcs;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -70,10 +70,7 @@ export default {
|
|||||||
schema() {
|
schema() {
|
||||||
return schema;
|
return schema;
|
||||||
},
|
},
|
||||||
filterRows() {
|
|
||||||
// we only show the non golden image PVCs in the list
|
|
||||||
return this.rows.filter((pvc) => pvc?.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] !== 'true');
|
|
||||||
},
|
|
||||||
headers() {
|
headers() {
|
||||||
return [
|
return [
|
||||||
STATE,
|
STATE,
|
||||||
@@ -95,8 +92,7 @@ export default {
|
|||||||
{
|
{
|
||||||
name: 'storageClass',
|
name: 'storageClass',
|
||||||
labelKey: 'tableHeaders.storageClass',
|
labelKey: 'tableHeaders.storageClass',
|
||||||
value: 'spec.storageClassName',
|
value: 'spec.storageClassName'
|
||||||
sort: 'spec.storageClassName'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'AttachedVM',
|
name: 'AttachedVM',
|
||||||
@@ -150,7 +146,7 @@ export default {
|
|||||||
:groupable="true"
|
:groupable="true"
|
||||||
default-sort-by="age"
|
default-sort-by="age"
|
||||||
:namespaced="true"
|
:namespaced="true"
|
||||||
:rows="filterRows"
|
:rows="rows"
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
key-field="_key"
|
key-field="_key"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -137,15 +137,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isTpmEnabled(spec) {
|
isTpmEnabled(spec) {
|
||||||
return !!spec?.template?.spec?.domain?.devices?.tpm;
|
return !!spec?.template?.spec?.domain?.devices?.tpm ;
|
||||||
},
|
|
||||||
|
|
||||||
isTPMPersistentStateEnabled(spec) {
|
|
||||||
return !!spec?.template?.spec?.domain?.devices?.tpm?.persistent;
|
|
||||||
},
|
|
||||||
|
|
||||||
isEFIPersistentStateEnabled(spec) {
|
|
||||||
return !!spec?.template?.spec?.domain?.firmware?.bootloader?.efi?.persistent;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isSecureBoot(spec) {
|
isSecureBoot(spec) {
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ import { HCI_SETTING } from '../../config/settings';
|
|||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import { parseVolumeClaimTemplates } from '../../utils/vm';
|
import { parseVolumeClaimTemplates } from '../../utils/vm';
|
||||||
import impl, { QGA_JSON, USB_TABLET } from './impl';
|
import impl, { QGA_JSON, USB_TABLET } from './impl';
|
||||||
import { GIBIBYTE } from '../../utils/unit';
|
|
||||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
|
||||||
|
|
||||||
const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine';
|
const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine';
|
||||||
|
|
||||||
@@ -166,8 +164,6 @@ export default {
|
|||||||
accessCredentials: [],
|
accessCredentials: [],
|
||||||
efiEnabled: false,
|
efiEnabled: false,
|
||||||
tpmEnabled: false,
|
tpmEnabled: false,
|
||||||
tpmPersistentStateEnabled: false,
|
|
||||||
efiPersistentStateEnabled: false,
|
|
||||||
secureBoot: false,
|
secureBoot: false,
|
||||||
userDataTemplateId: '',
|
userDataTemplateId: '',
|
||||||
saveUserDataAsClearText: false,
|
saveUserDataAsClearText: false,
|
||||||
@@ -221,7 +217,7 @@ export default {
|
|||||||
const selectedVMNetworks = networkNames.map((name) => vmNetworks.find((n) => n.id === name)).filter((n) => n?.id);
|
const selectedVMNetworks = networkNames.map((name) => vmNetworks.find((n) => n.id === name)).filter((n) => n?.id);
|
||||||
const clusterNetworks = uniq(selectedVMNetworks.map((n) => n.clusterNetworkResource?.id));
|
const clusterNetworks = uniq(selectedVMNetworks.map((n) => n.clusterNetworkResource?.id));
|
||||||
|
|
||||||
return nodes.filter((N) => !N.isUnSchedulable && N.isEtcd !== 'true').map((node) => {
|
return nodes.filter((N) => !N.isUnSchedulable).map((node) => {
|
||||||
const requireLabelKeys = [];
|
const requireLabelKeys = [];
|
||||||
let isNetworkSchedule = true;
|
let isNetworkSchedule = true;
|
||||||
|
|
||||||
@@ -245,6 +241,12 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
defaultStorageClass() {
|
||||||
|
const defaultStorage = this.$store.getters[`${ this.inStore }/all`](STORAGE_CLASS).find( (O) => O.isDefault);
|
||||||
|
|
||||||
|
return defaultStorage;
|
||||||
|
},
|
||||||
|
|
||||||
storageClassSetting() {
|
storageClassSetting() {
|
||||||
try {
|
try {
|
||||||
const storageClassValue = this.$store.getters[`${ this.inStore }/all`](HCI.SETTING).find( (O) => O.id === HCI_SETTING.DEFAULT_STORAGE_CLASS)?.value;
|
const storageClassValue = this.$store.getters[`${ this.inStore }/all`](HCI.SETTING).find( (O) => O.id === HCI_SETTING.DEFAULT_STORAGE_CLASS)?.value;
|
||||||
@@ -256,7 +258,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
customVolumeMode() {
|
customVolumeMode() {
|
||||||
return this.storageClassSetting.volumeMode || VOLUME_MODE.BLOCK;
|
return this.storageClassSetting.volumeMode || 'Block';
|
||||||
},
|
},
|
||||||
|
|
||||||
customAccessMode() {
|
customAccessMode() {
|
||||||
@@ -365,8 +367,6 @@ export default {
|
|||||||
const installAgent = this.hasInstallAgent(userData, osType, true);
|
const installAgent = this.hasInstallAgent(userData, osType, true);
|
||||||
const efiEnabled = this.isEfiEnabled(spec);
|
const efiEnabled = this.isEfiEnabled(spec);
|
||||||
const tpmEnabled = this.isTpmEnabled(spec);
|
const tpmEnabled = this.isTpmEnabled(spec);
|
||||||
const tpmPersistentStateEnabled = this.isTPMPersistentStateEnabled(spec);
|
|
||||||
const efiPersistentStateEnabled = this.isEFIPersistentStateEnabled(spec);
|
|
||||||
const secureBoot = this.isSecureBoot(spec);
|
const secureBoot = this.isSecureBoot(spec);
|
||||||
const cpuPinning = this.isCpuPinning(spec);
|
const cpuPinning = this.isCpuPinning(spec);
|
||||||
|
|
||||||
@@ -398,9 +398,7 @@ export default {
|
|||||||
|
|
||||||
this['installUSBTablet'] = installUSBTablet;
|
this['installUSBTablet'] = installUSBTablet;
|
||||||
this['efiEnabled'] = efiEnabled;
|
this['efiEnabled'] = efiEnabled;
|
||||||
this['efiPersistentStateEnabled'] = efiPersistentStateEnabled;
|
|
||||||
this['tpmEnabled'] = tpmEnabled;
|
this['tpmEnabled'] = tpmEnabled;
|
||||||
this['tpmPersistentStateEnabled'] = tpmPersistentStateEnabled;
|
|
||||||
this['secureBoot'] = secureBoot;
|
this['secureBoot'] = secureBoot;
|
||||||
this['cpuPinning'] = cpuPinning;
|
this['cpuPinning'] = cpuPinning;
|
||||||
|
|
||||||
@@ -444,7 +442,7 @@ export default {
|
|||||||
if (!isIsoImage) {
|
if (!isIsoImage) {
|
||||||
imageSizeGiB = Math.max(imageSizeGiB, 10);
|
imageSizeGiB = Math.max(imageSizeGiB, 10);
|
||||||
}
|
}
|
||||||
size = `${ imageSizeGiB }${ GIBIBYTE }`;
|
size = `${ imageSizeGiB }Gi`;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
@@ -458,7 +456,7 @@ export default {
|
|||||||
type,
|
type,
|
||||||
storageClassName: '',
|
storageClassName: '',
|
||||||
image: this.imageId,
|
image: this.imageId,
|
||||||
volumeMode: VOLUME_MODE.BLOCK,
|
volumeMode: 'Block',
|
||||||
isEncrypted,
|
isEncrypted,
|
||||||
volumeBackups,
|
volumeBackups,
|
||||||
});
|
});
|
||||||
@@ -517,7 +515,7 @@ export default {
|
|||||||
accessMode = pvcResource?.spec?.accessModes?.[0] || 'ReadWriteMany';
|
accessMode = pvcResource?.spec?.accessModes?.[0] || 'ReadWriteMany';
|
||||||
size = pvcResource?.spec?.resources?.requests?.storage || '10Gi';
|
size = pvcResource?.spec?.resources?.requests?.storage || '10Gi';
|
||||||
storageClassName = pvcResource?.spec?.storageClassName;
|
storageClassName = pvcResource?.spec?.storageClassName;
|
||||||
volumeMode = pvcResource?.spec?.volumeMode || VOLUME_MODE.BLOCK;
|
volumeMode = pvcResource?.spec?.volumeMode || 'Block';
|
||||||
volumeName = pvcResource?.metadata?.name || '';
|
volumeName = pvcResource?.metadata?.name || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -554,7 +552,7 @@ export default {
|
|||||||
volumeName,
|
volumeName,
|
||||||
container,
|
container,
|
||||||
accessMode,
|
accessMode,
|
||||||
size: `${ formatSize }${ GIBIBYTE }`,
|
size: `${ formatSize }Gi`,
|
||||||
volumeMode: volumeMode || this.customVolumeMode,
|
volumeMode: volumeMode || this.customVolumeMode,
|
||||||
image,
|
image,
|
||||||
type,
|
type,
|
||||||
@@ -669,7 +667,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.secretName) {
|
if (!this.secretName || this.needNewSecret) {
|
||||||
this.secretName = this.generateSecretName(this.secretNamePrefix);
|
this.secretName = this.generateSecretName(this.secretNamePrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,7 +869,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out.length === 0 && !!this.spec.template.spec.accessCredentials === false) {
|
if (out.length === 0 && !!this.spec.template.spec.accessCredentials) {
|
||||||
delete this.spec.template.spec.accessCredentials;
|
delete this.spec.template.spec.accessCredentials;
|
||||||
} else {
|
} else {
|
||||||
this.spec.template.spec.accessCredentials = out;
|
this.spec.template.spec.accessCredentials = out;
|
||||||
@@ -921,7 +919,9 @@ export default {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return userDataYaml;
|
const hasCloudComment = this.hasCloudConfigComment(userDataYaml);
|
||||||
|
|
||||||
|
return hasCloudComment ? userDataYaml : `#cloud-config\n${ userDataYaml }`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error: Unable to parse yaml document', e); // eslint-disable-line no-console
|
console.error('Error: Unable to parse yaml document', e); // eslint-disable-line no-console
|
||||||
|
|
||||||
@@ -969,7 +969,7 @@ export default {
|
|||||||
|
|
||||||
parseVolumeClaimTemplate(R, dataVolumeName) {
|
parseVolumeClaimTemplate(R, dataVolumeName) {
|
||||||
if (!String(R.size).includes('Gi') && R.size) {
|
if (!String(R.size).includes('Gi') && R.size) {
|
||||||
R.size = `${ R.size }${ GIBIBYTE }`;
|
R.size = `${ R.size }Gi`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const out = {
|
const out = {
|
||||||
@@ -1384,21 +1384,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setBootMethod(boot = {
|
setBootMethod(boot = { efi: false, secureBoot: false }) {
|
||||||
efi: false, secureBoot: false, efiPersistentStateEnabled: false
|
if (boot.efi && boot.secureBoot) {
|
||||||
}) {
|
|
||||||
if (boot.efi) {
|
|
||||||
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', boot.secureBoot);
|
|
||||||
} else {
|
|
||||||
delete this.spec.template.spec.domain['firmware'];
|
|
||||||
delete this.spec.template.spec.domain.features['smm'];
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (boot.secureBoot) {
|
|
||||||
set(this.spec.template.spec.domain, 'features.smm.enabled', true);
|
set(this.spec.template.spec.domain, 'features.smm.enabled', true);
|
||||||
} else {
|
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', true);
|
||||||
|
} else if (boot.efi && !boot.secureBoot) {
|
||||||
|
// set(this.spec.template.spec.domain, 'features.smm.enabled', false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
delete this.spec.template.spec.domain.features.smm['enabled'];
|
delete this.spec.template.spec.domain.features.smm['enabled'];
|
||||||
const noKeys = Object.keys(this.spec.template.spec.domain.features.smm).length === 0;
|
const noKeys = Object.keys(this.spec.template.spec.domain.features.smm).length === 0;
|
||||||
@@ -1407,12 +1399,10 @@ export default {
|
|||||||
delete this.spec.template.spec.domain.features['smm'];
|
delete this.spec.template.spec.domain.features['smm'];
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', false);
|
||||||
|
|
||||||
if (boot.efiPersistentStateEnabled) {
|
|
||||||
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.persistent', true);
|
|
||||||
} else {
|
} else {
|
||||||
delete this.spec.template.spec.domain.firmware.bootloader.efi['persistent'];
|
delete this.spec.template.spec.domain['firmware'];
|
||||||
|
delete this.spec.template.spec.domain.features['smm'];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1424,9 +1414,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setTPM({ tpmEnabled = false, tpmPersistentStateEnabled = false } = {}) {
|
setTPM(tpmEnabled) {
|
||||||
if (tpmEnabled) {
|
if (tpmEnabled) {
|
||||||
set(this.spec.template.spec.domain.devices, 'tpm', tpmPersistentStateEnabled ? { persistent: true } : {});
|
set(this.spec.template.spec.domain.devices, 'tpm', {});
|
||||||
} else {
|
} else {
|
||||||
delete this.spec.template.spec.domain.devices['tpm'];
|
delete this.spec.template.spec.domain.devices['tpm'];
|
||||||
}
|
}
|
||||||
@@ -1442,7 +1432,6 @@ export default {
|
|||||||
sshAuthorizedKeys.splice(index, 1);
|
sshAuthorizedKeys.splice(index, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const userDataJson = this.convertToJson(this.userScript);
|
const userDataJson = this.convertToJson(this.userScript);
|
||||||
|
|
||||||
userDataJson.ssh_authorized_keys = sshAuthorizedKeys;
|
userDataJson.ssh_authorized_keys = sshAuthorizedKeys;
|
||||||
@@ -1540,21 +1529,11 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
efiEnabled(val) {
|
efiEnabled(val) {
|
||||||
this.setBootMethod({
|
this.setBootMethod({ efi: val, secureBoot: this.secureBoot });
|
||||||
efi: val, secureBoot: this.secureBoot, efiPersistentStateEnabled: this.efiPersistentStateEnabled
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
secureBoot(val) {
|
secureBoot(val) {
|
||||||
this.setBootMethod({
|
this.setBootMethod({ efi: this.efiEnabled, secureBoot: val });
|
||||||
efi: this.efiEnabled, secureBoot: val, efiPersistentStateEnabled: this.efiPersistentStateEnabled
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
efiPersistentStateEnabled(val) {
|
|
||||||
this.setBootMethod({
|
|
||||||
efi: this.efiEnabled, secureBoot: this.secureBoot, efiPersistentStateEnabled: val
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cpuPinning(value) {
|
cpuPinning(value) {
|
||||||
@@ -1562,11 +1541,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
tpmEnabled(val) {
|
tpmEnabled(val) {
|
||||||
this.setTPM({ tpmEnabled: val, tpmPersistentStateEnabled: this.tpmPersistentStateEnabled });
|
this.setTPM(val);
|
||||||
},
|
|
||||||
|
|
||||||
tpmPersistentStateEnabled(val) {
|
|
||||||
this.setTPM({ tpmEnabled: this.tpmEnabled, tpmPersistentStateEnabled: val });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
installAgent: {
|
installAgent: {
|
||||||
@@ -1578,19 +1553,9 @@ export default {
|
|||||||
*/
|
*/
|
||||||
handler(neu) {
|
handler(neu) {
|
||||||
if (this.deleteAgent) {
|
if (this.deleteAgent) {
|
||||||
let out = this.getUserData({
|
this['userScript'] = this.getUserData({
|
||||||
installAgent: neu, osType: this.osType, deletePackage: this.deletePackage
|
installAgent: neu, osType: this.osType, deletePackage: this.deletePackage
|
||||||
});
|
});
|
||||||
|
|
||||||
if (neu) {
|
|
||||||
const hasCloudComment = this.hasCloudConfigComment(out);
|
|
||||||
|
|
||||||
if (!hasCloudComment) {
|
|
||||||
out = `#cloud-config\n${ out }`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this['userScript'] = out;
|
|
||||||
this.refreshYamlEditor();
|
this.refreshYamlEditor();
|
||||||
}
|
}
|
||||||
this.deleteAgent = true;
|
this.deleteAgent = true;
|
||||||
@@ -1615,15 +1580,16 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
sshKey(neu, old) {
|
sshKey(neu, old) {
|
||||||
// refresh yaml editor to get the latest userScript
|
|
||||||
this.userScript = this.getUserData({ installAgent: this.installAgent, osType: this.osType });
|
|
||||||
this.refreshYamlEditor();
|
|
||||||
|
|
||||||
const _diff = difference(old, neu);
|
const _diff = difference(old, neu);
|
||||||
|
|
||||||
|
// delete removed ssh key in userdata if needed
|
||||||
if (_diff.length > 0 && this.isCreate) {
|
if (_diff.length > 0 && this.isCreate) {
|
||||||
this.deleteSSHFromUserData(_diff);
|
this.deleteSSHFromUserData(_diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// refresh yaml editor to get the latest userScript
|
||||||
|
this.userScript = this.getUserData({ installAgent: this.installAgent, osType: this.osType });
|
||||||
|
this.refreshYamlEditor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -470,7 +470,7 @@ export default class HciNode extends HarvesterResource {
|
|||||||
get canDelete() {
|
get canDelete() {
|
||||||
const nodes = this.$rootGetters['harvester/all'](NODE) || [];
|
const nodes = this.$rootGetters['harvester/all'](NODE) || [];
|
||||||
|
|
||||||
return nodes.length > 1 && super.canDelete;
|
return nodes.length > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
get vlanStatuses() {
|
get vlanStatuses() {
|
||||||
|
|||||||
@@ -46,37 +46,22 @@ export default class HciPv extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportImageAction = {
|
if (!this.isLonghorn || !this.isLonghornV2) {
|
||||||
action: 'exportImage',
|
|
||||||
enabled: this.hasAction('export') && !this.isEncrypted,
|
|
||||||
icon: 'icon icon-copy',
|
|
||||||
label: this.t('harvester.action.exportImage')
|
|
||||||
};
|
|
||||||
const takeSnapshotAction = {
|
|
||||||
action: 'snapshot',
|
|
||||||
enabled: this.hasAction('snapshot'),
|
|
||||||
icon: 'icon icon-backup',
|
|
||||||
label: this.t('harvester.action.snapshot'),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.thirdPartyStorageFeatureEnabled) { // v1.5.0
|
|
||||||
out = [
|
out = [
|
||||||
exportImageAction,
|
{
|
||||||
takeSnapshotAction,
|
action: 'exportImage',
|
||||||
|
enabled: this.hasAction('export') && !this.isEncrypted,
|
||||||
|
icon: 'icon icon-copy',
|
||||||
|
label: this.t('harvester.action.exportImage')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'snapshot',
|
||||||
|
enabled: this.hasAction('snapshot'),
|
||||||
|
icon: 'icon icon-backup',
|
||||||
|
label: this.t('harvester.action.snapshot'),
|
||||||
|
},
|
||||||
...out
|
...out
|
||||||
];
|
];
|
||||||
// TODO: remove this block if Longhorn V2 engine supports restore volume snapshot
|
|
||||||
if (this.isLonghornV2) {
|
|
||||||
out = out.filter((action) => action.action !== takeSnapshotAction.action);
|
|
||||||
}
|
|
||||||
} else { // v1.4 / v1.3
|
|
||||||
if (!this.isLonghorn || !this.isLonghornV2) {
|
|
||||||
out = [
|
|
||||||
exportImageAction,
|
|
||||||
takeSnapshotAction,
|
|
||||||
...out
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -336,27 +321,15 @@ export default class HciPv extends HarvesterResource {
|
|||||||
return this.volumeProvider === LONGHORN_DRIVER;
|
return this.volumeProvider === LONGHORN_DRIVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLonghornV1() {
|
|
||||||
return this.isLonghorn && !this.isLonghornV2;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isLonghornV2() {
|
get isLonghornV2() {
|
||||||
return this.dataEngine === DATA_ENGINE_V2;
|
return this.dataEngine === DATA_ENGINE_V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
get thirdPartyStorageFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
|
||||||
}
|
|
||||||
|
|
||||||
get resourceExternalLink() {
|
get resourceExternalLink() {
|
||||||
const host = window.location.host;
|
const host = window.location.host;
|
||||||
const { params } = this.currentRoute();
|
const { params } = this.currentRoute();
|
||||||
const volumeName = this.spec?.volumeName;
|
const volumeName = this.spec?.volumeName;
|
||||||
|
|
||||||
if (!this.isLonghorn) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!volumeName) {
|
if (!volumeName) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,18 +58,6 @@ export default class HciStorageClass extends StorageClass {
|
|||||||
return key ? this.$rootGetters['i18n/t'](key) : this.provisioner;
|
return key ? this.$rootGetters['i18n/t'](key) : this.provisioner;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isEncrypted() {
|
|
||||||
return this.parameters?.encrypted === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
get isLonghorn() {
|
|
||||||
return this.provisioner === LONGHORN_DRIVER;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isLonghornV1() {
|
|
||||||
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isLonghornV2() {
|
get isLonghornV2() {
|
||||||
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2;
|
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2;
|
||||||
}
|
}
|
||||||
@@ -81,8 +69,4 @@ export default class HciStorageClass extends StorageClass {
|
|||||||
get volumeEncryptionFeatureEnabled() {
|
get volumeEncryptionFeatureEnabled() {
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
|
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
|
||||||
}
|
}
|
||||||
|
|
||||||
get thirdPartyStorageFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export default class HciVmBackup extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get sourceSchedule() {
|
get sourceSchedule() {
|
||||||
return this.metadata?.annotations?.[HCI_ANNOTATIONS.SVM_BACKUP_ID];
|
return this.metadata?.annotations[HCI_ANNOTATIONS.SVM_BACKUP_ID];
|
||||||
}
|
}
|
||||||
|
|
||||||
get attachVM() {
|
get attachVM() {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { HCI } from '../types';
|
|||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
||||||
import HarvesterResource from './harvester';
|
import HarvesterResource from './harvester';
|
||||||
import { CSI_SECRETS } from '@pkg/harvester/config/harvester-map';
|
import { CSI_SECRETS } from '@pkg/harvester/config/harvester-map';
|
||||||
import { UNIT_SUFFIX } from '../utils/unit';
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
CSI_PROVISIONER_SECRET_NAME,
|
CSI_PROVISIONER_SECRET_NAME,
|
||||||
@@ -73,7 +72,7 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
disabled: !this.isReady,
|
disabled: !this.isReady,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'imageDownload',
|
action: 'download',
|
||||||
enabled: this.links?.download,
|
enabled: this.links?.download,
|
||||||
icon: 'icon icon-download',
|
icon: 'icon icon-download',
|
||||||
label: this.t('asyncButton.download.action'),
|
label: this.t('asyncButton.download.action'),
|
||||||
@@ -200,10 +199,6 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
return `${ this.metadata.namespace }/${ this.spec.displayName }`;
|
return `${ this.metadata.namespace }/${ this.spec.displayName }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get imageStorageClass() {
|
|
||||||
return this?.metadata?.annotations?.[HCI_ANNOTATIONS.STORAGE_CLASS] || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
get imageMessage() {
|
get imageMessage() {
|
||||||
if (this.uploadError) {
|
if (this.uploadError) {
|
||||||
return ucFirst(this.uploadError);
|
return ucFirst(this.uploadError);
|
||||||
@@ -244,8 +239,8 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
return formatSi(size, {
|
return formatSi(size, {
|
||||||
increment: 1024,
|
increment: 1024,
|
||||||
maxPrecision: 2,
|
maxPrecision: 2,
|
||||||
suffix: UNIT_SUFFIX,
|
suffix: 'B',
|
||||||
firstSuffix: UNIT_SUFFIX,
|
firstSuffix: 'B',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,8 +254,8 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
return formatSi(virtualSize, {
|
return formatSi(virtualSize, {
|
||||||
increment: 1024,
|
increment: 1024,
|
||||||
maxPrecision: 2,
|
maxPrecision: 2,
|
||||||
suffix: UNIT_SUFFIX,
|
suffix: 'B',
|
||||||
firstSuffix: UNIT_SUFFIX,
|
firstSuffix: 'B',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,23 +385,7 @@ export default class HciVmImage extends HarvesterResource {
|
|||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
|
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
|
||||||
}
|
}
|
||||||
|
|
||||||
get thirdPartyStorageFeatureEnabled() {
|
download() {
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
|
||||||
}
|
|
||||||
|
|
||||||
imageDownload(resources = this) {
|
|
||||||
// spec.backend is introduced in v1.5.0. If it's not set, it's an old image can be downloaded via link
|
|
||||||
if (this.spec?.backend === 'cdi') {
|
|
||||||
this.$dispatch('promptModal', {
|
|
||||||
resources,
|
|
||||||
component: 'HarvesterImageDownloader'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.downloadViaLink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadViaLink() {
|
|
||||||
window.location.href = this.links.download;
|
window.location.href = this.links.download;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,10 +210,6 @@ export default class HciVmTemplateVersion extends HarvesterResource {
|
|||||||
await templateResource.save();
|
await templateResource.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
get cpuPinningFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('cpuPinning');
|
|
||||||
}
|
|
||||||
|
|
||||||
get defaultVersion() {
|
get defaultVersion() {
|
||||||
const templates = this.$rootGetters['harvester/all'](HCI.VM_TEMPLATE);
|
const templates = this.$rootGetters['harvester/all'](HCI.VM_TEMPLATE);
|
||||||
const template = templates.find((T) => this.templateId === T.id);
|
const template = templates.find((T) => this.templateId === T.id);
|
||||||
@@ -276,12 +272,4 @@ export default class HciVmTemplateVersion extends HarvesterResource {
|
|||||||
|
|
||||||
this.spec.vm.spec.template.metadata['labels'] = { ...wasIgnored, ...val };
|
this.spec.vm.spec.template.metadata['labels'] = { ...wasIgnored, ...val };
|
||||||
}
|
}
|
||||||
|
|
||||||
get tpmPersistentStateFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('tpmPersistentState');
|
|
||||||
}
|
|
||||||
|
|
||||||
get efiPersistentStateFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('efiPersistentState');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import SteveModel from '@shell/plugins/steve/steve-class';
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
||||||
import { HCI } from '@shell/config/labels-annotations';
|
import { HCI } from '@shell/config/labels-annotations';
|
||||||
import { NETWORK_TYPE } from '../config/types';
|
|
||||||
|
|
||||||
const { UNTAGGED } = NETWORK_TYPE;
|
|
||||||
|
|
||||||
export default class NetworkAttachmentDef extends SteveModel {
|
export default class NetworkAttachmentDef extends SteveModel {
|
||||||
applyDefaults() {
|
applyDefaults() {
|
||||||
@@ -45,7 +42,7 @@ export default class NetworkAttachmentDef extends SteveModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get vlanId() {
|
get vlanId() {
|
||||||
return this.vlanType === UNTAGGED ? 'N/A' : this.parseConfig.vlan;
|
return this.vlanType === 'UntaggedNetwork' ? 'N/A' : this.parseConfig.vlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
get customValidationRules() {
|
get customValidationRules() {
|
||||||
@@ -68,7 +65,7 @@ export default class NetworkAttachmentDef extends SteveModel {
|
|||||||
const route = annotations[HCI.NETWORK_ROUTE];
|
const route = annotations[HCI.NETWORK_ROUTE];
|
||||||
let config = {};
|
let config = {};
|
||||||
|
|
||||||
if (this.vlanType === UNTAGGED) {
|
if (this.vlanType === 'UntaggedNetwork') {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,13 +102,11 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
action: 'stopVM',
|
action: 'stopVM',
|
||||||
altAction: 'altStopVM',
|
enabled: !!this.actions?.stop,
|
||||||
enabled: !!this.actions?.stop,
|
icon: 'icon icon-close',
|
||||||
icon: 'icon icon-close',
|
label: this.t('harvester.action.stop'),
|
||||||
label: this.t('harvester.action.stop'),
|
bulkable: true
|
||||||
bulkable: true,
|
|
||||||
bulkAction: 'stopVM',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'forceStop',
|
action: 'forceStop',
|
||||||
@@ -118,11 +116,10 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
bulkable: true
|
bulkable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'pauseVM',
|
action: 'pauseVM',
|
||||||
altAction: 'altPauseVM',
|
enabled: !!this.actions?.pause,
|
||||||
enabled: !!this.actions?.pause,
|
icon: 'icon icon-pause',
|
||||||
icon: 'icon icon-pause',
|
label: this.t('harvester.action.pause')
|
||||||
label: this.t('harvester.action.pause')
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'unpauseVM',
|
action: 'unpauseVM',
|
||||||
@@ -158,7 +155,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'takeVMSnapshot',
|
action: 'takeVMSnapshot',
|
||||||
enabled: (!!this.actions?.snapshot || !!this.action?.backup) && !this.longhornV2Volumes.length,
|
enabled: !!this.actions?.backup && !this.longhornV2Volumes.length,
|
||||||
icon: 'icon icon-snapshot',
|
icon: 'icon icon-snapshot',
|
||||||
label: this.t('harvester.action.vmSnapshot')
|
label: this.t('harvester.action.vmSnapshot')
|
||||||
},
|
},
|
||||||
@@ -405,16 +402,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return node?.id;
|
return node?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseVM(resources = this) {
|
pauseVM() {
|
||||||
this.$dispatch('promptModal', {
|
|
||||||
resources,
|
|
||||||
action: 'pause',
|
|
||||||
warningMessageKey: 'dialog.confirmExecution.pause.message',
|
|
||||||
component: 'ConfirmExecutionDialog'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
altPauseVM() {
|
|
||||||
this.doActionGrowl('pause', {});
|
this.doActionGrowl('pause', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,16 +417,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
this.doActionGrowl('unpause', {});
|
this.doActionGrowl('unpause', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
stopVM(resources = this) {
|
stopVM() {
|
||||||
this.$dispatch('promptModal', {
|
|
||||||
resources,
|
|
||||||
action: 'stop',
|
|
||||||
warningMessageKey: 'dialog.confirmExecution.stop.message',
|
|
||||||
component: 'ConfirmExecutionDialog'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
altStopVM() {
|
|
||||||
this.doActionGrowl('stop', {});
|
this.doActionGrowl('stop', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,10 +619,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get encryptedVolumeType() {
|
get encryptedVolumeType() {
|
||||||
if (!this.volumes || this.volumes.length === 0) {
|
|
||||||
return 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.volumes.every((vol) => vol.isEncrypted)) {
|
if (this.volumes.every((vol) => vol.isEncrypted)) {
|
||||||
return 'all';
|
return 'all';
|
||||||
} else if (this.volumes.some((vol) => vol.isEncrypted)) {
|
} else if (this.volumes.some((vol) => vol.isEncrypted)) {
|
||||||
@@ -700,25 +675,8 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isPending() {
|
get isBeingStopped() {
|
||||||
if (this &&
|
if (this && !this.isVMExpectedRunning && this.isVMCreated && this.vmi?.status?.phase !== VMIPhase.Succeeded) {
|
||||||
!this.isVMExpectedRunning &&
|
|
||||||
this.isVMCreated &&
|
|
||||||
this.vmi?.status?.phase === VMIPhase.Pending
|
|
||||||
) {
|
|
||||||
return { status: VMIPhase.Pending };
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isStopping() {
|
|
||||||
if (this &&
|
|
||||||
!this.isVMExpectedRunning &&
|
|
||||||
this.isVMCreated &&
|
|
||||||
this.vmi?.status?.phase !== VMIPhase.Succeeded &&
|
|
||||||
this.vmi?.status?.phase !== VMIPhase.Pending
|
|
||||||
) {
|
|
||||||
return { status: STOPPING };
|
return { status: STOPPING };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,7 +711,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isUnschedulable() {
|
get isUnschedulable() {
|
||||||
if (this.isStopping || this.isStarting) {
|
if (this.isBeingStopped || this.isStarting) {
|
||||||
const condition = this.status?.conditions?.find((c) => c.reason === UNSCHEDULABLE);
|
const condition = this.status?.conditions?.find((c) => c.reason === UNSCHEDULABLE);
|
||||||
|
|
||||||
if (!!condition) {
|
if (!!condition) {
|
||||||
@@ -869,8 +827,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
this.isUnschedulable?.status ||
|
this.isUnschedulable?.status ||
|
||||||
this.isPaused?.status ||
|
this.isPaused?.status ||
|
||||||
this.isVMError?.status ||
|
this.isVMError?.status ||
|
||||||
this.isPending?.status ||
|
this.isBeingStopped?.status ||
|
||||||
this.isStopping?.status ||
|
|
||||||
this.isOff?.status ||
|
this.isOff?.status ||
|
||||||
this.isError?.status ||
|
this.isError?.status ||
|
||||||
this.isRunning?.status ||
|
this.isRunning?.status ||
|
||||||
@@ -1193,18 +1150,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
|
return this.$rootGetters['harvester-common/getFeatureEnabled']('volumeEncryption');
|
||||||
}
|
}
|
||||||
|
|
||||||
get tpmPersistentStateFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('tpmPersistentState');
|
|
||||||
}
|
|
||||||
|
|
||||||
get efiPersistentStateFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('efiPersistentState');
|
|
||||||
}
|
|
||||||
|
|
||||||
get thirdPartyStorageFeatureEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
|
||||||
}
|
|
||||||
|
|
||||||
setInstanceLabels(val) {
|
setInstanceLabels(val) {
|
||||||
if ( !this.spec?.template?.metadata?.labels ) {
|
if ( !this.spec?.template?.metadata?.labels ) {
|
||||||
set(this, 'spec.template.metadata.labels', {});
|
set(this, 'spec.template.metadata.labels', {});
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester",
|
"name": "harvester",
|
||||||
"description": "Rancher UI Extension for Harvester",
|
"description": "Rancher UI Extension for Harvester",
|
||||||
"version": "1.5.0-rc4",
|
"version": "1.0.6-rc3",
|
||||||
"private": false,
|
"private": false,
|
||||||
"rancher": {
|
"rancher": {
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"catalog.cattle.io/display-name": "Harvester",
|
"catalog.cattle.io/display-name": "Harvester",
|
||||||
"catalog.cattle.io/kube-version": ">= 1.16.0-0",
|
"catalog.cattle.io/kube-version": ">= 1.16.0-0",
|
||||||
"catalog.cattle.io/rancher-version": ">= 2.11.0-0",
|
"catalog.cattle.io/rancher-version": ">= 2.10.1-0",
|
||||||
"catalog.cattle.io/ui-extensions-version": ">= 3.0.0 < 4.0.0"
|
"catalog.cattle.io/ui-extensions-version": ">= 3.0.0 < 4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,12 +16,14 @@ export default {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
names: {
|
names: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => {
|
default: () => {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
@@ -67,6 +69,12 @@ export default {
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
plusMore() {
|
||||||
|
const remaining = this.toRemove.length - this.names.length;
|
||||||
|
|
||||||
|
return this.t('promptRemove.andOthers', { count: remaining });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@@ -135,7 +143,7 @@ export default {
|
|||||||
<div>
|
<div>
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
{{ t('promptRemove.attemptingToRemove', {type}) }}
|
{{ t('promptRemove.attemptingToRemove', {type}) }}
|
||||||
<span v-clean-html="resourceNames(names, t)"></span>
|
<span v-clean-html="resourceNames(names, plusMore, t)"></span>
|
||||||
|
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
{{ t('harvester.virtualMachine.promptRemove.title') }}
|
{{ t('harvester.virtualMachine.promptRemove.title') }}
|
||||||
|
|||||||
@@ -5,33 +5,16 @@ import { featureEnabled, getVersion } from '../utils/feature-flags';
|
|||||||
|
|
||||||
const state = function() {
|
const state = function() {
|
||||||
return {
|
return {
|
||||||
// support bundle
|
latestBundleId: '',
|
||||||
latestBundleId: '',
|
bundlePending: false,
|
||||||
bundlePending: false,
|
showBundleModal: false,
|
||||||
showBundleModal: false,
|
bundlePercentage: 0,
|
||||||
bundlePercentage: 0,
|
uploadingImages: [],
|
||||||
uploadingImages: [],
|
uploadingImageError: {},
|
||||||
uploadingImageError: {},
|
|
||||||
// download cdi image
|
|
||||||
downloadImageId: '',
|
|
||||||
downloadImageInProgress: false,
|
|
||||||
isDownloadImageCancel: false,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
setDownloadImageId(state, id) {
|
|
||||||
state.downloadImageId = id;
|
|
||||||
},
|
|
||||||
|
|
||||||
setDownloadImageCancel(state, value) {
|
|
||||||
state.isDownloadImageCancel = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
setDownloadImageInProgress(state, value) {
|
|
||||||
state.downloadImageInProgress = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
setLatestBundleId(state, bundleId) {
|
setLatestBundleId(state, bundleId) {
|
||||||
state.latestBundleId = bundleId;
|
state.latestBundleId = bundleId;
|
||||||
},
|
},
|
||||||
@@ -68,14 +51,6 @@ const getters = {
|
|||||||
return state.latestBundleId;
|
return state.latestBundleId;
|
||||||
},
|
},
|
||||||
|
|
||||||
isDownloadImageCancel(state) {
|
|
||||||
return state.isDownloadImageCancel;
|
|
||||||
},
|
|
||||||
|
|
||||||
isDownloadImageInProgress(state) {
|
|
||||||
return state.downloadImageInProgress;
|
|
||||||
},
|
|
||||||
|
|
||||||
isBundlePending(state) {
|
isBundlePending(state) {
|
||||||
return state.bundlePending;
|
return state.bundlePending;
|
||||||
},
|
},
|
||||||
@@ -123,70 +98,6 @@ const getters = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
async downloadImageProgress({
|
|
||||||
state, dispatch, commit, rootGetters
|
|
||||||
}) {
|
|
||||||
const parse = Parse(window.history.href);
|
|
||||||
|
|
||||||
const id = state.downloadImageId; // id is image_ns / image_name
|
|
||||||
|
|
||||||
let imageCrd = await dispatch(
|
|
||||||
'harvester/find',
|
|
||||||
{ type: HCI.VM_IMAGE_DOWNLOADER, id },
|
|
||||||
{ root: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
await commit('setDownloadImageInProgress', true);
|
|
||||||
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
const timer = setInterval(async() => {
|
|
||||||
count = count + 1;
|
|
||||||
if (count % 3 === 0) {
|
|
||||||
// ws maybe disconnect, force to get the latest status
|
|
||||||
imageCrd = await dispatch(
|
|
||||||
'harvester/find',
|
|
||||||
{
|
|
||||||
type: HCI.VM_IMAGE_DOWNLOADER,
|
|
||||||
id,
|
|
||||||
opt: { force: true }
|
|
||||||
},
|
|
||||||
{ root: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If is cancel, clear the timer
|
|
||||||
if (state.isDownloadImageCancel === true) {
|
|
||||||
clearInterval(timer);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// converting image status becomes ready
|
|
||||||
if (imageCrd?.status?.status === 'Ready') {
|
|
||||||
imageCrd = rootGetters['harvester/byId'](HCI.VM_IMAGE_DOWNLOADER, id);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
commit('setDownloadImageInProgress', false);
|
|
||||||
dispatch('promptModal'); // bring undefined data will close the promptModal
|
|
||||||
}, 600);
|
|
||||||
|
|
||||||
if (rootGetters['isMultiCluster']) {
|
|
||||||
const clusterId = rootGetters['clusterId'];
|
|
||||||
const prefix = `/k8s/clusters/${ clusterId }`;
|
|
||||||
|
|
||||||
window.location.href = `${ parse.origin }${ prefix }/v1/harvester/${ HCI.IMAGE }/${ id }/download`;
|
|
||||||
} else {
|
|
||||||
const link = `${ parse.origin }/v1/harvester/${ HCI.IMAGE }/${ id }/download`;
|
|
||||||
|
|
||||||
window.location.href = link;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(timer);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
},
|
|
||||||
|
|
||||||
async bundleProgress({
|
async bundleProgress({
|
||||||
state, dispatch, commit, rootGetters
|
state, dispatch, commit, rootGetters
|
||||||
}) {
|
}) {
|
||||||
@@ -206,7 +117,7 @@ const actions = {
|
|||||||
const timer = setInterval(async() => {
|
const timer = setInterval(async() => {
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
if (count % 3 === 0) {
|
if (count % 3 === 0) {
|
||||||
// ws maybe disconnect
|
// ws mayby disconnect
|
||||||
bundleCrd = await dispatch(
|
bundleCrd = await dispatch(
|
||||||
'harvester/find',
|
'harvester/find',
|
||||||
{
|
{
|
||||||
|
|||||||
+52
-53
@@ -1,57 +1,56 @@
|
|||||||
export const HCI = {
|
export const HCI = {
|
||||||
VM: 'kubevirt.io.virtualmachine',
|
VM: 'kubevirt.io.virtualmachine',
|
||||||
VMI: 'kubevirt.io.virtualmachineinstance',
|
VMI: 'kubevirt.io.virtualmachineinstance',
|
||||||
VMIM: 'kubevirt.io.virtualmachineinstancemigration',
|
VMIM: 'kubevirt.io.virtualmachineinstancemigration',
|
||||||
VM_TEMPLATE: 'harvesterhci.io.virtualmachinetemplate',
|
VM_TEMPLATE: 'harvesterhci.io.virtualmachinetemplate',
|
||||||
VM_VERSION: 'harvesterhci.io.virtualmachinetemplateversion',
|
VM_VERSION: 'harvesterhci.io.virtualmachinetemplateversion',
|
||||||
IMAGE: 'harvesterhci.io.virtualmachineimage',
|
IMAGE: 'harvesterhci.io.virtualmachineimage',
|
||||||
SSH: 'harvesterhci.io.keypair',
|
SSH: 'harvesterhci.io.keypair',
|
||||||
VOLUME: 'harvesterhci.io.volume',
|
VOLUME: 'harvesterhci.io.volume',
|
||||||
USER: 'harvesterhci.io.user',
|
USER: 'harvesterhci.io.user',
|
||||||
SETTING: 'harvesterhci.io.setting',
|
SETTING: 'harvesterhci.io.setting',
|
||||||
UPGRADE: 'harvesterhci.io.upgrade',
|
UPGRADE: 'harvesterhci.io.upgrade',
|
||||||
UPGRADE_LOG: 'harvesterhci.io.upgradelog',
|
UPGRADE_LOG: 'harvesterhci.io.upgradelog',
|
||||||
SCHEDULE_VM_BACKUP: 'harvesterhci.io.schedulevmbackup',
|
SCHEDULE_VM_BACKUP: 'harvesterhci.io.schedulevmbackup',
|
||||||
BACKUP: 'harvesterhci.io.virtualmachinebackup',
|
BACKUP: 'harvesterhci.io.virtualmachinebackup',
|
||||||
RESTORE: 'harvesterhci.io.virtualmachinerestore',
|
RESTORE: 'harvesterhci.io.virtualmachinerestore',
|
||||||
NODE_NETWORK: 'network.harvesterhci.io.nodenetwork',
|
NODE_NETWORK: 'network.harvesterhci.io.nodenetwork',
|
||||||
CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork',
|
CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork',
|
||||||
VM_IMAGE_DOWNLOADER: 'harvesterhci.io.virtualmachineimagedownloader',
|
SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle',
|
||||||
SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle',
|
NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',
|
||||||
NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',
|
CLUSTER: 'harvesterhci.io.management.cluster',
|
||||||
CLUSTER: 'harvesterhci.io.management.cluster',
|
DASHBOARD: 'harvesterhci.io.dashboard',
|
||||||
DASHBOARD: 'harvesterhci.io.dashboard',
|
BLOCK_DEVICE: 'harvesterhci.io.blockdevice',
|
||||||
BLOCK_DEVICE: 'harvesterhci.io.blockdevice',
|
CLOUD_TEMPLATE: 'harvesterhci.io.cloudtemplate',
|
||||||
CLOUD_TEMPLATE: 'harvesterhci.io.cloudtemplate',
|
HOST: 'harvesterhci.io.host',
|
||||||
HOST: 'harvesterhci.io.host',
|
VERSION: 'harvesterhci.io.version',
|
||||||
VERSION: 'harvesterhci.io.version',
|
SNAPSHOT: 'harvesterhci.io.volumesnapshot',
|
||||||
SNAPSHOT: 'harvesterhci.io.volumesnapshot',
|
VM_SNAPSHOT: 'harvesterhci.io.vmsnapshot',
|
||||||
VM_SNAPSHOT: 'harvesterhci.io.vmsnapshot',
|
ALERTMANAGERCONFIG: 'harvesterhci.io.monitoring.alertmanagerconfig',
|
||||||
ALERTMANAGERCONFIG: 'harvesterhci.io.monitoring.alertmanagerconfig',
|
CLUSTER_FLOW: 'harvesterhci.io.logging.clusterflow',
|
||||||
CLUSTER_FLOW: 'harvesterhci.io.logging.clusterflow',
|
CLUSTER_OUTPUT: 'harvesterhci.io.logging.clusteroutput',
|
||||||
CLUSTER_OUTPUT: 'harvesterhci.io.logging.clusteroutput',
|
FLOW: 'harvesterhci.io.logging.flow',
|
||||||
FLOW: 'harvesterhci.io.logging.flow',
|
OUTPUT: 'harvesterhci.io.logging.output',
|
||||||
OUTPUT: 'harvesterhci.io.logging.output',
|
STORAGE: 'harvesterhci.io.storage',
|
||||||
STORAGE: 'harvesterhci.io.storage',
|
RESOURCE_QUOTA: 'harvesterhci.io.resourcequota',
|
||||||
RESOURCE_QUOTA: 'harvesterhci.io.resourcequota',
|
KSTUNED: 'node.harvesterhci.io.ksmtuned',
|
||||||
KSTUNED: 'node.harvesterhci.io.ksmtuned',
|
PCI_DEVICE: 'devices.harvesterhci.io.pcidevice',
|
||||||
PCI_DEVICE: 'devices.harvesterhci.io.pcidevice',
|
PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim',
|
||||||
PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim',
|
SR_IOV: 'devices.harvesterhci.io.sriovnetworkdevice',
|
||||||
SR_IOV: 'devices.harvesterhci.io.sriovnetworkdevice',
|
VGPU_DEVICE: 'devices.harvesterhci.io.vgpudevice',
|
||||||
VGPU_DEVICE: 'devices.harvesterhci.io.vgpudevice',
|
SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice',
|
||||||
SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice',
|
USB_DEVICE: 'devices.harvesterhci.io.usbdevice',
|
||||||
USB_DEVICE: 'devices.harvesterhci.io.usbdevice',
|
USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim',
|
||||||
USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim',
|
VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig',
|
||||||
VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig',
|
VLAN_STATUS: 'network.harvesterhci.io.vlanstatus',
|
||||||
VLAN_STATUS: 'network.harvesterhci.io.vlanstatus',
|
ADD_ONS: 'harvesterhci.io.addon',
|
||||||
ADD_ONS: 'harvesterhci.io.addon',
|
LINK_MONITOR: 'network.harvesterhci.io.linkmonitor',
|
||||||
LINK_MONITOR: 'network.harvesterhci.io.linkmonitor',
|
SECRET: 'harvesterhci.io.secret',
|
||||||
SECRET: 'harvesterhci.io.secret',
|
INVENTORY: 'metal.harvesterhci.io.inventory',
|
||||||
INVENTORY: 'metal.harvesterhci.io.inventory',
|
LB: 'loadbalancer.harvesterhci.io.loadbalancer',
|
||||||
LB: 'loadbalancer.harvesterhci.io.loadbalancer',
|
IP_POOL: 'loadbalancer.harvesterhci.io.ippool',
|
||||||
IP_POOL: 'loadbalancer.harvesterhci.io.ippool',
|
HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig',
|
||||||
HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig',
|
LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup'
|
||||||
LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot';
|
export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot';
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export const UNIT_SUFFIX = 'i';
|
|
||||||
export const GIBIBYTE = 'Gi';
|
|
||||||
export const MEBIBYTE = 'Mi';
|
|
||||||
@@ -2,7 +2,6 @@ import { PVC } from '@shell/config/types';
|
|||||||
import { isValidMac, isValidDNSLabelName } from '@pkg/utils/regular';
|
import { isValidMac, isValidDNSLabelName } from '@pkg/utils/regular';
|
||||||
import { SOURCE_TYPE } from '@pkg/config/harvester-map';
|
import { SOURCE_TYPE } from '@pkg/config/harvester-map';
|
||||||
import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
|
||||||
import { GIBIBYTE } from '../utils/unit';
|
|
||||||
|
|
||||||
const maxNameLength = 63;
|
const maxNameLength = 63;
|
||||||
|
|
||||||
@@ -89,7 +88,7 @@ export function vmDisks(spec, getters, errors, validatorArgs, displayKey, value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeValue?.spec?.resources?.requests?.storage && !/^([0-9][0-9]{0,8})[a-zA-Z]+$/.test(typeValue?.spec?.resources?.requests?.storage)) {
|
if (typeValue?.spec?.resources?.requests?.storage && !/^([0-9][0-9]{0,8})[a-zA-Z]+$/.test(typeValue?.spec?.resources?.requests?.storage)) {
|
||||||
const message = getters['i18n/t']('harvester.validation.generic.maximumSize', { max: `999999999 ${ GIBIBYTE }` });
|
const message = getters['i18n/t']('harvester.validation.generic.maximumSize', { max: '999999999 GiB' });
|
||||||
|
|
||||||
errors.push(getters['i18n/t']('harvester.validation.generic.tabError', { prefix, message }));
|
errors.push(getters['i18n/t']('harvester.validation.generic.tabError', { prefix, message }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { GIBIBYTE } from '../utils/unit';
|
|
||||||
|
|
||||||
export function volumeSize(size, getters, errors, validatorArgs, displayKey, value) {
|
export function volumeSize(size, getters, errors, validatorArgs, displayKey, value) {
|
||||||
if (!size) {
|
if (!size) {
|
||||||
const key = getters['i18n/t']('harvester.volume.size');
|
const key = getters['i18n/t']('harvester.volume.size');
|
||||||
@@ -8,7 +6,7 @@ export function volumeSize(size, getters, errors, validatorArgs, displayKey, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (size && !/^([0-9][0-9]{0,8})[a-zA-Z]+$/.test(size)) {
|
if (size && !/^([0-9][0-9]{0,8})[a-zA-Z]+$/.test(size)) {
|
||||||
const message = getters['i18n/t']('harvester.validation.generic.maximumSize', { max: `999999999 ${ GIBIBYTE }` });
|
const message = getters['i18n/t']('harvester.validation.generic.maximumSize', { max: '999999999 GiB' });
|
||||||
|
|
||||||
errors.push(message);
|
errors.push(message);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user