Compare commits

..
Author SHA1 Message Date
mergify[bot]andAndy Lee b8b4ee4c41 replace tag format v1.5.0 to harvester-1.5.0 (#190) (#313)
(cherry picked from commit 374b904191)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-05-26 14:21:47 +08:00
Andy Lee 021d6f673f bump version to 1.0.6
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-26 13:48:57 +08:00
Andy Lee a849671034 bump to v1.0.6-rc4
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-23 11:19:39 +08:00
mergify[bot]andYiya Chen b98f878c5d fix: range condition (#299) (#300)
(cherry picked from commit cbfcf4dbae)

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
Co-authored-by: Yiya Chen <yiya.chen@suse.com>
2025-05-15 16:26:48 +08:00
mergify[bot]andJack Yu 78482285b3 chore: use default imag repo name (#212) (#291)
(cherry picked from commit de4edbbf3b)

Signed-off-by: Jack Yu <jack.yu@suse.com>
Co-authored-by: Jack Yu <jack.yu@suse.com>
2025-05-13 12:10:05 +08:00
mergify[bot]andJack Yu 4656976b91 chore: add building catalog GA (#181) (#290)
* chore: add building catalog GA



* feat: should include prerelease



---------


(cherry picked from commit 1880043a80)

Signed-off-by: Jack Yu <jack.yu@suse.com>
Co-authored-by: Jack Yu <jack.yu@suse.com>
2025-05-13 12:08:06 +08:00
renovate[bot] aeb3588b83 chore(deps): update dependency follow-redirects to v1.15.6 [security] (#287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-13 04:07:25 +00:00
renovate[bot] d14b0cb95c chore(deps): update dependency ejs to v3.1.10 [security] (#286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-13 03:10:36 +00:00
Andy Lee 2f0884d7bd bump to v1.0.6-rc3
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-13 11:07:05 +08:00
mergify[bot]andAndy Lee 037ce6132b VM Access Credentials SSH Keys Not Rendering (backport #279) (#281)
* Remove unneeded condition (#279)

Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 8410f75859)

# Conflicts:
#	pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineSSHKey.vue

* solve conflict

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-05-12 10:56:49 +08:00
Andy Lee 47b54b4f06 bump version to 1.0.6-rc2
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-08 17:35:08 +08:00
Andy Lee 9dd8ae479a fix #cloud-config disappear when add/remove ssh key (#273)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-08 17:32:35 +08:00
Andy Lee 5682bf4c3a Fix create ssh key modal not pop up in rancher integration mode (#269)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-07 15:50:29 +08:00
Andy Lee 960df31667 Bump version to 1.0.6-rc1
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-06 11:57:48 +08:00
Yi-Ya Chen 63451e7d2b ci: rename file
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-05-06 11:56:30 +08:00
Yi-Ya Chen 55aaa8440d ci: revert change
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-05-06 11:56:29 +08:00
Yi-Ya Chen da4121da69 ci: rename the workflow 2025-05-06 11:56:29 +08:00
Yi-Ya Chen 30e49a6f2c ci: add ci pipeline
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-05-06 11:56:28 +08:00
Andy Lee 2255bd4f69 Add 1.4.3 feature flag
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-05-06 11:26:53 +08:00
Andy Lee 7e5ee04637 fix template page should be groupable (#262)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-04-17 15:45:40 +08:00
mergify[bot]andAndy Lee 8863de8874 fix the doneOverride path for harvesterhci.io.virtualmachinetemplate (#211) (#261)
Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 22a032e56c)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-17 15:40:34 +08:00
mergify[bot]andAndy Lee 73952a6b4e update RadioGroup update value action (#255) (#259)
Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 74300f24b3)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-17 15:29:29 +08:00
mergify[bot]andAndy Lee 0496fae623 fix percent typo (#258)
Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 487f9abc10)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-17 15:25:00 +08:00
mergify[bot]andandy.lee d5cd97aafd Fix upgrade cluster model styles (backport #162) (#257)
* fix upgrade model style

Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit ff439e66a9)

* change overallMessage color to red

Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 50901a2eaf)

---------

Co-authored-by: andy.lee <andy.lee@suse.com>
2025-04-17 15:23:32 +08:00
mergify[bot]andYi-Ya Chen 31c6a794be fix: register upgrade component in header (#256)
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
(cherry picked from commit b499f62c56)

Co-authored-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-04-17 15:19:08 +08:00
mergify[bot]andandy.lee 9d9383ba90 fix: vGPU number not correct (#254)
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 26afffc2f1)
(cherry picked from commit 40dc31226b)

Co-authored-by: andy.lee <andy.lee@suse.com>
2025-04-17 14:56:05 +08:00
mergify[bot]andandy.lee 1e02dbffe3 fix: isEncrypted breaks all the volume page (#253)
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit 2232e56ba1)
(cherry picked from commit c188f3367d)

Co-authored-by: andy.lee <andy.lee@suse.com>
2025-04-17 14:50:31 +08:00
mergify[bot]andandy.lee b0609020cc fix sshkey is not added to cloud init user data immediately (#252)
Signed-off-by: andy.lee <andy.lee@suse.com>
(cherry picked from commit e2daa1ea0c)

Co-authored-by: andy.lee <andy.lee@suse.com>
2025-04-17 14:50:10 +08:00
mergify[bot]andAndy Lee feca476e36 Fix error banner position in all dialogs (backport #226) (#250)
* Fix error banner position in all dialogs (#226)

* fix error banner position in all dialogs

Signed-off-by: Andy Lee <andy.lee@suse.com>

* update HarvesterImageDownloader.vue

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit 25439aee65)

# Conflicts:
#	pkg/harvester/dialog/ConfirmExecutionDialog.vue
#	pkg/harvester/dialog/HarvesterImageDownloader.vue

* remove unneeded dialog components

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-17 14:46:30 +08:00
mergify[bot]andAndy Lee e6e36e94f1 fix image.repo to image.repository (#214) (#251)
Signed-off-by: Andy Lee <andy.lee@suse.com>
(cherry picked from commit a24d01717a)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-04-17 14:46:11 +08:00
Andy Lee 54d48ce396 bump version to 1.0.6
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-04-17 13:47:13 +08:00
Andy Lee 404395c7d9 bump version to 1.0.5
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-04-08 15:26:18 +08:00
Andy Lee dc2437db99 bring status object in PUT dismiss API
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-04-08 15:12:13 +08:00
Andy Lee e8c8a54b9f add cleanForSave in harvester model
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-04-08 15:12:07 +08:00
andy.lee f3e4a8afe8 add pcideviceclaim and usbdeviceclaim models
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-04-08 15:11:55 +08:00
andy.lee e73c62739f fix unable to add/delete new hard disk
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-04-08 15:03:14 +08:00
Andy Lee 696af4e04f update yarn.lock
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-03-12 12:05:24 +08:00
Yi-Ya Chen 009f384a73 feat: fix $ctx redefine issue
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-03-12 12:04:24 +08:00
andy.lee 37766454bd move harvester schema.js into harvester folder
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-03-12 12:02:36 +08:00
Andy Lee 7943913ef7 replace tag format v1.5.0 to harvester-1.5.0 (#190)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-03-12 12:02:11 +08:00
Andy Lee 56a0fefd40 bump release version to v1.0.4
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-03-11 12:11:37 +08:00
Andy Lee ff1d5925a8 remove require dot for non-longhorn v1
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-03-11 12:09:18 +08:00
Andy Lee 63b5b980b7 Fix infobox style
Signed-off-by: Andy Lee <andy.lee@suse.com>
2025-03-11 12:09:10 +08:00
Yi-Ya Chen 1f7fd1246f refactor: default should selectable
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-03-11 12:07:52 +08:00
Yi-Ya Chen 34a25e9fc8 feat: gray out class name selector
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-03-11 12:07:32 +08:00
Andy Lee fb8d81d953 bump version to v1.0.4-rc2 2025-03-11 12:06:29 +08:00
andy.lee ba810a52b2 add refreshIntervalInSecond in 1.4.2 feature flag
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-03-11 12:06:29 +08:00
andy.lee 3f4f30ba0b Add refreshIntervalInSeconds in backup targe edit page
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-03-11 12:06:29 +08:00
andy.lee b428aaecf8 bump version to v1.0.4-rc1
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-02-13 14:34:14 +08:00
andy.lee 6b07a03451 add v1.4.2 feature flag
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-02-13 14:19:37 +08:00
Volker Theile 2c756582e3 The hints on the branding page is using the wrong product name
The hints on the `Branding` page at `Advanced > Settings > UI > branding` are using the product name `Rancher` instead of `Harvester`.

Related to: https://github.com/harvester/harvester/issues/6341

Signed-off-by: Volker Theile <vtheile@suse.com>
(cherry picked from commit 849dcb6f26d52c955db669207f008de4480fe048)
2025-02-13 14:11:24 +08:00
Andy Lee 1c15ed8003 Merge pull request #92 from a110605/bump_v103_release
chore: bump release version to v1.0.3
2025-01-17 09:57:49 +08:00
Andy Lee 9f3e689727 Merge pull request #91 from a110605/issue_7351
fix: bring default valueContext when customizing vm-import controller
2025-01-17 09:56:58 +08:00
andy.lee 6644651fe5 chore: bump release version to v1.0.3
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-16 14:03:46 +08:00
andy.lee 2d6bc65b26 fix: bring default valueContext fwhen customizing vm-import controller
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-16 12:18:57 +08:00
Francesco Torchia 7b6efa624f Merge pull request #86 from torchiaf/replace-sortablejs-vue3
Replace sortablejs-vue3
2025-01-14 17:02:21 +01:00
Francesco Torchia d9db1263d1 Fix Volumes draggable key
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
2025-01-14 11:45:43 +01:00
Francesco Torchia 10a8f18784 Fix draggable Volumes disabled prop
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
2025-01-14 10:09:30 +01:00
Andy Lee 1098ef8f6a Merge pull request #84 from a110605/issue_7174_2
fix: resume schedule job error
2025-01-14 12:55:41 +08:00
Andy Lee 4480dbdeed Merge pull request #85 from a110605/issue_7347
fix: filter schedule style and functionality
2025-01-14 12:55:24 +08:00
Francesco Torchia a7b4366d7c Replace sortablejs-vue3
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
2025-01-13 18:13:18 +01:00
andy.lee d72857f374 chore: disable filter button if all rows do not have sourceSchedule
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-13 16:29:08 +08:00
andy.lee 57ab9e2830 fix: filter schedule style and functionality
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-13 16:07:32 +08:00
andy.lee 8116358011 fix: resume schedule job error
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-13 14:13:00 +08:00
Andy Lee 5bd8ad2a93 Merge pull request #79 from a110605/fix_7317
fix: add missing wording in delete VM modal
2025-01-13 13:36:52 +08:00
Andy Lee 345fe636ec Merge pull request #72 from torchiaf/7082-host-vms
[porting 1262] Fix vm rows reactivity
2025-01-13 11:58:45 +08:00
andy.lee 7e32ea453b fix: delete VM modal style and wording
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-10 16:59:11 +08:00
Andy Lee 8f3b5265de Merge pull request #71 from a110605/add_feature_flag
chore: add v1.5.0 and v1.3.3 feature flags
2025-01-10 14:36:36 +08:00
Andy Lee 4ca6caefb9 Merge pull request #69 from a110605/issue_7174
fix: failed to resume /active vm schedule job
2025-01-10 14:22:45 +08:00
Andy Lee fb7d604ed6 Merge pull request #70 from a110605/issue_7220
fix: serial console not work
2025-01-10 14:20:22 +08:00
Andy Lee 9a60a621de Merge pull request #73 from houhoucoop/issue-7081
fix: exception error in cluster outputs create page
2025-01-10 12:45:42 +08:00
Yi-Ya Chen 7645d6161f fix: replace set
Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
2025-01-10 10:54:58 +08:00
Francesco Torchia c3f2665912 [porting 1262] Fix vm rows reactivity
Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
2025-01-09 12:06:35 +01:00
andy.lee 2a58745550 chore: add v1.5.0 and v1.3.3 feature flags
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-09 17:43:19 +08:00
andy.lee 69d7046bc7 fix: serial console not work
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-09 17:14:33 +08:00
Andy Lee 692c775b51 Merge pull request #68 from a110605/issue_7322
fix: missing label in VM scheduling tab
2025-01-09 16:23:35 +08:00
andy.lee 8b43c200b2 fix: remove translation key
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-09 16:13:51 +08:00
andy.lee ab71cb377c fix: update translation key
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-09 10:59:49 +08:00
andy.lee 814d296d75 fix: resume/active scheduling job failed
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-08 15:59:25 +08:00
andy.lee eb667fd56b fix: missing label in VM scheduling tab
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-08 14:41:18 +08:00
andy.lee e4b3f84905 change mergify.yml base branch to main
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 21:37:38 +08:00
Andy Lee ea68b95c26 Merge pull request #67 from a110605/add_mergify
add mergify.yml
2025-01-06 21:34:56 +08:00
andy.lee da77762b7f add mergify.yml
Signed-off-by: andy.lee <andy.lee@suse.com>
2025-01-06 21:32:00 +08:00
66 changed files with 1773 additions and 2582 deletions
@@ -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
@@ -1,4 +1,4 @@
name: Build Harvester Dashboard and Upload name: Build Harvester Dashboard and Publish
on: on:
workflow_call: workflow_call:
@@ -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
@@ -1,34 +0,0 @@
name: Build and Release Extension Charts
on:
workflow_dispatch:
release:
types: [released]
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-extension-charts:
needs:
- lint
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
permissions:
actions: write
contents: write
deployments: write
pages: write
with:
target_branch: gh-pages
tagged_release: ${{ github.ref_name }}
@@ -0,0 +1,136 @@
name: Build and Release Extension Charts on PR Merge
on:
push:
branches:
- 'release-harvester-v*'
jobs:
setup-target-branch:
runs-on: ubuntu-latest
outputs:
target_branch: ${{ steps.get-version.outputs.target_branch }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Determine target branch
id: get-version
run: |
if [[ "${{ github.ref_name }}" =~ ^release-harvester-v([0-9]+\.[0-9]+)$ ]]; then
TARGET_BRANCH="v${BASH_REMATCH[1]}-head"
echo "target_branch=${TARGET_BRANCH}" >> $GITHUB_ENV
echo "target_branch=${TARGET_BRANCH}" >> $GITHUB_OUTPUT
else
echo "Error: invalid branch format." && exit 1
fi
- name: Ensure target branch exists
run: |
git fetch --all
if ! git ls-remote --exit-code --heads origin "${{ env.target_branch }}"; then
git checkout gh-pages
git checkout -b "${{ env.target_branch }}"
git push origin "${{ env.target_branch }}"
fi
extract-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Extract version from package.json
id: get_version
run: |
VERSION=$(jq -r '.version' pkg/harvester/package.json)
echo "VERSION=${VERSION}" >> $GITHUB_ENV
echo "version=${VERSION}" >> $GITHUB_OUTPUT
build-extension-charts:
needs:
- setup-target-branch
- extract-version
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup environment
run: |
corepack enable
yarn install --frozen-lockfile
- name: Setup Helm
uses: azure/setup-helm@v3
with:
version: v3.8.0
- name: Build Helm charts
run: |
yarn publish-pkgs -s ${{ github.repository }} -b ${{ needs.setup-target-branch.outputs.target_branch }} -t harvester-${{ needs.extract-version.outputs.version }}
- name: Upload charts artifact
uses: actions/upload-artifact@v4
with:
name: charts
path: tmp
release:
needs:
- setup-target-branch
- extract-version
- build-extension-charts
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout release branch
uses: actions/checkout@v4
with:
ref: '${{ github.ref_name }}'
- name: Get last commit hash
id: last_commit
run: |
LAST_COMMIT=$(git rev-parse HEAD)
echo "LAST_COMMIT=${LAST_COMMIT}" >> $GITHUB_ENV
- name: Checkout target branch
uses: actions/checkout@v4
with:
ref: '${{ needs.setup-target-branch.outputs.target_branch }}'
- name: Remove old artifacts
run: |
rm -rf extensions/harvester/${{ needs.extract-version.outputs.version }}
rm -rf charts/harvester/${{ needs.extract-version.outputs.version }}
rm -f assets/harvester/harvester-${{ needs.extract-version.outputs.version }}.tgz
- name: Configure Git
run: |
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: charts
- name: Commit and push artifacts
run: |
git add ./{assets,charts,extensions,index.yaml}
git commit -m "CI Build Artifacts (commit: ${{ env.LAST_COMMIT }}, version: ${{ needs.extract-version.outputs.version }})"
git push origin ${{ needs.setup-target-branch.outputs.target_branch }}
- name: Run Helm chart releaser
uses: helm/chart-releaser-action@v1.4.1
with:
charts_dir: ./charts
env:
CR_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
CR_SKIP_EXISTING: true
@@ -0,0 +1,53 @@
name: Build and Release Extension Charts on Release
on:
workflow_dispatch:
release:
types: [released]
defaults:
run:
shell: bash
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:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run tests
uses: ./.github/actions/lint
build-extension-charts:
needs:
- setup-release-tag
- lint
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
permissions:
actions: write
contents: write
deployments: write
pages: write
with:
target_branch: gh-pages
tagged_release: '${{ needs.setup-release-tag.outputs.release_tag }}'
@@ -1,4 +1,4 @@
name: Build Dashboard (Branch) name: Build Standalone on PR Merge
on: on:
push: push:
@@ -17,10 +17,10 @@ on:
jobs: jobs:
build-validation: build-validation:
name: Build Test name: Build Test
uses: ./.github/workflows/test.yaml uses: ./.github/workflows/run-lint.yaml
build: build:
name: Build and Upload Package name: Build and Upload Package
uses: ./.github/workflows/build-and-upload.yaml uses: ./.github/workflows/build-and-publish-standalone.yaml
needs: needs:
- build-validation - build-validation
permissions: permissions:
@@ -1,4 +1,4 @@
name: Build Dashboard (Release) name: Build Standalone on Release
on: on:
push: push:
tags: tags:
@@ -7,10 +7,10 @@ on:
jobs: jobs:
build-validation: build-validation:
name: Build Test name: Build Test
uses: ./.github/workflows/test.yaml uses: ./.github/workflows/run-lint.yaml
build: build:
name: Build and Upload Package name: Build and Upload Package
uses: ./.github/workflows/build-and-upload.yaml uses: ./.github/workflows/build-and-publish-standalone.yaml
needs: needs:
- build-validation - build-validation
permissions: permissions:
+1 -1
View File
@@ -70,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
View File
@@ -1,12 +1,12 @@
{ {
"name": "harvester-ui-extension", "name": "harvester-ui-extension",
"version": "0.1.0", "version": "1.0.6",
"private": false, "private": false,
"engines": { "engines": {
"node": ">=20.0.0" "node": ">=20.0.0"
}, },
"dependencies": { "dependencies": {
"@rancher/shell": "3.0.2-rc.3", "@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',
@@ -36,7 +34,6 @@ export default {
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)"
@@ -190,6 +190,7 @@ export default {
v-clean-tooltip="{ v-clean-tooltip="{
placement: 'bottom-left', placement: 'bottom-left',
}" }"
popper-class="upgrade-header-dropdown"
class="hand" class="hand"
> >
<slot name="button-content"> <slot name="button-content">
@@ -272,7 +273,7 @@ export default {
<p <p
v-if="overallMessage" v-if="overallMessage"
class="text-warning mb-20" class="text-error mb-20"
> >
{{ overallMessage }} {{ overallMessage }}
</p> </p>
@@ -300,14 +301,14 @@ export default {
<ProgressBarList <ProgressBarList
:title="t('harvester.upgradePage.upgradeNode')" :title="t('harvester.upgradePage.upgradeNode')"
:precent="nodesPercent" :percent="nodesPercent"
:list="nodesStatus" :list="nodesStatus"
/> />
<p class="bordered-section"></p> <p class="bordered-section"></p>
<ProgressBarList <ProgressBarList
:title="t('harvester.upgradePage.upgradeSysService')" :title="t('harvester.upgradePage.upgradeSysService')"
:precent="sysServiceTotal" :percent="sysServiceTotal"
:list="sysServiceUpgradeMessage" :list="sysServiceUpgradeMessage"
/> />
</div> </div>
@@ -338,6 +339,12 @@ export default {
</div> </div>
</template> </template>
<style lang="scss">
.upgrade-header-dropdown .v-popper__arrow-container {
display: none;
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
a { a {
float: right; float: right;
@@ -12,7 +12,7 @@ export default {
default: '' default: ''
}, },
precent: { percent: {
type: Number, type: Number,
default: 0 default: 0
}, },
@@ -39,16 +39,18 @@ export default {
<template> <template>
<div class="bar-list"> <div class="bar-list">
<h4>{{ title }} <span class="float-r text-info">{{ precent }}%</span></h4> <h4>{{ title }} <span class="float-r text-info">{{ percent }}%</span></h4>
<div> <div>
<div> <div>
<Collapse v-model:open="open"> <Collapse v-model:open="open">
<template #title> <template #title>
<div class="total-bar"> <div class="total-bar">
<span class="bar"><PercentageBar <span class="bar">
:model-value="precent" <PercentageBar
:model-value="percent"
preferred-direction="MORE" preferred-direction="MORE"
/></span> />
</span>
<span <span
class="on-off" class="on-off"
@click="handleSwitch" @click="handleSwitch"
@@ -56,7 +58,6 @@ export default {
</div> </div>
</template> </template>
<template>
<div class="custom-content"> <div class="custom-content">
<div <div
v-for="(item, i) in list" v-for="(item, i) in list"
@@ -77,7 +78,6 @@ export default {
</p> </p>
</div> </div>
</div> </div>
</template>
</Collapse> </Collapse>
</div> </div>
</div> </div>
@@ -93,7 +93,7 @@ export default {
.total-bar { .total-bar {
display: flex; display: flex;
user-select: none; user-select: none;
.bar { > .bar {
width: 85%; width: 85%;
} }
.on-off { .on-off {
@@ -102,7 +102,6 @@ export default {
} }
} }
.custom-content { .custom-content {
.item {
margin-bottom: 14px; margin-bottom: 14px;
p { p {
margin-bottom: 4px; margin-bottom: 4px;
@@ -120,7 +119,5 @@ export default {
color: var(--error); color: var(--error);
} }
} }
}
} }
</style> </style>
@@ -6,6 +6,7 @@ import CreateEditView from '@shell/mixins/create-edit-view';
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 { HCI_SETTING } from '../../config/settings'; import { HCI_SETTING } from '../../config/settings';
import UnitInput from '@shell/components/form/UnitInput';
const DEFAULT_TYPE = 's3'; const DEFAULT_TYPE = 's3';
@@ -13,7 +14,7 @@ export default {
name: 'HarvesterEditBackupTarget', name: 'HarvesterEditBackupTarget',
components: { components: {
LabeledInput, LabeledSelect, Tip, Password, MessageLink LabeledInput, LabeledSelect, Tip, Password, MessageLink, UnitInput
}, },
mixins: [CreateEditView], mixins: [CreateEditView],
@@ -24,7 +25,9 @@ export default {
try { try {
parseDefaultValue = JSON.parse(this.value.value); parseDefaultValue = JSON.parse(this.value.value);
} catch (error) { } catch (error) {
parseDefaultValue = { type: '', endpoint: '' }; parseDefaultValue = {
type: '', endpoint: '', refreshIntervalInSeconds: 0
};
} }
// set default type to s3 // set default type to s3
@@ -62,6 +65,10 @@ export default {
}]; }];
}, },
refreshIntervalInSecondEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('refreshIntervalInSecond');
},
isS3() { isS3() {
return this.parseDefaultValue.type === DEFAULT_TYPE; return this.parseDefaultValue.type === DEFAULT_TYPE;
}, },
@@ -83,7 +90,9 @@ export default {
try { try {
parseDefaultValue = JSON.parse(neu.value); parseDefaultValue = JSON.parse(neu.value);
} catch (err) { } catch (err) {
parseDefaultValue = { type: '', endpoint: '' }; parseDefaultValue = {
type: '', endpoint: '', refreshIntervalInSeconds: 0
};
} }
this['parseDefaultValue'] = parseDefaultValue; this['parseDefaultValue'] = parseDefaultValue;
@@ -111,7 +120,6 @@ export default {
if (this.isS3 && !this.parseDefaultValue.virtualHostedStyle) { if (this.isS3 && !this.parseDefaultValue.virtualHostedStyle) {
this.parseDefaultValue.virtualHostedStyle = false; this.parseDefaultValue.virtualHostedStyle = false;
} }
if (!this.parseDefaultValue.type) { if (!this.parseDefaultValue.type) {
delete this.value['value']; delete this.value['value'];
} else { } else {
@@ -120,7 +128,9 @@ export default {
}, },
useDefault() { useDefault() {
this['parseDefaultValue'] = { type: '', endpoint: '' }; this['parseDefaultValue'] = {
type: '', endpoint: '', refreshIntervalInSeconds: 0
};
} }
} }
}; };
@@ -139,6 +149,23 @@ export default {
:options="typeOption" :options="typeOption"
@update:value="update" @update:value="update"
/> />
<UnitInput
v-if="refreshIntervalInSecondEnabled"
v-model:value="parseDefaultValue.refreshIntervalInSeconds"
:suffix="parseDefaultValue.refreshIntervalInSeconds <= 1 ? 'Second' : 'Seconds'"
:label="t('harvester.backup.refreshInterval.label')"
:mode="mode"
:positive="true"
class="mb-5"
required
@update:value="update"
/>
<Tip
v-if="refreshIntervalInSecondEnabled"
class="mb-20"
icon="icon icon-info"
:text="t('harvester.backup.refreshInterval.tip')"
/>
<LabeledInput <LabeledInput
v-model:value="parseDefaultValue.endpoint" v-model:value="parseDefaultValue.endpoint"
class="mb-5" class="mb-5"
@@ -75,7 +75,11 @@ export default {
const csiDrivers = this.$store.getters[`${ inStore }/all`](CSI_DRIVER) || []; const csiDrivers = this.$store.getters[`${ inStore }/all`](CSI_DRIVER) || [];
return this.configArr.length >= csiDrivers.length; return this.configArr.length >= csiDrivers.length;
} },
allowEmptySnapshotClassNameFeatureEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('allowEmptySnapshotClassName');
},
}, },
methods: { methods: {
@@ -139,7 +143,7 @@ export default {
errors.push(this.t('validation.required', { key: this.t('harvester.setting.csiDriverConfig.volumeSnapshotClassName') }, true)); errors.push(this.t('validation.required', { key: this.t('harvester.setting.csiDriverConfig.volumeSnapshotClassName') }, true));
} }
if (!config.value.backupVolumeSnapshotClassName) { if (!this.allowEmptySnapshotClassNameFeatureEnabled && !config.value.backupVolumeSnapshotClassName) {
errors.push(this.t('validation.required', { key: this.t('harvester.setting.csiDriverConfig.backupVolumeSnapshotClassName') }, true)); errors.push(this.t('validation.required', { key: this.t('harvester.setting.csiDriverConfig.backupVolumeSnapshotClassName') }, true));
} }
}); });
@@ -158,10 +162,22 @@ export default {
this.configArr.splice(idx, 1); this.configArr.splice(idx, 1);
}, },
isBackupVolumeSnapshotRequired(driver) {
return driver === LONGHORN_DRIVER;
},
disableEdit(driver) { disableEdit(driver) {
return driver === LONGHORN_DRIVER; return driver === LONGHORN_DRIVER;
}, },
isBackupVolumeSnapshotClassNameDisabled(driver) {
return driver === LONGHORN_DRIVER || this.allowEmptySnapshotClassNameFeatureEnabled;
},
isBackupVolumeSnapshotClassNameSelectable(driver) {
return driver === LONGHORN_DRIVER;
},
add() { add() {
this.configArr.push({ this.configArr.push({
key: '', key: '',
@@ -184,6 +200,7 @@ export default {
<InfoBox <InfoBox
v-for="(driver, idx) in configArr" v-for="(driver, idx) in configArr"
:key="idx" :key="idx"
class="box"
> >
<button <button
:disabled="disableEdit(driver.key)" :disabled="disableEdit(driver.key)"
@@ -226,10 +243,11 @@ export default {
<LabeledSelect <LabeledSelect
v-model:value="driver.value.backupVolumeSnapshotClassName" v-model:value="driver.value.backupVolumeSnapshotClassName"
:mode="mode" :mode="mode"
required :disabled="isBackupVolumeSnapshotClassNameDisabled(driver.key)"
:disabled="disableEdit(driver.key)"
:options="getVolumeSnapshotOptions(driver.key)" :options="getVolumeSnapshotOptions(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
+13 -9
View File
@@ -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,18 +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',
'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 = [
...featuresV142
]; ];
export const RELEASE_FEATURES = { export const RELEASE_FEATURES = {
@@ -62,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
}; };
@@ -43,7 +43,6 @@ export const HCI = {
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',
SKIP_SINGLE_REPLICA_DETACHED_VOL: 'harvesterhci.io/skipSingleReplicaDetachedVol',
HOST_REQUEST: 'management.cattle.io/pod-requests', HOST_REQUEST: 'management.cattle.io/pod-requests',
STORAGE_CLASS: 'harvesterhci.io/storageClassName', STORAGE_CLASS: 'harvesterhci.io/storageClassName',
STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io', STORAGE_NETWORK: 'storage-network.settings.harvesterhci.io',
+1 -3
View File
@@ -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]: {
-5
View File
@@ -2,8 +2,3 @@ export const BACKUP_TYPE = {
BACKUP: 'backup', BACKUP: 'backup',
SNAPSHOT: 'snapshot' SNAPSHOT: 'snapshot'
}; };
export const NETWORK_TYPE = {
L2VLAN: 'L2VlanNetwork',
UNTAGGED: 'UntaggedNetwork'
};
@@ -1,173 +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"
/>
</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>
@@ -166,6 +166,8 @@ export default {
<Banner <Banner
v-for="(error, i) in errors" v-for="(error, i) in errors"
:key="i" :key="i"
:label="error"
color="error"
/> />
</div> </div>
</template> </template>
+6 -7
View File
@@ -106,6 +106,12 @@ export default {
:label="t('harvester.modal.cloneVM.name')" :label="t('harvester.modal.cloneVM.name')"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
:label="err"
color="error"
/>
</template> </template>
<template #actions> <template #actions>
@@ -125,13 +131,6 @@ export default {
@click="create" @click="create"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -137,6 +137,12 @@ export default {
class="mt-20" class="mt-20"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
:label="err"
color="error"
/>
</template> </template>
<template #actions> <template #actions>
@@ -156,11 +162,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -103,6 +103,12 @@ export default {
:label="t('generic.name')" :label="t('generic.name')"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -121,13 +127,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -120,6 +120,12 @@ export default {
v-model:value="description" v-model:value="description"
:label="t('harvester.modal.createTemplate.description')" :label="t('harvester.modal.createTemplate.description')"
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -138,11 +144,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -128,6 +128,12 @@ export default {
<Banner color="warning"> <Banner color="warning">
<span>{{ t('harvester.modal.ejectCDROM.warnTip') }}</span> <span>{{ t('harvester.modal.ejectCDROM.warnTip') }}</span>
</Banner> </Banner>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -147,13 +153,6 @@ export default {
@click="remove" @click="remove"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -164,6 +164,12 @@ export default {
class="mt-20" class="mt-20"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -182,11 +188,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -161,6 +161,12 @@ export default {
:placeholder="t('harvester.modal.migration.fields.nodeName.placeholder')" :placeholder="t('harvester.modal.migration.fields.nodeName.placeholder')"
:options="nodeNameList" :options="nodeNameList"
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template <template
@@ -181,12 +187,6 @@ export default {
@click="apply" @click="apply"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
</Card> </Card>
</template> </template>
@@ -95,6 +95,12 @@ export default {
suffix="GiB" suffix="GiB"
class="mb-20" class="mb-20"
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -108,12 +114,6 @@ export default {
</button> </button>
<AsyncButton @click="save" /> <AsyncButton @click="save" />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -136,6 +136,12 @@ export default {
:options="backupOption" :options="backupOption"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -154,11 +160,6 @@ export default {
@click="saveRestore" @click="saveRestore"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -90,6 +90,12 @@ export default {
v-clean-html="t('harvester.virtualMachine.unplug.title', { name: diskName })" v-clean-html="t('harvester.virtualMachine.unplug.title', { name: diskName })"
class="text-default-text" class="text-default-text"
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -111,13 +117,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -132,6 +132,12 @@ export default {
:label="t('generic.name')" :label="t('generic.name')"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -150,11 +156,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -107,6 +107,12 @@ export default {
:placeholder="t('harvester.harvesterVlanConfigMigrateDialog.targetClusterNetwork.placeholder')" :placeholder="t('harvester.harvesterVlanConfigMigrateDialog.targetClusterNetwork.placeholder')"
:options="clusterNetworks" :options="clusterNetworks"
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -125,11 +131,6 @@ export default {
@click="apply" @click="apply"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
+4 -4
View File
@@ -70,16 +70,16 @@ export default {
v-clean-html="t('harvester.modal.restart.tip')" v-clean-html="t('harvester.modal.restart.tip')"
class="pl-10 pr-10" class="pl-10 pr-10"
/> />
</template>
<template #actions>
<div class="bottom">
<Banner <Banner
v-for="(err, i) in errors" v-for="(err, i) in errors"
:key="i" :key="i"
color="error" color="error"
:label="err" :label="err"
/> />
</template>
<template #actions>
<div class="bottom">
<div class="buttons"> <div class="buttons">
<button <button
class="btn role-secondary mr-10" class="btn role-secondary mr-10"
@@ -145,6 +145,12 @@ export default {
class="mt-20" class="mt-20"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -162,10 +168,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
+6 -6
View File
@@ -79,6 +79,12 @@ export default {
:label="t('harvester.modal.snapshot.name')" :label="t('harvester.modal.snapshot.name')"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -96,12 +102,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</div> </div>
</template> </template>
</Card> </Card>
+6 -4
View File
@@ -98,6 +98,12 @@ export default {
:label="t('harvester.modal.volumeClone.name')" :label="t('harvester.modal.volumeClone.name')"
required required
/> />
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template> </template>
<template #actions> <template #actions>
@@ -115,10 +121,6 @@ export default {
@click="save" @click="save"
/> />
</div> </div>
<Banner
v-for="(err, i) in errors"
:key="i"
/>
</div> </div>
</template> </template>
</Card> </Card>
@@ -10,7 +10,7 @@ import { Banner } from '@components/Banner';
import CreateEditView from '@shell/mixins/create-edit-view'; 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 { export default {
name: 'EditAddonNvidiaDriverToolkit', name: 'EditAddonNvidiaDriverToolkit',
@@ -122,7 +122,7 @@ export default {
> >
<div class="col span-6"> <div class="col span-6">
<LabeledInput <LabeledInput
v-model:value="valuesContentJson.image.repo" v-model:value="valuesContentJson.image.repository"
:mode="mode" :mode="mode"
:required="true" :required="true"
label-key="harvester.addons.nvidiaDriverToolkit.image.repository" label-key="harvester.addons.nvidiaDriverToolkit.image.repository"
@@ -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">
@@ -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',
@@ -61,7 +60,10 @@ export default {
images: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IMAGE }), images: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IMAGE }),
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['storageClassName'] = this.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE); this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE);
const { securityParameters } = this.value.spec; const { securityParameters } = this.value.spec;
@@ -142,22 +144,9 @@ 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 storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
return storages return storages
.filter((s) => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) // Lvm storage is not supported. .filter((s) => !s.parameters?.backingImage && s.provisioner !== LVM_DRIVER) // Lvm storage is not supported.
@@ -250,13 +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();
}
}
}, },
methods: { methods: {
@@ -403,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;
} }
}, },
}; };
@@ -434,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"
@@ -465,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"
/> />
@@ -496,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>
@@ -1,14 +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 { clone } from '@shell/utils/object';
import { _VIEW } from '@shell/config/query-params'; import { _VIEW } 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';
@@ -126,10 +123,10 @@ export default {
this.checkedSsh = neu; this.checkedSsh = neu;
}, },
checkedSsh(val, old) { checkedSsh(val) {
if ( val.includes(_NEW)) { // if click on Create a New...
this['checkedSsh'] = old; if (val.includes(_NEW)) {
this.update(); this.checkedSsh = this.checkedSsh.filter((key) => key !== _NEW);
this.show(); this.show();
} }
} }
@@ -175,6 +172,7 @@ export default {
if (res.id) { if (res.id) {
this.checkedSsh.push(`${ this.namespace }/${ this.sshName }`); this.checkedSsh.push(`${ this.namespace }/${ this.sshName }`);
this.update();
} }
}, },
@@ -243,13 +241,13 @@ export default {
<LabeledSelect <LabeledSelect
v-model:value="checkedSsh" v-model:value="checkedSsh"
:label="t('harvester.virtualMachine.input.sshKey')" :label="t('harvester.virtualMachine.input.sshKey')"
:taggable="true" :taggable="!disabled"
:mode="mode" :mode="mode"
:multiple="true" :multiple="true"
:searchable="searchable" :searchable="searchable"
:disabled="disabled" :disabled="disabled"
:options="sshOption" :options="sshOption"
@input="update" @update:value="update"
/> />
<ModalWithCard <ModalWithCard
@@ -579,7 +579,7 @@ export default {
:create-namespace="true" :create-namespace="true"
:namespace="value.metadata.namespace" :namespace="value.metadata.namespace"
:mode="mode" :mode="mode"
:disabled="isWindows" :disabled="isWindows || isEdit"
@update:sshKey="updateSSHKey" @update:sshKey="updateSSHKey"
@register-after-hook="registerAfterHook" @register-after-hook="registerAfterHook"
/> />
@@ -873,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"
/> />
@@ -904,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"
@@ -29,7 +29,7 @@ export default {
const rows = (this.value || []).map((row) => { const rows = (this.value || []).map((row) => {
let type = 'cidr'; let type = 'cidr';
if (row.rangeStart && row.rangeEnd) { if (row.rangeStart || row.rangeEnd) {
type = 'range'; type = 'range';
} }
@@ -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>
+5
View File
@@ -6,6 +6,7 @@ import harvesterCommonStore from './store/harvester-common';
import harvesterStore from './store/harvester-store'; import harvesterStore from './store/harvester-store';
import customValidators from './validators'; import customValidators from './validators';
import { PRODUCT_NAME } from './config/harvester'; import { PRODUCT_NAME } from './config/harvester';
import { defineAsyncComponent } from 'vue';
// Init the package // Init the package
export default function (plugin: IPlugin) { export default function (plugin: IPlugin) {
@@ -28,4 +29,8 @@ export default function (plugin: IPlugin) {
plugin.validators = customValidators; plugin.validators = customValidators;
plugin.addRoutes(extensionRoutes); plugin.addRoutes(extensionRoutes);
plugin.register('component', 'NavHeaderRight', defineAsyncComponent(() =>
import('./components/HarvesterUpgradeHeader.vue')
));
} }
+6 -21
View File
@@ -38,21 +38,6 @@ asyncButton:
success: Restarted success: Restarted
waiting: Restarting&hellip; waiting: Restarting&hellip;
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:
@@ -599,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.
@@ -855,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
@@ -920,6 +902,9 @@ harvester:
label: Virtual Machine Backups label: Virtual Machine Backups
createText: Restore Backup createText: Restore Backup
title: Restore Virtual Machine title: Restore Virtual Machine
refreshInterval:
label: Refresh Interval
tip: The interval for the backup target to sync data to harvester cluster. Set to 0 to disable refresh mechanism.
backupTargetTip: The endpoint used to access the backupstore. NFS and S3 are supported. backupTargetTip: The endpoint used to access the backupstore. NFS and S3 are supported.
message: message:
noSetting: noSetting:
@@ -1029,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
+1 -9
View File
@@ -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) {
+27 -62
View File
@@ -164,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,
@@ -219,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;
@@ -369,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);
@@ -402,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;
@@ -900,8 +894,8 @@ export default {
}, },
/** /**
* Generate user data yaml which is decide by the "Install guest agent", * Generate user data yaml which is decided by the
* "OS type", "SSH Keys" and user input. * "Install guest agent", "OS type", "SSH keys" and user input.
* @param config * @param config
*/ */
getUserData(config) { getUserData(config) {
@@ -925,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
@@ -1218,7 +1214,6 @@ export default {
let secret = this.getSecret(vm.spec); let secret = this.getSecret(vm.spec);
// const userData = this.getUserData({ osType: this.osType, installAgent: this.installAgent });
if (!secret && this.isEdit && this.secretRef) { if (!secret && this.isEdit && this.secretRef) {
// When editing the vm, if the userData and networkData are deleted, we also need to clean up the secret values // When editing the vm, if the userData and networkData are deleted, we also need to clean up the secret values
secret = this.secretRef; secret = this.secretRef;
@@ -1389,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;
@@ -1412,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'];
} }
}, },
@@ -1429,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'];
} }
@@ -1447,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;
@@ -1545,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) {
@@ -1567,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: {
@@ -1583,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;
@@ -1622,9 +1582,14 @@ export default {
sshKey(neu, old) { sshKey(neu, old) {
const _diff = difference(old, neu); const _diff = difference(old, neu);
if (_diff.length && this.isEdit) { // delete removed ssh key in userdata if needed
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();
} }
} }
}; };
@@ -0,0 +1,11 @@
import SteveModel from '@shell/plugins/steve/steve-class';
/**
* Class representing PCI Device Claim resource.
* @extends SteveModal
*/
export default class PCIDeviceClaim extends SteveModel {
cleanForSave(data, _forNew) {
return data;
}
}
@@ -0,0 +1,11 @@
import SteveModel from '@shell/plugins/steve/steve-class';
/**
* Class representing USB Device Claim resource.
* @extends SteveModal
*/
export default class USBDeviceClaim extends SteveModel {
cleanForSave(data, _forNew) {
return data;
}
}
+4
View File
@@ -66,4 +66,8 @@ export default class HarvesterResource extends SteveModel {
return this.hasLink('self') && this.$rootGetters['prefs/get'](DEV); return this.hasLink('self') && this.$rootGetters['prefs/get'](DEV);
} }
} }
cleanForSave(data, _forNew) {
return data;
}
} }
+1 -1
View File
@@ -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() {
@@ -58,10 +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 isLonghornV2() { get isLonghornV2() {
return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2; return this.provisioner === LONGHORN_DRIVER && this.longhornVersion === DATA_ENGINE_V2;
} }
@@ -61,6 +61,10 @@ export default class HciUpgrade extends HarvesterResource {
return this?.status?.imageID; return this?.status?.imageID;
} }
cleanForSave(data, _forNew) {
return data;
}
get upgradeMessage() { get upgradeMessage() {
const upgradeMessage = []; const upgradeMessage = [];
const nodeStatuses = this?.status?.nodeStatuses || {}; const nodeStatuses = this?.status?.nodeStatuses || {};
@@ -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() {
@@ -2,6 +2,7 @@ import { MODE, _CREATE } from '@shell/config/query-params';
import { HCI } from '../types'; 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 { clone } from '@shell/utils/object';
export default class HciVmTemplate extends HarvesterResource { export default class HciVmTemplate extends HarvesterResource {
get availableActions() { get availableActions() {
@@ -71,4 +72,12 @@ export default class HciVmTemplate extends HarvesterResource {
get defaultVersion() { get defaultVersion() {
return this.status?.defaultVersion; return this.status?.defaultVersion;
} }
get doneOverride() {
const detailLocation = clone(this.listLocation);
detailLocation.params.resource = HCI.VM_VERSION;
return detailLocation;
}
} }
@@ -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';
} }
@@ -103,12 +103,10 @@ 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',
@@ -119,7 +117,6 @@ export default class VirtVm extends HarvesterResource {
}, },
{ {
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')
@@ -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)) {
@@ -1175,14 +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');
}
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 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "harvester", "name": "harvester",
"description": "Rancher UI Extension for Harvester", "description": "Rancher UI Extension for Harvester",
"version": "1.0.3", "version": "1.0.6",
"private": false, "private": false,
"rancher": { "rancher": {
"annotations": { "annotations": {
+1050 -1753
View File
File diff suppressed because it is too large Load Diff