mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-01 11:46:06 +08:00
Compare commits
30
Commits
v1.5.0-rc1
...
v1.5.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2e8cce29f | ||
|
|
31682dd165 | ||
|
|
9fac43d5e5 | ||
|
|
cd6ea6e402 | ||
|
|
3bfe57dc54 | ||
|
|
089573f193 | ||
|
|
5d0c4f0c5a | ||
|
|
57bb1e8562 | ||
|
|
30ff129691 | ||
|
|
9175f16bfc | ||
|
|
7c5e0f93d1 | ||
|
|
8a7d3dffbe | ||
|
|
b7810c5970 | ||
|
|
72dedd46d1 | ||
|
|
24f716ab20 | ||
|
|
38ad0fe1a7 | ||
|
|
c84d262099 | ||
|
|
dbb53eae00 | ||
|
|
4afacd754a | ||
|
|
10d159e2c7 | ||
|
|
de755a662a | ||
|
|
f1200f4508 | ||
|
|
0ca2b0591a | ||
|
|
44f003309c | ||
|
|
7c1596c965 | ||
|
|
a5130986a8 | ||
|
|
e4ae7cec5e | ||
|
|
2c7b59daed | ||
|
|
224e34315b | ||
|
|
9f56f9eccc |
@@ -0,0 +1,35 @@
|
||||
name: Build Harvester Catalog Image and Publish on Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: ./
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run tests
|
||||
uses: ./.github/actions/lint
|
||||
|
||||
build-and-push-extension-catalog:
|
||||
needs: lint
|
||||
uses: ./.github/workflows/build-extension-catalog.yml
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
with:
|
||||
registry_target: docker.io
|
||||
registry_user: rancher
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Build and release Extension Catalog Image to registry
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
registry_target:
|
||||
required: true
|
||||
type: string
|
||||
registry_user:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
build-extension-catalog-job-status:
|
||||
value: ${{ jobs.build-extension-catalog.outputs.build-status }}
|
||||
|
||||
jobs:
|
||||
build-and-push-extension-catalog:
|
||||
name: Build container image
|
||||
if: github.ref_type == 'tag' || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
outputs:
|
||||
build-status: ${{ job.status }}
|
||||
steps:
|
||||
- name: Read Secrets
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
with:
|
||||
secrets: |
|
||||
secret/data/github/repo/${{ github.repository }}/dockerhub/rancher/credentials username | DOCKER_USERNAME ;
|
||||
secret/data/github/repo/${{ github.repository }}/dockerhub/rancher/credentials password | DOCKER_PASSWORD ;
|
||||
|
||||
- name: Checkout repository (normal flow)
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ env.DOCKER_USERNAME }}
|
||||
password: ${{ env.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Setup Helm
|
||||
uses: azure/setup-helm@v3
|
||||
with:
|
||||
version: v3.8.0
|
||||
|
||||
- name: Setup Nodejs with yarn caching
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build and push UI image
|
||||
run: |
|
||||
publish="yarn publish-pkgs -cp -r ${{ inputs.registry_target }} -o ${{ inputs.registry_user }}"
|
||||
$publish
|
||||
@@ -11,6 +11,24 @@ defaults:
|
||||
working-directory: ./
|
||||
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -22,6 +40,7 @@ jobs:
|
||||
uses: ./.github/actions/lint
|
||||
build-extension-charts:
|
||||
needs:
|
||||
- setup-release-tag
|
||||
- lint
|
||||
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
|
||||
permissions:
|
||||
@@ -31,4 +50,4 @@ jobs:
|
||||
pages: write
|
||||
with:
|
||||
target_branch: gh-pages
|
||||
tagged_release: ${{ github.ref_name }}
|
||||
tagged_release: '${{ needs.setup-release-tag.outputs.release_tag }}'
|
||||
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "harvester-ui-extension",
|
||||
"version": "1.5.0-rc1",
|
||||
"version": "1.5.0-rc5",
|
||||
"private": false,
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rancher/shell": "3.0.2-rc.3",
|
||||
"@rancher/shell": "3.0.2-rc.6",
|
||||
"cache-loader": "^4.1.0",
|
||||
"color": "4.2.3",
|
||||
"ip": "2.0.1",
|
||||
|
||||
@@ -301,14 +301,14 @@ export default {
|
||||
|
||||
<ProgressBarList
|
||||
:title="t('harvester.upgradePage.upgradeNode')"
|
||||
:precent="nodesPercent"
|
||||
:percent="nodesPercent"
|
||||
:list="nodesStatus"
|
||||
/>
|
||||
<p class="bordered-section"></p>
|
||||
|
||||
<ProgressBarList
|
||||
:title="t('harvester.upgradePage.upgradeSysService')"
|
||||
:precent="sysServiceTotal"
|
||||
:percent="sysServiceTotal"
|
||||
:list="sysServiceUpgradeMessage"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -162,6 +162,10 @@ export default {
|
||||
this.configArr.splice(idx, 1);
|
||||
},
|
||||
|
||||
isBackupVolumeSnapshotRequired(driver) {
|
||||
return driver === LONGHORN_DRIVER;
|
||||
},
|
||||
|
||||
disableEdit(driver) {
|
||||
return driver === LONGHORN_DRIVER;
|
||||
},
|
||||
@@ -170,10 +174,6 @@ export default {
|
||||
return driver === LONGHORN_DRIVER || this.allowEmptySnapshotClassNameFeatureEnabled;
|
||||
},
|
||||
|
||||
isBackupVolumeSnapshotClassNameSelectable(driver) {
|
||||
return driver === LONGHORN_DRIVER;
|
||||
},
|
||||
|
||||
add() {
|
||||
this.configArr.push({
|
||||
key: '',
|
||||
@@ -196,6 +196,7 @@ export default {
|
||||
<InfoBox
|
||||
v-for="(driver, idx) in configArr"
|
||||
:key="idx"
|
||||
class="box"
|
||||
>
|
||||
<button
|
||||
:disabled="disableEdit(driver.key)"
|
||||
@@ -238,11 +239,10 @@ export default {
|
||||
<LabeledSelect
|
||||
v-model:value="driver.value.backupVolumeSnapshotClassName"
|
||||
:mode="mode"
|
||||
required
|
||||
:disabled="isBackupVolumeSnapshotClassNameDisabled(driver.key)"
|
||||
:options="getVolumeSnapshotOptions(driver.key)"
|
||||
:required="isBackupVolumeSnapshotRequired(driver.key)"
|
||||
:label="t('harvester.setting.csiDriverConfig.backupVolumeSnapshotClassName')"
|
||||
@selectable="isBackupVolumeSnapshotClassNameSelectable(driver.key)"
|
||||
@keydown.native.enter.prevent="()=>{}"
|
||||
@update:value="update"
|
||||
/>
|
||||
|
||||
@@ -50,6 +50,7 @@ export const HCI = {
|
||||
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
|
||||
ADDON_EXPERIMENTAL: 'addon.harvesterhci.io/experimental',
|
||||
VOLUME_ERROR: 'longhorn.io/volume-scheduling-error',
|
||||
VOLUME_FOR_VM: 'harvesterhci.io/volumeForVirtualMachine',
|
||||
KVM_AMD_CPU: 'cpu-feature.node.kubevirt.io/svm',
|
||||
KVM_INTEL_CPU: 'cpu-feature.node.kubevirt.io/vmx',
|
||||
NODE_MANUFACTURER: 'manufacturer',
|
||||
|
||||
@@ -7,3 +7,8 @@ export const NETWORK_TYPE = {
|
||||
L2VLAN: 'L2VlanNetwork',
|
||||
UNTAGGED: 'UntaggedNetwork'
|
||||
};
|
||||
|
||||
export const VOLUME_MODE = {
|
||||
BLOCK: 'Block',
|
||||
FILE_SYSTEM: 'Filesystem'
|
||||
};
|
||||
|
||||
@@ -138,6 +138,8 @@ export default {
|
||||
<Banner
|
||||
v-for="(error, i) in errors"
|
||||
:key="i"
|
||||
:label="error"
|
||||
color="error"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -166,6 +166,8 @@ export default {
|
||||
<Banner
|
||||
v-for="(error, i) in errors"
|
||||
:key="i"
|
||||
:label="error"
|
||||
color="error"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -106,6 +106,12 @@ export default {
|
||||
:label="t('harvester.modal.cloneVM.name')"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
:label="err"
|
||||
color="error"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -125,13 +131,6 @@ export default {
|
||||
@click="create"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -57,6 +57,10 @@ export default {
|
||||
if (!!pvc.metadata?.annotations?.[HCI_ANNOTATIONS.IMAGE_ID]) {
|
||||
return false;
|
||||
}
|
||||
// we won't show golden image volume in the hot plug volume modal
|
||||
if (pvc.isGoldenImageVolume) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !pvc.attachVM;
|
||||
})
|
||||
@@ -137,6 +141,12 @@ export default {
|
||||
class="mt-20"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
:label="err"
|
||||
color="error"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -156,11 +166,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -103,6 +103,12 @@ export default {
|
||||
:label="t('generic.name')"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -121,13 +127,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -120,6 +120,12 @@ export default {
|
||||
v-model:value="description"
|
||||
:label="t('harvester.modal.createTemplate.description')"
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -138,11 +144,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -128,6 +128,12 @@ export default {
|
||||
<Banner color="warning">
|
||||
<span>{{ t('harvester.modal.ejectCDROM.warnTip') }}</span>
|
||||
</Banner>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -147,13 +153,6 @@ export default {
|
||||
@click="remove"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -187,6 +187,12 @@ export default {
|
||||
class="mt-20"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -205,11 +211,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -122,16 +122,15 @@ export default {
|
||||
</Banner>
|
||||
{{ t('harvester.modal.downloadImage.startMessage') }}
|
||||
<br /><br />
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
>
|
||||
{{ err }}
|
||||
</Banner>
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<div class="actions">
|
||||
<div class="buttons">
|
||||
<button
|
||||
|
||||
@@ -161,6 +161,12 @@ export default {
|
||||
:placeholder="t('harvester.modal.migration.fields.nodeName.placeholder')"
|
||||
:options="nodeNameList"
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template
|
||||
@@ -181,12 +187,6 @@ export default {
|
||||
@click="apply"
|
||||
/>
|
||||
</div>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
@@ -96,6 +96,12 @@ export default {
|
||||
:suffix="GIBIBYTE"
|
||||
class="mb-20"
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -109,12 +115,6 @@ export default {
|
||||
</button>
|
||||
<AsyncButton @click="save" />
|
||||
</div>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -136,6 +136,12 @@ export default {
|
||||
:options="backupOption"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -154,11 +160,6 @@ export default {
|
||||
@click="saveRestore"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -90,6 +90,12 @@ export default {
|
||||
v-clean-html="t('harvester.virtualMachine.unplug.title', { name: diskName })"
|
||||
class="text-default-text"
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -111,13 +117,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -132,6 +132,12 @@ export default {
|
||||
:label="t('generic.name')"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -150,11 +156,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -107,6 +107,12 @@ export default {
|
||||
:placeholder="t('harvester.harvesterVlanConfigMigrateDialog.targetClusterNetwork.placeholder')"
|
||||
:options="clusterNetworks"
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -125,11 +131,6 @@ export default {
|
||||
@click="apply"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -70,16 +70,16 @@ export default {
|
||||
v-clean-html="t('harvester.modal.restart.tip')"
|
||||
class="pl-10 pr-10"
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<div class="bottom">
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
<div class="buttons">
|
||||
<button
|
||||
class="btn role-secondary mr-10"
|
||||
|
||||
@@ -145,6 +145,12 @@ export default {
|
||||
class="mt-20"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -162,10 +168,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -79,6 +79,12 @@ export default {
|
||||
:label="t('harvester.modal.snapshot.name')"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -96,12 +102,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -98,6 +98,12 @@ export default {
|
||||
:label="t('harvester.modal.volumeClone.name')"
|
||||
required
|
||||
/>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
@@ -115,10 +121,6 @@ export default {
|
||||
@click="save"
|
||||
/>
|
||||
</div>
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Banner } from '@components/Banner';
|
||||
|
||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||
|
||||
const DEFAULT_VALUE = { image: { repo: 'rancher/harvester-nvidia-driver-toolkit' } };
|
||||
const DEFAULT_VALUE = { image: { repository: 'rancher/harvester-nvidia-driver-toolkit' } };
|
||||
|
||||
export default {
|
||||
name: 'EditAddonNvidiaDriverToolkit',
|
||||
@@ -122,7 +122,7 @@ export default {
|
||||
>
|
||||
<div class="col span-6">
|
||||
<LabeledInput
|
||||
v-model:value="valuesContentJson.image.repo"
|
||||
v-model:value="valuesContentJson.image.repository"
|
||||
:mode="mode"
|
||||
:required="true"
|
||||
label-key="harvester.addons.nvidiaDriverToolkit.image.repository"
|
||||
|
||||
@@ -22,6 +22,7 @@ import { HCI, VOLUME_SNAPSHOT } from '../types';
|
||||
import { LVM_DRIVER } from '../models/harvester/storage.k8s.io.storageclass';
|
||||
import { DATA_ENGINE_V2 } from '../models/harvester/persistentvolumeclaim';
|
||||
import { GIBIBYTE } from '../utils/unit';
|
||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterVolume',
|
||||
@@ -73,7 +74,8 @@ export default {
|
||||
|
||||
data() {
|
||||
if (this.mode === _CREATE) {
|
||||
this.value.spec.volumeMode = 'Block';
|
||||
// default volumeMode to Block
|
||||
this.value.spec.volumeMode = VOLUME_MODE.BLOCK;
|
||||
this.value.spec.accessModes = ['ReadWriteMany'];
|
||||
}
|
||||
|
||||
@@ -126,6 +128,10 @@ export default {
|
||||
return InterfaceOption;
|
||||
},
|
||||
|
||||
volumeModeOptions() {
|
||||
return Object.values(VOLUME_MODE);
|
||||
},
|
||||
|
||||
imageOption() {
|
||||
return sortBy(
|
||||
this.images
|
||||
@@ -179,6 +185,25 @@ export default {
|
||||
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() {
|
||||
return this.storageClasses.filter((s) => !s.parameters?.backingImage).map((s) => {
|
||||
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
|
||||
@@ -237,7 +262,43 @@ 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: {
|
||||
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() {
|
||||
if (!this.longhornV2LVMSupport) {
|
||||
return ['ReadWriteMany'];
|
||||
@@ -246,10 +307,10 @@ export default {
|
||||
const storageClassName = this.value.spec.storageClassName;
|
||||
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
||||
|
||||
let readWriteOnce = this.value.isLvm || this.value.isLonghornV2;
|
||||
let readWriteOnce = this.value.isLvm || (!this.value.thirdPartyStorageFeatureEnabled && this.value.isLonghornV2);
|
||||
|
||||
if (storageClass) {
|
||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || storageClass.parameters?.dataEngine === DATA_ENGINE_V2;
|
||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.value.thirdPartyStorageFeatureEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
||||
}
|
||||
|
||||
return readWriteOnce ? ['ReadWriteOnce'] : ['ReadWriteMany'];
|
||||
@@ -359,7 +420,7 @@ export default {
|
||||
/>
|
||||
|
||||
<LabeledSelect
|
||||
v-if="source === 'blank'"
|
||||
v-if="isBlank"
|
||||
v-model:value="value.spec.storageClassName"
|
||||
:options="storageClassOptions"
|
||||
:label="t('harvester.storage.storageClass.label')"
|
||||
@@ -369,6 +430,18 @@ export default {
|
||||
@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
|
||||
v-model:value="storage"
|
||||
:label="t('harvester.volume.size')"
|
||||
|
||||
@@ -97,7 +97,7 @@ export default {
|
||||
});
|
||||
|
||||
const devices = [
|
||||
...this.otherDevices(this.value.domain.devices.hostDevices || []),
|
||||
...this.otherDevices(this.value?.domain?.devices?.hostDevices || []),
|
||||
...formatted,
|
||||
];
|
||||
|
||||
@@ -121,7 +121,9 @@ export default {
|
||||
return inUse;
|
||||
}
|
||||
|
||||
vm.hostDevices.forEach((device) => {
|
||||
const hostDevices = vm?.hostDevices || [];
|
||||
|
||||
hostDevices.forEach((device) => {
|
||||
inUse[device.name] = { usedBy: [vm.metadata.name] };
|
||||
});
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ export default {
|
||||
});
|
||||
|
||||
const devices = [
|
||||
...this.otherDevices(this.value.domain.devices.hostDevices || []),
|
||||
...this.otherDevices(this.value?.domain?.devices?.hostDevices || []),
|
||||
...formatted,
|
||||
];
|
||||
|
||||
@@ -121,7 +121,9 @@ export default {
|
||||
return inUse;
|
||||
}
|
||||
|
||||
vm.hostDevices.forEach((device) => {
|
||||
const hostDevices = vm?.hostDevices || [];
|
||||
|
||||
hostDevices.forEach((device) => {
|
||||
inUse[device.name] = { usedBy: [vm.metadata.name] };
|
||||
});
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ import UnitInput from '@shell/components/form/UnitInput';
|
||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||
import ModalWithCard from '@shell/components/ModalWithCard';
|
||||
|
||||
import { PVC } from '@shell/config/types';
|
||||
import { PVC, STORAGE_CLASS } from '@shell/config/types';
|
||||
import { clone } from '@shell/utils/object';
|
||||
import { ucFirst, randomStr } from '@shell/utils/string';
|
||||
import { removeObject } from '@shell/utils/array';
|
||||
@@ -17,6 +16,7 @@ import { PLUGIN_DEVELOPER, DEV } from '@shell/store/prefs';
|
||||
import { SOURCE_TYPE } from '../../../config/harvester-map';
|
||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../config/harvester';
|
||||
import { HCI } from '../../../types';
|
||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
||||
|
||||
export default {
|
||||
emits: ['update:value'],
|
||||
@@ -62,7 +62,7 @@ export default {
|
||||
|
||||
customVolumeMode: {
|
||||
type: String,
|
||||
default: 'Block'
|
||||
default: VOLUME_MODE.Block
|
||||
},
|
||||
|
||||
customAccessMode: {
|
||||
@@ -116,6 +116,12 @@ export default {
|
||||
return this.mode === _CREATE;
|
||||
},
|
||||
|
||||
defaultStorageClass() {
|
||||
const defaultStorage = this.$store.getters['harvester/all'](STORAGE_CLASS).find((sc) => sc.isDefault);
|
||||
|
||||
return defaultStorage;
|
||||
},
|
||||
|
||||
showVolumeTip() {
|
||||
const imageName = this.getImageDisplayName(this.rows[0]?.image);
|
||||
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ export default {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pvc.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] === 'true') {
|
||||
if (pvc.isGoldenImageVolume) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ucFirst } from '@shell/utils/string';
|
||||
import { LVM_DRIVER } from '../../../../models/harvester/storage.k8s.io.storageclass';
|
||||
import { DATA_ENGINE_V2 } from '../../../../models/harvester/persistentvolumeclaim';
|
||||
import { GIBIBYTE } from '../../../../utils/unit';
|
||||
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterEditVolume',
|
||||
@@ -71,6 +72,10 @@ export default {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('longhornV2LVMSupport');
|
||||
},
|
||||
|
||||
thirdPartyStorageClassEnabled() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
||||
},
|
||||
|
||||
encryptionValue() {
|
||||
return ucFirst(String(this.value.isEncrypted));
|
||||
},
|
||||
@@ -91,6 +96,18 @@ export default {
|
||||
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() {
|
||||
return !this.value.newCreateId && this.isEdit && this.isVirtualType;
|
||||
},
|
||||
@@ -112,7 +129,17 @@ export default {
|
||||
|
||||
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: {
|
||||
@@ -120,6 +147,8 @@ export default {
|
||||
immediate: true,
|
||||
handler(neu) {
|
||||
this.value.accessMode = this.getAccessMode(neu);
|
||||
this.value.volumeMode = this.getVolumeMode(neu, this.value.volumeMode);
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -151,6 +180,20 @@ export default {
|
||||
},
|
||||
|
||||
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) {
|
||||
if (!this.longhornV2LVMSupport) {
|
||||
return 'ReadWriteMany';
|
||||
@@ -158,10 +201,10 @@ export default {
|
||||
|
||||
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
||||
|
||||
let readWriteOnce = this.value.pvc?.isLvm || this.value.pvc?.isLonghornV2;
|
||||
let readWriteOnce = this.value.pvc?.isLvm || (!this.thirdPartyStorageClassEnabled && this.value.pvc?.isLonghornV2);
|
||||
|
||||
if (storageClass) {
|
||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || storageClass.parameters?.dataEngine === DATA_ENGINE_V2;
|
||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.thirdPartyStorageClassEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
||||
}
|
||||
|
||||
return readWriteOnce ? 'ReadWriteOnce' : 'ReadWriteMany';
|
||||
@@ -286,6 +329,29 @@ export default {
|
||||
/>
|
||||
</InputOrDisplay>
|
||||
</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
|
||||
v-if="value.volumeEncryptionFeatureEnabled && isView"
|
||||
class="col span-6"
|
||||
@@ -295,11 +361,9 @@ export default {
|
||||
:value="encryptionValue"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-20">
|
||||
<div
|
||||
v-if="value.volumeBackups && isView"
|
||||
class="col span-3"
|
||||
class="col span-6"
|
||||
>
|
||||
<LabelValue
|
||||
:name="t('harvester.virtualMachine.volume.readyToUse')"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { isEqual } from 'lodash';
|
||||
import { mapGetters } from 'vuex';
|
||||
import Tabbed from '@shell/components/Tabbed';
|
||||
import { clone, set } from '@shell/utils/object';
|
||||
import Tab from '@shell/components/Tabbed/Tab';
|
||||
import { Checkbox } from '@components/Form/Checkbox';
|
||||
import CruResource from '@shell/components/CruResource';
|
||||
@@ -15,9 +16,7 @@ import PodAffinity from '@shell/components/form/PodAffinity';
|
||||
import VGpuDevices from './VirtualMachineVGpuDevices/index';
|
||||
import UsbDevices from './VirtualMachineUSBDevices/index';
|
||||
import KeyValue from '@shell/components/form/KeyValue';
|
||||
|
||||
import { clear } from '@shell/utils/array';
|
||||
import { clone } from '@shell/utils/object';
|
||||
import { saferDump } from '@shell/utils/create-yaml';
|
||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||
@@ -212,7 +211,10 @@ export default {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
usbPassthroughEnabled() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('usbPassthrough');
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -409,13 +411,26 @@ export default {
|
||||
}
|
||||
const cloneDeepNewVM = clone(this.value);
|
||||
|
||||
// new VM
|
||||
delete cloneDeepNewVM?.metadata;
|
||||
delete cloneDeepNewVM?.__clone;
|
||||
|
||||
// old VM
|
||||
delete this.cloneVM?.metadata;
|
||||
delete this.cloneVM?.__clone;
|
||||
|
||||
// add empty hostDevices to old VM as CRD does not have it.
|
||||
const devicesObj = this.cloneVM?.spec?.template?.spec?.domain?.devices;
|
||||
|
||||
if (devicesObj && devicesObj.hostDevices === undefined) {
|
||||
set(devicesObj, 'hostDevices', []);
|
||||
}
|
||||
|
||||
const oldVM = JSON.parse(JSON.stringify(this.cloneVM));
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
@@ -424,7 +439,11 @@ export default {
|
||||
this.isOpen = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.restartDialog.resolve = resolve;
|
||||
if (this?.$refs?.restartDialog) {
|
||||
this.$refs.restartDialog.resolve = resolve;
|
||||
} else {
|
||||
return resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -474,6 +493,7 @@ export default {
|
||||
},
|
||||
|
||||
generateYaml() {
|
||||
this.showYaml = this?.$refs?.vmCruResource?.showYaml || false;
|
||||
this.parseVM();
|
||||
const out = saferDump(this.value);
|
||||
|
||||
@@ -487,6 +507,7 @@ export default {
|
||||
<CruResource
|
||||
v-if="spec"
|
||||
id="vm"
|
||||
ref="vmCruResource"
|
||||
:done-route="doneRoute"
|
||||
:resource="value"
|
||||
:cancel-event="true"
|
||||
@@ -674,7 +695,7 @@ export default {
|
||||
</Tab>
|
||||
|
||||
<Tab
|
||||
v-if="enabledPCI"
|
||||
v-if="enabledPCI && usbPassthroughEnabled"
|
||||
:label="t('harvester.tab.usbDevices')"
|
||||
name="usbDevices"
|
||||
:weight="-7"
|
||||
|
||||
@@ -791,6 +791,7 @@ harvester:
|
||||
datasource: Data Source
|
||||
details: Details
|
||||
size: Size
|
||||
volumeMode: Volume Mode
|
||||
source: Source
|
||||
kind: Kind
|
||||
sourceOptions:
|
||||
|
||||
@@ -101,14 +101,14 @@ export default {
|
||||
const metricCol = [
|
||||
{
|
||||
name: 'cpu',
|
||||
labelKey: 'node.detail.glance.consumptionGauge.cpu',
|
||||
labelKey: 'harvester.dashboard.hardwareResourceGauge.cpu',
|
||||
value: 'id',
|
||||
formatter: 'HarvesterCPUUsed',
|
||||
formatterOpts: { showUsed: true },
|
||||
},
|
||||
{
|
||||
name: 'memory',
|
||||
labelKey: 'node.detail.glance.consumptionGauge.memory',
|
||||
labelKey: 'harvester.dashboard.hardwareResourceGauge.memory',
|
||||
value: 'id',
|
||||
formatter: 'HarvesterMemoryUsed',
|
||||
formatterOpts: { showUsed: true },
|
||||
|
||||
@@ -6,10 +6,8 @@ import {
|
||||
} from '@shell/config/types';
|
||||
import { STATE, AGE, NAME, NAMESPACE } from '@shell/config/table-headers';
|
||||
import HarvesterVolumeState from '../formatters/HarvesterVolumeState';
|
||||
|
||||
import { allSettled } from '../utils/promise';
|
||||
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||
|
||||
const schema = {
|
||||
id: HCI.VOLUME,
|
||||
@@ -59,10 +57,7 @@ export default {
|
||||
if (!pvcSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
|
||||
this.$store.dispatch('type-map/configureType', { match: HCI.VOLUME, isCreatable: false });
|
||||
}
|
||||
// we only show the non golden image PVCs in the list
|
||||
const pvcs = hash.pvcs.filter((pvc) => pvc?.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] !== 'true');
|
||||
|
||||
this.rows = pvcs;
|
||||
this.rows = hash.pvcs;
|
||||
},
|
||||
|
||||
data() {
|
||||
@@ -73,7 +68,10 @@ export default {
|
||||
schema() {
|
||||
return schema;
|
||||
},
|
||||
|
||||
filterRows() {
|
||||
// we only show the non golden image PVCs in the list
|
||||
return this.rows.filter((pvc) => !pvc?.isGoldenImageVolume);
|
||||
},
|
||||
headers() {
|
||||
return [
|
||||
STATE,
|
||||
@@ -150,7 +148,7 @@ export default {
|
||||
:groupable="true"
|
||||
default-sort-by="age"
|
||||
:namespaced="true"
|
||||
:rows="rows"
|
||||
:rows="filterRows"
|
||||
:schema="schema"
|
||||
key-field="_key"
|
||||
>
|
||||
|
||||
@@ -26,6 +26,7 @@ import { HCI } from '../../types';
|
||||
import { parseVolumeClaimTemplates } from '../../utils/vm';
|
||||
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';
|
||||
|
||||
@@ -139,6 +140,7 @@ export default {
|
||||
return {
|
||||
OS,
|
||||
isClone,
|
||||
showYaml: false,
|
||||
spec: null,
|
||||
osType: 'linux',
|
||||
sshKey: [],
|
||||
@@ -244,12 +246,6 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
defaultStorageClass() {
|
||||
const defaultStorage = this.$store.getters[`${ this.inStore }/all`](STORAGE_CLASS).find( (O) => O.isDefault);
|
||||
|
||||
return defaultStorage;
|
||||
},
|
||||
|
||||
storageClassSetting() {
|
||||
try {
|
||||
const storageClassValue = this.$store.getters[`${ this.inStore }/all`](HCI.SETTING).find( (O) => O.id === HCI_SETTING.DEFAULT_STORAGE_CLASS)?.value;
|
||||
@@ -261,7 +257,7 @@ export default {
|
||||
},
|
||||
|
||||
customVolumeMode() {
|
||||
return this.storageClassSetting.volumeMode || 'Block';
|
||||
return this.storageClassSetting.volumeMode || VOLUME_MODE.BLOCK;
|
||||
},
|
||||
|
||||
customAccessMode() {
|
||||
@@ -274,7 +270,7 @@ export default {
|
||||
|
||||
needNewSecret() {
|
||||
// When creating a template it is always necessary to create a new secret.
|
||||
return this.resourceType === HCI.VM_VERSION || this.isCreate;
|
||||
return this.showYaml ? false : this.resourceType === HCI.VM_VERSION || this.isCreate;
|
||||
},
|
||||
|
||||
defaultTerminationSetting() {
|
||||
@@ -463,7 +459,7 @@ export default {
|
||||
type,
|
||||
storageClassName: '',
|
||||
image: this.imageId,
|
||||
volumeMode: 'Block',
|
||||
volumeMode: VOLUME_MODE.BLOCK,
|
||||
isEncrypted,
|
||||
volumeBackups,
|
||||
});
|
||||
@@ -522,7 +518,7 @@ export default {
|
||||
accessMode = pvcResource?.spec?.accessModes?.[0] || 'ReadWriteMany';
|
||||
size = pvcResource?.spec?.resources?.requests?.storage || '10Gi';
|
||||
storageClassName = pvcResource?.spec?.storageClassName;
|
||||
volumeMode = pvcResource?.spec?.volumeMode || 'Block';
|
||||
volumeMode = pvcResource?.spec?.volumeMode || VOLUME_MODE.BLOCK;
|
||||
volumeName = pvcResource?.metadata?.name || '';
|
||||
}
|
||||
|
||||
@@ -674,7 +670,7 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.secretName || this.needNewSecret) {
|
||||
if (this.needNewSecret || !this.secretName) {
|
||||
this.secretName = this.generateSecretName(this.secretNamePrefix);
|
||||
}
|
||||
|
||||
@@ -876,7 +872,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (out.length === 0 && !!this.spec.template.spec.accessCredentials) {
|
||||
if (out.length === 0 && !!this.spec.template.spec.accessCredentials === false) {
|
||||
delete this.spec.template.spec.accessCredentials;
|
||||
} else {
|
||||
this.spec.template.spec.accessCredentials = out;
|
||||
|
||||
@@ -66,4 +66,8 @@ export default class HarvesterResource extends SteveModel {
|
||||
return this.hasLink('self') && this.$rootGetters['prefs/get'](DEV);
|
||||
}
|
||||
}
|
||||
|
||||
cleanForSave(data, _forNew) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,6 +344,10 @@ export default class HciPv extends HarvesterResource {
|
||||
return this.dataEngine === DATA_ENGINE_V2;
|
||||
}
|
||||
|
||||
get isGoldenImageVolume() {
|
||||
return this?.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] === 'true';
|
||||
}
|
||||
|
||||
get thirdPartyStorageFeatureEnabled() {
|
||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@ export default class HciStorageClass extends StorageClass {
|
||||
return this.parameters?.encrypted === 'true';
|
||||
}
|
||||
|
||||
get isLonghorn() {
|
||||
return this.provisioner === LONGHORN_DRIVER;
|
||||
}
|
||||
|
||||
get isLonghornV1() {
|
||||
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V1;
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ export default class HciUpgrade extends HarvesterResource {
|
||||
return this?.status?.imageID;
|
||||
}
|
||||
|
||||
cleanForSave(data, _forNew) {
|
||||
return data;
|
||||
}
|
||||
|
||||
get upgradeMessage() {
|
||||
const upgradeMessage = [];
|
||||
const nodeStatuses = this?.status?.nodeStatuses || {};
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MODE, _CREATE } from '@shell/config/query-params';
|
||||
import { HCI } from '../types';
|
||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
||||
import HarvesterResource from './harvester';
|
||||
import { clone } from '@shell/utils/object';
|
||||
|
||||
export default class HciVmTemplate extends HarvesterResource {
|
||||
get availableActions() {
|
||||
@@ -71,4 +72,12 @@ export default class HciVmTemplate extends HarvesterResource {
|
||||
get defaultVersion() {
|
||||
return this.status?.defaultVersion;
|
||||
}
|
||||
|
||||
get doneOverride() {
|
||||
const detailLocation = clone(this.listLocation);
|
||||
|
||||
detailLocation.params.resource = HCI.VM_VERSION;
|
||||
|
||||
return detailLocation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ export default class VirtVm extends HarvesterResource {
|
||||
},
|
||||
{
|
||||
action: 'takeVMSnapshot',
|
||||
enabled: !!this.actions?.backup && !this.longhornV2Volumes.length,
|
||||
enabled: (!!this.actions?.snapshot || !!this.action?.backup) && !this.longhornV2Volumes.length,
|
||||
icon: 'icon icon-snapshot',
|
||||
label: this.t('harvester.action.vmSnapshot')
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "harvester",
|
||||
"description": "Rancher UI Extension for Harvester",
|
||||
"version": "1.5.0-rc1",
|
||||
"version": "1.5.0-rc5",
|
||||
"private": false,
|
||||
"rancher": {
|
||||
"annotations": {
|
||||
|
||||
@@ -16,14 +16,12 @@ export default {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
names: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
@@ -69,12 +67,6 @@ export default {
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
plusMore() {
|
||||
const remaining = this.toRemove.length - this.names.length;
|
||||
|
||||
return this.t('promptRemove.andOthers', { count: remaining });
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -143,7 +135,7 @@ export default {
|
||||
<div>
|
||||
<div class="mt-10">
|
||||
{{ t('promptRemove.attemptingToRemove', {type}) }}
|
||||
<span v-clean-html="resourceNames(names, plusMore, t)"></span>
|
||||
<span v-clean-html="resourceNames(names, t)"></span>
|
||||
|
||||
<div class="mt-10">
|
||||
{{ t('harvester.virtualMachine.promptRemove.title') }}
|
||||
|
||||
@@ -35,7 +35,17 @@ function latestMinorVersion(v) {
|
||||
}
|
||||
|
||||
export const featureEnabled = (featureKey, serverVersion) => {
|
||||
const minSupportedVersion = '1.3.0';
|
||||
|
||||
const version = getVersion(serverVersion);
|
||||
|
||||
if (semver.lt(version.replace('v', ''), minSupportedVersion)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Harvester UI extension only supports Harvester cluster version >= ${ minSupportedVersion }. Current version: ${ version }`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let releasedFeatures = RELEASE_FEATURES[version];
|
||||
|
||||
if (!releasedFeatures) {
|
||||
|
||||
@@ -3200,10 +3200,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.29.tgz#6546d69768c706bebd66bfa73b36aef3d105987a"
|
||||
integrity sha512-qBBqfazS9y5VjV7fJDPNXmxd9AP/2uiE05mKFWP41kpbO+tEb62RnUBXCm14XLeScDZQcOuiAKVHMmvCFzF0BA==
|
||||
|
||||
"@rancher/shell@3.0.2-rc.3":
|
||||
version "3.0.2-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.2-rc.3.tgz#745d4970425ba0bddf1a8003ff7f82d408566da8"
|
||||
integrity sha512-K5jCaNfyltjCSTQUgzFFntmO5HT1Uu+vM1aJsMCjiwiyrNPBsf0Z3ic9Is6HSvOs9DB+W5j7jvHwouuq0sxUlw==
|
||||
"@rancher/shell@3.0.2-rc.6":
|
||||
version "3.0.2-rc.6"
|
||||
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.2-rc.6.tgz#bde65a3da391c4aa0352e2adf8180900a2575000"
|
||||
integrity sha512-UpNCv0iZH16nlo+x/4xVisIX20v1JrQC47GUUtXcGDw9pde6HnnBPxpN0cF32r+Kg7F0QRp4Yvjoljmpp1HS7g==
|
||||
dependencies:
|
||||
"@aws-sdk/client-ec2" "3.658.1"
|
||||
"@aws-sdk/client-eks" "3.1.0"
|
||||
@@ -3258,7 +3258,7 @@
|
||||
entities "4.5.0"
|
||||
eslint "7.32.0"
|
||||
eslint-config-standard "16.0.3"
|
||||
eslint-import-resolver-node "0.3.4"
|
||||
eslint-import-resolver-node "0.3.9"
|
||||
eslint-module-utils "2.6.1"
|
||||
eslint-plugin-cypress "2.12.1"
|
||||
eslint-plugin-import "2.31.0"
|
||||
@@ -3279,7 +3279,7 @@
|
||||
jest-serializer-vue "2.0.2"
|
||||
jexl "2.3.0"
|
||||
jquery "3.5.1"
|
||||
js-cookie "2.2.1"
|
||||
js-cookie "3.0.5"
|
||||
js-yaml "4.1.0"
|
||||
js-yaml-loader "1.2.2"
|
||||
jsdiff "1.1.1"
|
||||
@@ -6999,7 +6999,7 @@ debounce@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
|
||||
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
|
||||
|
||||
debug@2.6.9, debug@^2.6.9:
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
@@ -7672,15 +7672,7 @@ eslint-config-standard@16.0.3:
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz#6c8761e544e96c531ff92642eeb87842b8488516"
|
||||
integrity sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==
|
||||
|
||||
eslint-import-resolver-node@0.3.4:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
|
||||
integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==
|
||||
dependencies:
|
||||
debug "^2.6.9"
|
||||
resolve "^1.13.1"
|
||||
|
||||
eslint-import-resolver-node@^0.3.9:
|
||||
eslint-import-resolver-node@0.3.9, eslint-import-resolver-node@^0.3.9:
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"
|
||||
integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==
|
||||
@@ -10275,12 +10267,7 @@ js-beautify@^1.14.9, js-beautify@^1.6.12:
|
||||
js-cookie "^3.0.5"
|
||||
nopt "^7.2.0"
|
||||
|
||||
js-cookie@2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
|
||||
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
|
||||
|
||||
js-cookie@^3.0.5:
|
||||
js-cookie@3.0.5, js-cookie@^3.0.5:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
|
||||
integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
|
||||
|
||||
Reference in New Issue
Block a user