Compare commits

..

46 Commits
main ... v1.7.1

Author SHA1 Message Date
renovate[bot]
bd647738ae
deps: update patch digest dependencies (#706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-08 09:11:33 +00:00
Andy Lee
c6fb969d7e
chore: update version v1.7.1 (#704)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-02-06 14:48:07 +08:00
Andy Lee
2afb04947d
chore: update v1.7.1-rc4 version
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-02-03 16:42:06 +08:00
Andy Lee
730c68bf14
chore: add v1.7.1 feature flag (#701)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-01-30 15:11:08 +08:00
freeze
20bee39a6c
chore: bump to v1.7.1-rc3 (#699)
Signed-off-by: Vicente Cheng <vicente.cheng@suse.com>
2026-01-29 01:10:39 +08:00
Andy Lee
dbb199d7bb
chore: bump to v1.7.1-rc2
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-01-27 22:37:48 +08:00
mergify[bot]
5488979448
fix: use longhorn-static for upgrade vmimage (#690) (#692)
(cherry picked from commit 0647600e88b59ccdf6a8d8f78d88b972cd604185)

Signed-off-by: Cooper Tseng <cooper.tseng@suse.com>
2026-01-22 09:51:01 +08:00
mergify[bot]
9e588e90c2
fix: remove isCordoned condition (#689) (#691)
(cherry picked from commit 99dbba7958c5bffb38aa68a3c6f6f0f44706ebae)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2026-01-21 17:43:08 +08:00
Andy Lee
9378277102
chore: change to v1.7.1-rc1 version
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-01-20 17:34:15 +08:00
mergify[bot]
b5e78018a5
fix: use file as field name instead of chunk in cdi vmimage upload (#684) (#688)
(cherry picked from commit 915559962a91802789750cec7549b61baea096e7)

Signed-off-by: Cooper Tseng <cooper.tseng@suse.com>
Co-authored-by: Kuan-Po Tseng <brandboat@gmail.com>
2026-01-20 11:00:50 +08:00
renovate[bot]
f411a0c0af
deps: update patch digest dependencies (#687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-18 08:48:10 +00:00
mergify[bot]
cfa58985cf
fix: do not set cpu.maxSockets on UI (#674) (#685)
* fix: do not set cpu.maxSockets for ARM clusters



* fix: remove maxSocket to fix bug on ARM cluster



---------


(cherry picked from commit b1b1a31c04a2f0b20fdfee42f987c694614617bf)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2026-01-16 17:15:34 +08:00
renovate[bot]
66a8f9d0e7
deps: update patch digest dependencies (#680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-11 08:53:44 +00:00
freeze
bf61c7dd7d
chore: bump version to v1.7.1-dev (#673)
Signed-off-by: Vicente Cheng <vicente.cheng@suse.com>
2026-01-04 23:21:46 +08:00
mergify[bot]
56d97260c4
fix: drop mac-address annotation from vm template to prevent MAC address reusing (#663) (#669)
related-to: harvester/harvester#9789

(cherry picked from commit 1352246e1efdab691b7ccba9e1d02da01b6844a1)

Signed-off-by: Tim Liou <tim.liou@suse.com>
Co-authored-by: Tim Liou <tim.liou@suse.com>
2026-01-02 18:13:35 +08:00
mergify[bot]
beabb34920
docs: add README.md in pkg/harvester (#661) (#662)
(cherry picked from commit fe3a12e28ca6b28193b18e665af151078ce46499)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2026-01-02 16:18:06 +08:00
renovate[bot]
d2609157bd
deps: update dependency qs to v6.14.1 [security] (#668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-02 00:42:31 +00:00
mergify[bot]
8fbe1943d8
chore: bump version to v1.7.0 (#658) (#659)
(cherry picked from commit a86302c9d5f2c1e065fb2cfe9cfd8d927bdc239b)

Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-12-22 13:41:18 +08:00
mergify[bot]
ec6bc4d639
chore: bump version to v1.7.0-rc7 (#656) (#657)
(cherry picked from commit 5fe7e13fcd6e46f32573e97a8d7bd8710078488e)

Signed-off-by: Vicente Cheng <vicente.cheng@suse.com>
Co-authored-by: freeze <1615081+Vicente-Cheng@users.noreply.github.com>
2025-12-16 20:27:51 +08:00
renovate[bot]
3824a14730
deps: update dependency @types/node to v20.19.27 (#655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-14 08:59:31 +00:00
mergify[bot]
0fc8bece02
chore: bump version to v1.7.0-rc6 (#649) (#650)
(cherry picked from commit 57695886336a7553b5ffdf6bb65093fa2037b3e6)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-12-11 16:50:51 +08:00
mergify[bot]
39764af627
fix: failed to create multiple VMs (#647) (#648)
(cherry picked from commit b29950f99cbcaf40919654fee8f6a58201a33574)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-12-11 16:17:02 +08:00
mergify[bot]
bdc87bda0e
fix: do not inherit template secret when creating new VM (#643) (#646)
(cherry picked from commit 6c27a462748575da1fd6e0f04baf116063f7498f)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-12-10 17:18:33 +08:00
mergify[bot]
e0dc77624b
feat: read addon displayname from label and add descheduler description (#644) (#645)
* refactor: display addon name from addon.harvesterhci.io/displayName label



* refactor: add descheduler description



---------


(cherry picked from commit b03fffbc3014dc7214177cac69bfcecdf7cb30c3)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-12-10 15:16:08 +08:00
renovate[bot]
c3ba10bd22
deps: update dependency node-forge to v1.3.3 (#641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-07 09:15:44 +00:00
mergify[bot]
5b7d54d0a3
chore: bump to v1.7.0-rc5 (#636) (#637)
(cherry picked from commit 416098ffd822ff531d8cfa42fdbfed1c8d53ff53)

Signed-off-by: Vicente Cheng <vicente.cheng@suse.com>
Co-authored-by: freeze <1615081+Vicente-Cheng@users.noreply.github.com>
2025-12-04 22:45:15 +08:00
renovate[bot]
99a216dfa0
deps: update dependency yaml to v2.8.2 (#634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 16:55:46 +00:00
mergify[bot]
e4c85f510e
chore: bump to v1.7.0-rc4 (#621) (#629)
(cherry picked from commit 3d7b96d86d3fce8f7746f1eca1a90ea9b31bb67d)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-28 13:23:55 +08:00
mergify[bot]
f391f018de
fix: create new secret on vm creation (#614) (#628)
* fix: create new secret on vm creation



* fix: ensure parseVM result is immutable



---------


(cherry picked from commit 0b37467f7637639209c27570bfe5633a41b96ac0)

Signed-off-by: Caio Torres <caio.torres@suse.com>
Co-authored-by: Caio Torres <caio.torres@suse.com>
2025-11-27 17:42:46 +08:00
renovate[bot]
4b2e92ea15
deps: update dependency node-forge to v1.3.2 [security] (#627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 14:31:50 +08:00
mergify[bot]
2f956d5946
feat: allow user to attach volume to muliple VMs (#620) (#622)
(cherry picked from commit d94003f8c28876b4e5803bb04a6049ab63f812e6)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-27 14:30:44 +08:00
renovate[bot]
5f8d556ea2
deps: update patch digest dependencies (#619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-23 09:32:21 +00:00
mergify[bot]
396ab48f1c
chore: bump version to v1.7.0-rc3 (#612) (#613) 2025-11-20 10:25:03 +08:00
mergify[bot]
6b8c079018
fix: condition render namespaceOptions (#607) (#611)
(cherry picked from commit 1b183febdc5e13d29d48b655198114f7d38af526)

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
Co-authored-by: Yiya Chen <yiya.chen@suse.com>
2025-11-19 17:40:03 +08:00
mergify[bot]
7f638e86c8
fix: change migConfiguration model to inherit from harvester resource (#608) (#609)
(cherry picked from commit 70d3b656f78439ba56816d75b526bc5637a67e1f)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-19 17:35:52 +08:00
mergify[bot]
0c4955a766
feat: create related image storageclass before OS upgrade (#595) (#606)
* feat: create related image SC before upgrade



* refactor: update spec.targetStorageClassName



* refactor: based on comment



---------


(cherry picked from commit 10d19cd329cce7e376ce2712a8843742d8968b65)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-17 17:34:51 +08:00
mergify[bot]
3f4ff30275
feat: modified placeholder (#599) (#605)
(cherry picked from commit 1715ae754caf4cb6e0688d8eb4326f9b0a90f960)

Co-authored-by: Yiya Chen <yiya.chen@suse.com>
2025-11-17 17:08:20 +08:00
renovate[bot]
8e0332a364
deps: update dependency @types/node to v20.19.25 (#604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-16 13:03:17 +00:00
mergify[bot]
6700b2055e
feat: add support for configuring transparent hugepages (#414) (#598)
* feat: add support for configuring transparent hugepages

Related-to: https://github.com/harvester/harvester/issues/5006



* fix: return empty object if hugepages can't be found for node

Related-to: https://github.com/harvester/harvester/issues/5006




---------




(cherry picked from commit 30de2b1a185ccc2a3ec159e220de742dd2156229)

Signed-off-by: Tim Serong <tserong@suse.com>
Co-authored-by: Tim Serong <tserong@suse.com>
Co-authored-by: Moritz Röhrich <moritz.rohrich@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-12 14:15:27 +08:00
mergify[bot]
e486852f7a
chore: bump version to v1.7.0-rc2 (#596) (#597)
(cherry picked from commit 6fedcc353c59df9e36693822f56c0be78029a46a)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-12 09:21:01 +08:00
mergify[bot]
c19341bec9
feat: support for HotPlugNICs from Kubevirt (#582) (#594)
* refactor: rename hotplug volume
* feat: add hotplug NIC
* feat: add hot unplug
* refactor: rename NIC
* feat: get latest status
* feat: disable not ready options
* feat: filter out system networks
* refactor: update wordings

---------


(cherry picked from commit f9bff21e840885a120679864a0ef312163bd48a7)

Signed-off-by: Yi-Ya Chen <yiya.chen@suse.com>
Co-authored-by: Yiya Chen <yiya.chen@suse.com>
2025-11-11 11:46:43 +08:00
mergify[bot]
8cbb9d6b18
chore: update yarn.lock for @rancher/shell v3.0.8-rc.8 (#591) (#592)
(cherry picked from commit 6735826e15d6515f87fa752418141ebb79cc5c1f)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-10 16:19:48 +08:00
mergify[bot]
00f0953592
chore: bump shell version to v3.0.8-rc.8 (#588) (#590)
(cherry picked from commit 9e17e239cf9d6e65f5d2f42636bac19cef70bb16)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-10 15:05:04 +08:00
mergify[bot]
58507f0b2e
ci: lint last commit if is empty string or all zero (#584) (#586)
(cherry picked from commit db58024351e06c6ddc90d6143cae9133ddbbfedc)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-06 15:53:50 +08:00
mergify[bot]
7785d7f469
feat: enable snapshot and clone for LHv2 (#379) (#587)
Now that Longhorn supports volume clone with the V2 data engine, we
can enable volume snapshot and clone.

Related issue: https://github.com/harvester/harvester/issues/6710


(cherry picked from commit a1cf41bda92ceb399be21904ec267311f9568bdb)

Signed-off-by: Tim Serong <tserong@suse.com>
Co-authored-by: Tim Serong <tserong@suse.com>
2025-11-06 15:53:20 +08:00
mergify[bot]
2c043e0a8e
chore: bump version to v1.7.0-rc1 (#583) (#585)
(cherry picked from commit 81bf19419c56dbd670d4cf8a1b9b658bbae6ea4f)

Signed-off-by: Andy Lee <andy.lee@suse.com>
Co-authored-by: Andy Lee <andy.lee@suse.com>
2025-11-04 16:12:14 +08:00
65 changed files with 302 additions and 6123 deletions

View File

@ -7,8 +7,7 @@
], ],
"baseBranches": [ "baseBranches": [
"main", "main",
"release-harvester-v1.7", "/^release-harvester-v\\d+\\.\\d+$/"
"release-harvester-v1.8"
], ],
"automergeMajor": false, "automergeMajor": false,
"semanticCommits": "enabled", "semanticCommits": "enabled",

View File

@ -1,34 +0,0 @@
name: FOSSA Scanning
on:
push:
branches: ["main", "release-harvester-v*"]
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
fossa-scanning:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
# The FOSSA token is shared between all repos in Harvester's GH org. It can
# be used directly and there is no need to request specific access to EIO.
- name: Read FOSSA token
uses: rancher-eio/read-vault-secrets@main
with:
secrets: |
secret/data/github/org/harvester/fossa/credentials token | FOSSA_API_KEY_PUSH_ONLY
- name: FOSSA scan
uses: fossas/fossa-action@main
with:
api-key: ${{ env.FOSSA_API_KEY_PUSH_ONLY }}
# Only runs the scan and do not provide/returns any results back to the
# pipeline.
run-tests: false

View File

@ -7,7 +7,7 @@ The Harvester UI Extension is a Rancher extension that provides the user interfa
## Installation ## Installation
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io). For detailed installation instructions, please refer to the [official Harvester documentation](https://docs.harvesterhci.io/v1.5/rancher/harvester-ui-extension#installation-on-rancher-210).
## Development Setup ## Development Setup
@ -163,7 +163,7 @@ If you want to contribute, start by reading this document, then visit our [Getti
## License ## License
Copyright (c) 2014-2026 [SUSE, LLC.](https://www.suse.com/) Copyright (c) 2014-2025 [SUSE, LLC.](https://www.suse.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.

View File

@ -1,22 +1,17 @@
{ {
"name": "harvester-ui-extension", "name": "harvester-ui-extension",
"version": "1.8.0-rc2", "version": "1.7.1",
"private": false, "private": false,
"engines": { "engines": {
"node": ">=20.0.0" "node": ">=20.0.0"
}, },
"dependencies": { "dependencies": {
"@babel/plugin-transform-class-static-block": "7.28.6", "@babel/plugin-transform-class-static-block": "7.28.6",
"@rancher/shell": "3.0.9-rc.6", "@rancher/shell": "3.0.8-rc.8",
"@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.1",
"@vue-flow/core": "^1.33.5",
"@vue-flow/minimap": "^1.4.0",
"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",
"node-polyfill-webpack-plugin": "^3.0.0", "node-polyfill-webpack-plugin": "^3.0.0",
"elkjs": "^0.11.0",
"vue-draggable-next": "^2.2.1", "vue-draggable-next": "^2.2.1",
"yaml": "^2.5.1" "yaml": "^2.5.1"
}, },
@ -29,11 +24,11 @@
"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",
"@types/lodash": "4.17.24", "@types/lodash": "4.17.23",
"merge": "2.1.1", "merge": "2.1.1",
"node-forge": "1.3.3", "node-forge": "1.3.3",
"nth-check": "2.1.1", "nth-check": "2.1.1",
"qs": "6.15.0", "qs": "6.14.1",
"roarr": "7.21.4", "roarr": "7.21.4",
"semver": "7.7.4", "semver": "7.7.4",
"@vue/cli-service/html-webpack-plugin": "^5.0.0" "@vue/cli-service/html-webpack-plugin": "^5.0.0"
@ -43,7 +38,6 @@
"build": "./node_modules/.bin/vue-cli-service build", "build": "./node_modules/.bin/vue-cli-service build",
"clean": "./node_modules/@rancher/shell/scripts/clean", "clean": "./node_modules/@rancher/shell/scripts/clean",
"lint": "./node_modules/.bin/eslint --max-warnings 0 --ext .js,.ts,.vue .", "lint": "./node_modules/.bin/eslint --max-warnings 0 --ext .js,.ts,.vue .",
"lint:fix": "./node_modules/.bin/eslint --fix --max-warnings 0 --ext .js,.ts,.vue .",
"build-pkg": "./node_modules/@rancher/shell/scripts/build-pkg.sh", "build-pkg": "./node_modules/@rancher/shell/scripts/build-pkg.sh",
"serve-pkgs": "./node_modules/@rancher/shell/scripts/serve-pkgs", "serve-pkgs": "./node_modules/@rancher/shell/scripts/serve-pkgs",
"publish-pkgs": "./node_modules/@rancher/shell/scripts/extension/publish", "publish-pkgs": "./node_modules/@rancher/shell/scripts/extension/publish",

View File

@ -7,7 +7,8 @@ The Harvester UI Extension is a Rancher extension that provides the user interfa
## Installation ## Installation
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io). For detailed installation instructions, please refer to the [official Harvester documentation](https://docs.harvesterhci.io/v1.5/rancher/harvester-ui-extension#installation-on-rancher-210).
## Development Setup ## Development Setup
@ -162,7 +163,7 @@ If you want to contribute, start by reading this document, then visit our [Getti
## License ## License
Copyright (c) 2014-2026 [SUSE, LLC.](https://www.suse.com/) Copyright (c) 2014-2025 [SUSE, LLC.](https://www.suse.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.

View File

@ -1,8 +1,6 @@
<script> <script>
import Collapse from '@shell/components/Collapse'; import Collapse from '@shell/components/Collapse';
import PercentageBar from '@shell/components/PercentageBar'; import PercentageBar from '@shell/components/PercentageBar';
import { HCI } from '../types';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
export default { export default {
name: 'HarvesterUpgradeProgressList', name: 'HarvesterUpgradeProgressList',
@ -27,45 +25,13 @@ export default {
} }
}, },
async fetch() {
await this.$store.dispatch('harvester/findAll', { type: HCI.UPGRADE });
},
data() { data() {
return { open: true }; return { open: true };
}, },
computed: {
showResumeButton() {
return this.title === 'Upgrading Node';
},
latestUpgradeCR() {
return this.$store.getters['harvester/all'](HCI.UPGRADE).find( (U) => U.isLatestUpgrade);
},
resumeUpgradePausedNodeEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('resumeUpgradePausedNode');
},
},
methods: { methods: {
handleSwitch() { handleSwitch() {
this.open = !this.open; this.open = !this.open;
},
async resumeNodeUpgrade(nodeName) {
if (!this.latestUpgradeCR || !nodeName) return;
try {
const upgradePauseMapString = this.latestUpgradeCR.metadata.annotations[HCI_ANNOTATIONS.NODE_UPGRADE_PAUSE_MAP] || '{}';
const upgradePauseMap = JSON.parse(upgradePauseMapString);
// update the upgrade CR annotation harvesterhci.io/node-upgrade-pause-map to unpause the node upgrade process
upgradePauseMap[`${ nodeName }`] = 'unpause';
this.latestUpgradeCR.setAnnotation(HCI_ANNOTATIONS.NODE_UPGRADE_PAUSE_MAP, JSON.stringify(upgradePauseMap));
await this.latestUpgradeCR.save();
} catch (e) {
console.error(`unable to update harvester upgrade CR annotations: ${ this.latestUpgradeCR.id }.`, e); // eslint-disable-line no-console
return false;
}
} }
} }
}; };
@ -97,28 +63,12 @@ export default {
v-for="(item, i) in list" v-for="(item, i) in list"
:key="i" :key="i"
> >
<div class="upgrade-node-header"> <p>
<div class="upgrade-node-title"> {{ item.name }} <span
<p> class="status"
{{ item.name }} :class="{ [item.state]: true }"
</p> >{{ item.state }}</span>
<span </p>
class="status"
:class="{ [item.state]: true }"
>
{{ item.state }}
</span>
</div>
<button
v-if="showResumeButton && resumeUpgradePausedNodeEnabled && item.state === 'Node-upgrade paused'"
type="button"
class="btn bg-info btn-sm"
data-testid="add-item"
@click="resumeNodeUpgrade(item.name)"
>
{{ t('action.resume') }}
</button>
</div>
<PercentageBar <PercentageBar
:model-value="item.percent" :model-value="item.percent"
preferred-direction="MORE" preferred-direction="MORE"
@ -152,21 +102,10 @@ export default {
} }
} }
.custom-content { .custom-content {
.upgrade-node-title { margin-bottom: 14px;
flex: 1 0 80%; p {
margin-right: 10px;
display: flex;
justify-content: space-between;
}
.upgrade-node-header {
display:flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px; margin-bottom: 4px;
} }
margin-bottom: 14px;
.status { .status {
float: right; float: right;
} }
@ -178,8 +117,6 @@ export default {
} }
.warning { .warning {
color: var(--error); color: var(--error);
margin-bottom: 8px;
margin-top: 4px;
} }
} }
} }

View File

@ -1,123 +0,0 @@
<script>
import MessageLink from '@shell/components/MessageLink';
import CreateEditView from '@shell/mixins/create-edit-view';
import { LabeledInput } from '@components/Form/LabeledInput';
import { HCI_SETTING } from '../../config/settings';
import { Checkbox } from '@components/Form/Checkbox';
import { Banner } from '@components/Banner';
export default {
name: 'HarvesterEditClusterRegistrationURL',
components: {
LabeledInput, MessageLink, Checkbox, Banner
},
mixins: [CreateEditView],
data() {
let parseDefaultValue = {};
try {
parseDefaultValue = JSON.parse(this.value.value);
} catch (error) {
parseDefaultValue.url = this.value.value;
parseDefaultValue.insecureSkipTLSVerify = true;
}
return {
parseDefaultValue,
errors: []
};
},
computed: {
toCA() {
return `${ HCI_SETTING.ADDITIONAL_CA }?mode=edit`;
},
clusterRegistrationTLSVerifyEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('clusterRegistrationTLSVerify');
},
registrationURL: {
get() {
return this.clusterRegistrationTLSVerifyEnabled ? this.parseDefaultValue.url : this.parseDefaultValue;
},
set(value) {
if (this.clusterRegistrationTLSVerifyEnabled) {
this.parseDefaultValue.url = value;
} else {
this.parseDefaultValue = value;
}
}
}
},
methods: {
getDefaultValue() {
if (this.clusterRegistrationTLSVerifyEnabled) {
return { url: '', insecureSkipTLSVerify: false };
} else {
return '';
}
},
updateUrl() {
this.update();
},
update() {
if (this.clusterRegistrationTLSVerifyEnabled) {
this.value['value'] = JSON.stringify(this.parseDefaultValue);
} else {
this.value['value'] = this.parseDefaultValue;
}
},
useDefault() {
this.parseDefaultValue = this.getDefaultValue();
},
updateInsecureSkipTLSVerify(newValue) {
const { url = '' } = this.parseDefaultValue;
this.parseDefaultValue = { url, insecureSkipTLSVerify: newValue };
this.update();
},
}
};
</script>
<template>
<div
class="row"
>
<div class="col span-12">
<Banner color="info">
<MessageLink
:to="toCA"
target="_blank"
prefix-label="harvester.setting.clusterRegistrationUrl.tip.prefix"
middle-label="harvester.setting.clusterRegistrationUrl.tip.middle"
suffix-label="harvester.setting.clusterRegistrationUrl.tip.suffix"
/>
</Banner>
<LabeledInput
v-model:value="registrationURL"
class="mb-20"
:mode="mode"
:label="t('harvester.setting.clusterRegistrationUrl.url')"
@update:value="updateUrl"
/>
<div v-if="clusterRegistrationTLSVerifyEnabled">
<Checkbox
v-model:value="parseDefaultValue.insecureSkipTLSVerify"
class="check mb-5"
type="checkbox"
:label="t('harvester.setting.clusterRegistrationUrl.insecureSkipTLSVerify')"
@update:value="updateInsecureSkipTLSVerify"
/>
</div>
</div>
</div>
</template>

View File

@ -1,104 +0,0 @@
<script>
import UnitInput from '@shell/components/form/UnitInput';
import { Banner } from '@components/Banner';
export default {
name: 'HarvesterInstanceManagerResources',
components: {
UnitInput,
Banner,
},
props: {
value: {
type: Object,
default: () => ({
value: '',
default: '{}'
})
},
},
data() {
const resources = this.parseJSON(this.value?.value) || this.parseJSON(this.value?.default) || {};
return {
resources,
parseError: null,
};
},
methods: {
parseJSON(string) {
try {
return JSON.parse(string);
} catch (e) {
this.parseError = this.t('harvester.setting.instanceManagerResources.parseError', { error: e.message });
return null;
}
},
update() {
if (!this.value) return;
const cpu = { ...this.resources?.cpu };
if (cpu.v1 !== null) cpu.v1 = String(cpu.v1);
if (cpu.v2 !== null) cpu.v2 = String(cpu.v2);
this.value.value = JSON.stringify({ ...this.resources, cpu });
},
useDefault() {
if (this.value?.default) {
this.resources = this.parseJSON(this.value.default) || {};
this.update();
}
},
},
};
</script>
<template>
<div>
<Banner
v-if="parseError"
color="error"
>
{{ parseError }}
</Banner>
<div class="row">
<div class="col span-12">
<UnitInput
v-model:value="resources.cpu.v1"
:label="t('harvester.setting.instanceManagerResources.v1')"
suffix="%"
:delay="0"
type="number"
min="0"
max="100"
required
:mode="mode"
class="mb-20"
@update:value="update"
/>
<UnitInput
v-model:value="resources.cpu.v2"
:label="t('harvester.setting.instanceManagerResources.v2')"
suffix="%"
:delay="0"
type="number"
min="0"
max="100"
required
:mode="mode"
class="mb-20"
@update:value="update"
/>
</div>
</div>
</div>
</template>

View File

@ -1,371 +0,0 @@
<script>
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect.vue';
import { RadioGroup } from '@components/Form/Radio';
import ArrayList from '@shell/components/form/ArrayList';
import { isValidCIDR } from '@shell/utils/validators/cidr';
import { _EDIT } from '@shell/config/query-params';
import { Banner } from '@components/Banner';
import { allHash } from '@shell/utils/promise';
import { HCI } from '../../types';
import { NETWORK_TYPE } from '../../config/types';
const { L2VLAN, UNTAGGED } = NETWORK_TYPE;
const SHARE_STORAGE_NETWORK = 'share-storage-network';
const NETWORK = 'network';
const DEFAULT_DEDICATED_NETWORK = {
vlan: '',
clusterNetwork: '',
range: '',
exclude: [],
};
export default {
name: 'RwxNetworkSetting',
components: {
RadioGroup,
Banner,
ArrayList,
LabeledInput,
LabeledSelect,
},
props: {
registerBeforeHook: {
type: Function,
required: true,
},
mode: {
type: String,
default: _EDIT,
},
value: {
type: Object,
default: () => {
return {};
},
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
await allHash({
clusterNetworks: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER_NETWORK }),
vlanStatus: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VLAN_STATUS }),
});
},
data() {
let enabled = false; // enabled / disabled options
let shareStorageNetwork = false; // shareStorageNetwork / dedicatedRwxNetwork options
let dedicatedNetwork = { ...DEFAULT_DEDICATED_NETWORK };
let networkType = L2VLAN;
let exclude = [];
try {
const parsedValue = JSON.parse(this.value.value || this.value.default || '{}');
const parsedNetwork = parsedValue?.[NETWORK] || parsedValue || {};
if (parsedValue && typeof parsedValue === 'object') {
shareStorageNetwork = !!parsedValue[SHARE_STORAGE_NETWORK];
networkType = 'vlan' in parsedNetwork ? L2VLAN : UNTAGGED;
dedicatedNetwork = {
vlan: parsedNetwork.vlan || '',
clusterNetwork: parsedNetwork.clusterNetwork || '',
range: parsedNetwork.range || '',
};
exclude = parsedNetwork?.exclude?.toString().split(',') || [];
enabled = shareStorageNetwork || !!(parsedNetwork.vlan || parsedNetwork.clusterNetwork || parsedNetwork.range);
}
} catch (error) {
enabled = false;
shareStorageNetwork = false;
dedicatedNetwork = { ...DEFAULT_DEDICATED_NETWORK };
}
return {
enabled,
shareStorageNetwork,
dedicatedNetwork,
networkType,
exclude,
defaultAddValue: '',
};
},
created() {
if (this.registerBeforeHook) {
this.registerBeforeHook(this.willSave, 'willSave');
}
},
computed: {
showDedicatedNetworkConfig() {
return this.enabled && !this.shareStorageNetwork;
},
showVlan() {
return this.networkType === L2VLAN;
},
networkTypes() {
return [L2VLAN, UNTAGGED];
},
clusterNetworkOptions() {
const inStore = this.$store.getters['currentProduct'].inStore;
const clusterNetworks = this.$store.getters[`${ inStore }/all`](HCI.CLUSTER_NETWORK) || [];
const clusterNetworksOptions = this.networkType === UNTAGGED ? clusterNetworks.filter((network) => network.id !== 'mgmt') : clusterNetworks;
return clusterNetworksOptions.map((network) => {
const disabled = !network.isReadyForStorageNetwork;
return {
label: disabled ? `${ network.id } (${ this.t('generic.notReady') })` : network.id,
value: network.id,
disabled,
};
});
},
},
methods: {
onUpdateEnabled() {
if (!this.enabled) {
this.shareStorageNetwork = false;
this.dedicatedNetwork = { ...DEFAULT_DEDICATED_NETWORK };
}
this.update();
},
onUpdateNetworkType() {
if (this.shareStorageNetwork) {
this.dedicatedNetwork = { ...DEFAULT_DEDICATED_NETWORK };
}
this.update();
},
onUpdateDedicatedType(neu) {
this.dedicatedNetwork.clusterNetwork = '';
if (neu === L2VLAN) {
this.dedicatedNetwork.vlan = '';
} else {
delete this.dedicatedNetwork.vlan;
}
this.update();
},
inputVlan(neu) {
if (neu === '') {
this.dedicatedNetwork.vlan = '';
this.update();
return;
}
const newValue = Number(neu);
if (newValue > 4094) {
this.dedicatedNetwork.vlan = 4094;
} else if (newValue < 1) {
this.dedicatedNetwork.vlan = 1;
} else {
this.dedicatedNetwork.vlan = newValue;
}
this.update();
},
useDefault() {
this.enabled = false;
this.shareStorageNetwork = false;
this.dedicatedNetwork = { ...DEFAULT_DEDICATED_NETWORK };
this.update();
},
update() {
const value = { [SHARE_STORAGE_NETWORK]: false };
if (this.enabled && this.shareStorageNetwork) {
value[SHARE_STORAGE_NETWORK] = true;
}
if (this.showDedicatedNetworkConfig) {
value[NETWORK] = {};
if (this.showVlan) {
value[NETWORK].vlan = this.dedicatedNetwork.vlan;
}
value[NETWORK].clusterNetwork = this.dedicatedNetwork.clusterNetwork;
value[NETWORK].range = this.dedicatedNetwork.range;
const excludeList = this.exclude.filter((ip) => ip);
if (Array.isArray(excludeList) && excludeList.length > 0) {
value[NETWORK].exclude = excludeList;
}
}
this.value.value = JSON.stringify(value);
},
willSave() {
this.update();
if (!this.showDedicatedNetworkConfig) {
return Promise.resolve();
}
const errors = [];
if (this.showVlan && !this.dedicatedNetwork.vlan) {
errors.push(this.t('validation.required', { key: this.t('harvester.setting.storageNetwork.vlan') }, true));
}
if (!this.dedicatedNetwork.clusterNetwork) {
errors.push(this.t('validation.required', { key: this.t('harvester.setting.storageNetwork.clusterNetwork') }, true));
}
if (!this.dedicatedNetwork.range) {
errors.push(this.t('validation.required', { key: this.t('harvester.setting.storageNetwork.range.label') }, true));
} else if (!isValidCIDR(this.dedicatedNetwork.range)) {
errors.push(this.t('harvester.setting.storageNetwork.range.invalid', null, true));
}
if (this.exclude) {
const hasInvalidCIDR = this.exclude.find((cidr) => {
return cidr && !isValidCIDR(cidr);
});
if (hasInvalidCIDR) {
errors.push(this.t('harvester.setting.storageNetwork.exclude.invalid', null, true));
}
}
if (errors.length > 0) {
return Promise.reject(errors);
}
return Promise.resolve();
},
},
};
</script>
<template>
<div :class="mode">
<Banner color="warning">
<t
k="harvester.setting.rwxNetwork.warning"
:raw="true"
/>
</Banner>
<RadioGroup
v-model:value="enabled"
class="mb-20"
name="rwx-network-enable"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
@update:value="onUpdateEnabled"
/>
<RadioGroup
v-if="enabled"
v-model:value="shareStorageNetwork"
class="mb-20"
name="rwx-network-type"
:options="[true,false]"
:labels="[t('harvester.setting.rwxNetwork.shareStorageNetwork'), t('harvester.setting.rwxNetwork.dedicatedRwxNetwork')]"
@update:value="onUpdateNetworkType"
/>
<Banner
v-if="shareStorageNetwork"
class="mb-20"
color="warning"
>
<t
k="harvester.setting.rwxNetwork.shareStorageNetworkWarning"
:raw="true"
/>
</Banner>
<template v-if="showDedicatedNetworkConfig">
<LabeledSelect
v-model:value="networkType"
class="mb-20"
:options="networkTypes"
:mode="mode"
:label="t('harvester.fields.type')"
required
@update:value="onUpdateDedicatedType"
/>
<LabeledInput
v-if="showVlan"
v-model:value.number="dedicatedNetwork.vlan"
type="number"
class="mb-20"
:mode="mode"
required
placeholder="e.g. 1 - 4094"
label-key="harvester.setting.storageNetwork.vlan"
@update:value="inputVlan"
/>
<LabeledSelect
v-model:value="dedicatedNetwork.clusterNetwork"
label-key="harvester.setting.storageNetwork.clusterNetwork"
class="mb-20"
required
:options="clusterNetworkOptions"
@update:value="update"
/>
<LabeledInput
v-model:value="dedicatedNetwork.range"
class="mb-5"
:mode="mode"
required
:placeholder="t('harvester.setting.storageNetwork.range.placeholder')"
label-key="harvester.setting.storageNetwork.range.label"
@update:value="update"
/>
<ArrayList
v-model:value="exclude"
:show-header="true"
:default-add-value="defaultAddValue"
:mode="mode"
:add-label="t('harvester.setting.storageNetwork.exclude.addIp')"
class="mt-20"
@update:value="update"
>
<template #column-headers>
<div class="box mb-10">
<div class="key">
{{ t('harvester.setting.storageNetwork.exclude.label') }}
</div>
</div>
</template>
<template #columns="scope">
<div class="key">
<input
v-model="scope.row.value"
:placeholder="t('harvester.setting.storageNetwork.exclude.placeholder')"
@update:value="update"
/>
</div>
</template>
</ArrayList>
</template>
</div>
</template>

View File

@ -4,8 +4,6 @@ import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect'; import LabeledSelect from '@shell/components/form/LabeledSelect';
import { RadioGroup } from '@components/Form/Radio'; import { RadioGroup } from '@components/Form/Radio';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { allHash } from '@shell/utils/promise';
import { NODE } from '@shell/config/types';
export default { export default {
name: 'HarvesterUpgradeConfig', name: 'HarvesterUpgradeConfig',
@ -17,13 +15,6 @@ export default {
}, },
mixins: [CreateEditView], mixins: [CreateEditView],
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
const hash = { nodes: this.$store.dispatch(`${ inStore }/findAll`, { type: NODE }) };
await allHash(hash);
},
data() { data() {
let parseDefaultValue = {}; let parseDefaultValue = {};
@ -48,25 +39,7 @@ export default {
{ value: 'skip', label: 'skip' }, { value: 'skip', label: 'skip' },
{ value: 'parallel', label: 'parallel' } { value: 'parallel', label: 'parallel' }
]; ];
}, }
nodeUpgradeOptions() {
return [
{ value: 'auto', label: 'auto' },
{ value: 'manual', label: 'manual' }
];
},
nodesOptions() {
const inStore = this.$store.getters['currentProduct'].inStore;
const nodes = this.$store.getters[`${ inStore }/all`](NODE);
return nodes.map((node) => ({ value: node.id, label: node.name }));
},
showPauseNodes() {
return this.parseDefaultValue.nodeUpgradeOption?.strategy?.mode === 'manual';
},
resumeUpgradePausedNodeEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('resumeUpgradePausedNode');
},
}, },
created() { created() {
@ -75,18 +48,6 @@ export default {
methods: { methods: {
normalizeValue(obj) { normalizeValue(obj) {
// handle nodeUpgradeOption.strategy
if (obj?.nodeUpgradeOption?.strategy?.mode === 'auto') {
delete obj.nodeUpgradeOption.strategy.pauseNodes;
}
if (obj?.nodeUpgradeOption?.strategy?.mode === 'manual') {
if (!Array.isArray(obj.nodeUpgradeOption.strategy.pauseNodes)) {
obj.nodeUpgradeOption.strategy.pauseNodes = this.nodesOptions.map((node) => node.value);
}
}
// handle imagePreloadOption.strategy
if (!obj.imagePreloadOption) { if (!obj.imagePreloadOption) {
obj.imagePreloadOption = { strategy: { type: 'sequential' } }; obj.imagePreloadOption = { strategy: { type: 'sequential' } };
} }
@ -144,8 +105,8 @@ export default {
this.update(); this.update();
}, },
deep: true deep: true
}, }
}, }
}; };
</script> </script>
@ -183,28 +144,6 @@ export default {
:labels="[t('generic.enabled'), t('generic.disabled')]" :labels="[t('generic.enabled'), t('generic.disabled')]"
@update:value="update" @update:value="update"
/> />
<div v-if="resumeUpgradePausedNodeEnabled">
<label class="mb-5"><b>{{ t('harvester.setting.upgrade.nodeUpgradeOption') }}</b></label>
<LabeledSelect
v-model:value="parseDefaultValue.nodeUpgradeOption.strategy.mode"
class="mb-20 label-select"
:mode="mode"
:label="t('harvester.setting.upgrade.strategy')"
:options="nodeUpgradeOptions"
@update:value="update"
/>
<LabeledSelect
v-if="showPauseNodes"
v-model:value="parseDefaultValue.nodeUpgradeOption.strategy.pauseNodes"
class="mb-20 label-select"
:clearable="true"
:multiple="true"
:mode="mode"
:label="t('harvester.setting.upgrade.pauseNodes')"
:options="nodesOptions"
@update:value="update"
/>
</div>
<div <div
v-if="errors.length" v-if="errors.length"
class="error" class="error"

View File

@ -69,15 +69,6 @@ export default {
:mode="mode" :mode="mode"
label-key="harvester.setting.vmForceDeletionPolicy.period" label-key="harvester.setting.vmForceDeletionPolicy.period"
/> />
<LabeledInput
v-if="parseDefaultValue.enable"
v-model:value.number="parseDefaultValue.vmMigrationTimeout"
class="mb-20"
type="number"
:mode="mode"
label-key="harvester.setting.vmForceDeletionPolicy.vmMigrationTimeout"
/>
</div> </div>
</div> </div>
</template> </template>

View File

@ -54,18 +54,9 @@ const FEATURE_FLAGS = {
'lhV2VolExpansion', 'lhV2VolExpansion',
'l2VlanTrunkMode', 'l2VlanTrunkMode',
'kubevirtMigration', 'kubevirtMigration',
'hotplugNic', 'hotplugNic'
'resumeUpgradePausedNode',
], ],
'v1.7.1': [], 'v1.7.1': [],
'v1.8.0': [
'hotplugCdRom',
'supportBundleFileNameSetting',
'clusterRegistrationTLSVerify',
'vGPUAsPCIDevice',
'instanceManagerResourcesSetting',
'rwxNetworkSetting',
],
}; };
const generateFeatureFlags = () => { const generateFeatureFlags = () => {

View File

@ -35,21 +35,8 @@ import {
SNAPSHOT_TARGET_VOLUME, SNAPSHOT_TARGET_VOLUME,
IMAGE_VIRTUAL_SIZE, IMAGE_VIRTUAL_SIZE,
IMAGE_STORAGE_CLASS, IMAGE_STORAGE_CLASS,
HARVESTER_DESCRIPTION, HARVESTER_DESCRIPTION
VM_IMPORT_SOURCE_VM,
VM_IMPORT_SOURCE_CLUSTER,
VM_IMPORT_STATUS,
VM_IMPORT_SOURCE_V_DC,
VM_IMPORT_SOURCE_V_ENDPOINT,
VM_IMPORT_SOURCE_V_STATUS,
VM_IMPORT_SOURCE_O_REGION,
VM_IMPORT_SOURCE_O_ENDPOINT,
VM_IMPORT_SOURCE_O_STATUS,
VM_IMPORT_SOURCE_OVA_URL,
VM_IMPORT_SOURCE_OVA_STATUS,
} from './table-headers'; } from './table-headers';
import { ADD_ONS } from './harvester-map';
import { registerAddonSideNav } from '../utils/dynamic-nav';
const TEMPLATE = HCI.VM_VERSION; const TEMPLATE = HCI.VM_VERSION;
const MONITORING_GROUP = 'Monitoring & Logging::Monitoring'; const MONITORING_GROUP = 'Monitoring & Logging::Monitoring';
@ -208,142 +195,6 @@ export function init($plugin, store) {
exact: false exact: false
}); });
// ===========================================================================
// VM Import Controller UI Flow
// ===========================================================================
// Define group (Hidden by default)
weightGroup('vmimport', 0, false);
// VirtualMachineImport
headers(HCI.VMIMPORT, [
STATE,
NAME_COL,
NAMESPACE_COL,
VM_IMPORT_SOURCE_VM,
VM_IMPORT_SOURCE_CLUSTER,
VM_IMPORT_STATUS,
AGE
]);
configureType(HCI.VMIMPORT, {
resource: HCI.VMIMPORT,
resourceDetail: HCI.VMIMPORT,
resourceEdit: HCI.VMIMPORT,
location: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT }
}
});
virtualType({ // needed to avoid 404 on refresh when combined with registerAddonSideNav()
name: HCI.VMIMPORT,
labelKey: 'harvester.addons.vmImport.labels.vmimport',
group: 'vmimport',
namespaced: true,
route: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT }
}
});
// Source: VMware
headers(HCI.VMIMPORT_SOURCE_V, [
STATE,
NAME_COL,
VM_IMPORT_SOURCE_V_ENDPOINT,
VM_IMPORT_SOURCE_V_DC,
VM_IMPORT_SOURCE_V_STATUS,
AGE
]);
configureType(HCI.VMIMPORT_SOURCE_V, {
resource: HCI.VMIMPORT_SOURCE_V,
resourceDetail: HCI.VMIMPORT_SOURCE_V,
resourceEdit: HCI.VMIMPORT_SOURCE_V,
location: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT_SOURCE_V }
}
});
virtualType({ // needed to avoid 404 on refresh when combined with registerAddonSideNav()
name: HCI.VMIMPORT_SOURCE_V,
labelKey: 'harvester.addons.vmImport.labels.vmimportSourceVMWare',
group: 'vmimport',
namespaced: true,
route: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT_SOURCE_V }
}
});
// Source: OpenStack
headers(HCI.VMIMPORT_SOURCE_O, [
STATE,
NAME_COL,
VM_IMPORT_SOURCE_O_ENDPOINT,
VM_IMPORT_SOURCE_O_REGION,
VM_IMPORT_SOURCE_O_STATUS,
AGE
]);
configureType(HCI.VMIMPORT_SOURCE_O, {
resource: HCI.VMIMPORT_SOURCE_O,
resourceDetail: HCI.VMIMPORT_SOURCE_O,
resourceEdit: HCI.VMIMPORT_SOURCE_O,
location: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT_SOURCE_O }
}
});
virtualType({ // needed to avoid 404 on refresh when combined with registerAddonSideNav()
name: HCI.VMIMPORT_SOURCE_O,
labelKey: 'harvester.addons.vmImport.labels.vmimportSourceOpenStack',
group: 'vmimport',
namespaced: true,
route: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT_SOURCE_O }
}
});
// Source: OVA
headers(HCI.VMIMPORT_SOURCE_OVA, [
STATE,
NAME_COL,
VM_IMPORT_SOURCE_OVA_URL,
VM_IMPORT_SOURCE_OVA_STATUS,
AGE
]);
configureType(HCI.VMIMPORT_SOURCE_OVA, {
resource: HCI.VMIMPORT_SOURCE_OVA,
resourceDetail: HCI.VMIMPORT_SOURCE_OVA,
resourceEdit: HCI.VMIMPORT_SOURCE_OVA,
location: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT_SOURCE_OVA }
}
});
virtualType({ // needed to avoid 404 on refresh when combined with registerAddonSideNav()
name: HCI.VMIMPORT_SOURCE_OVA,
labelKey: 'harvester.addons.vmImport.labels.vmimportSourceOVA',
group: 'vmimport',
namespaced: true,
route: {
name: `${ PRODUCT_NAME }-c-cluster-resource`,
params: { resource: HCI.VMIMPORT_SOURCE_OVA }
}
});
// Enable SideNav based on Addon Status
registerAddonSideNav(store, PRODUCT_NAME, {
addonName: ADD_ONS.VM_IMPORT_CONTROLLER,
resourceType: HCI.ADD_ONS,
navGroup: 'vmimport',
types: [
HCI.VMIMPORT_SOURCE_V,
HCI.VMIMPORT_SOURCE_O,
HCI.VMIMPORT_SOURCE_OVA,
HCI.VMIMPORT
]
});
// ===========================================================================
basicType([HCI.VOLUME]); basicType([HCI.VOLUME]);
configureType(HCI.VOLUME, { configureType(HCI.VOLUME, {
location: { location: {

View File

@ -39,12 +39,6 @@ export const VOLUME_TYPE = [{
value: 'cd-rom' value: 'cd-rom'
}]; }];
export const VOLUME_HOTPLUG_ACTION = {
INSERT_CDROM_IMAGE: 'INSERT_CDROM_IMAGE',
EJECT_CDROM_IMAGE: 'EJECT_CDROM_IMAGE',
DETACH_DISK: 'DETACH_DISK'
};
export const ACCESS_CREDENTIALS = { export const ACCESS_CREDENTIALS = {
RESET_PWD: 'userPassword', RESET_PWD: 'userPassword',
INJECT_SSH: 'sshPublicKey' INJECT_SSH: 'sshPublicKey'

View File

@ -28,7 +28,6 @@ export const HCI = {
NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane', NODE_ROLE_CONTROL_PLANE: 'node-role.kubernetes.io/control-plane',
NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness', NODE_ROLE_ETCD: 'node-role.harvesterhci.io/witness',
PROMOTE_STATUS: 'harvesterhci.io/promote-status', PROMOTE_STATUS: 'harvesterhci.io/promote-status',
CLONE_BACKEND_STORAGE_STATUS: 'harvesterhci.io/clone-backend-storage-status',
MIGRATION_STATE: 'harvesterhci.io/migrationState', MIGRATION_STATE: 'harvesterhci.io/migrationState',
VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates', VOLUME_CLAIM_TEMPLATE: 'harvesterhci.io/volumeClaimTemplates',
IMAGE_NAME: 'harvesterhci.io/image-name', IMAGE_NAME: 'harvesterhci.io/image-name',
@ -79,5 +78,4 @@ export const HCI = {
VOLUME_MODE_ACCESS_MODES: 'cdi.harvesterhci.io/storageProfileVolumeModeAccessModes', VOLUME_MODE_ACCESS_MODES: 'cdi.harvesterhci.io/storageProfileVolumeModeAccessModes',
VOLUME_SNAPSHOT_CLASS: 'cdi.harvesterhci.io/storageProfileVolumeSnapshotClass', VOLUME_SNAPSHOT_CLASS: 'cdi.harvesterhci.io/storageProfileVolumeSnapshotClass',
MAC_ADDRESS: 'harvesterhci.io/mac-address', MAC_ADDRESS: 'harvesterhci.io/mac-address',
NODE_UPGRADE_PAUSE_MAP: 'harvesterhci.io/node-upgrade-pause-map',
}; };

View File

@ -16,11 +16,9 @@ export const HCI_SETTING = {
DEFAULT_STORAGE_CLASS: 'default-storage-class', DEFAULT_STORAGE_CLASS: 'default-storage-class',
SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout', SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout',
SUPPORT_BUNDLE_EXPIRATION: 'support-bundle-expiration', SUPPORT_BUNDLE_EXPIRATION: 'support-bundle-expiration',
SUPPORT_BUNDLE_FILE_NAME: 'support-bundle-file-name',
SUPPORT_BUNDLE_IMAGE: 'support-bundle-image', SUPPORT_BUNDLE_IMAGE: 'support-bundle-image',
SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT: 'support-bundle-node-collection-timeout', SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT: 'support-bundle-node-collection-timeout',
STORAGE_NETWORK: 'storage-network', STORAGE_NETWORK: 'storage-network',
RWX_NETWORK: 'rwx-network',
VM_FORCE_RESET_POLICY: 'vm-force-reset-policy', VM_FORCE_RESET_POLICY: 'vm-force-reset-policy',
SSL_CERTIFICATES: 'ssl-certificates', SSL_CERTIFICATES: 'ssl-certificates',
SSL_PARAMETERS: 'ssl-parameters', SSL_PARAMETERS: 'ssl-parameters',
@ -40,8 +38,7 @@ export const HCI_SETTING = {
VM_MIGRATION_NETWORK: 'vm-migration-network', VM_MIGRATION_NETWORK: 'vm-migration-network',
RANCHER_CLUSTER: 'rancher-cluster', RANCHER_CLUSTER: 'rancher-cluster',
MAX_HOTPLUG_RATIO: 'max-hotplug-ratio', MAX_HOTPLUG_RATIO: 'max-hotplug-ratio',
KUBEVIRT_MIGRATION: 'kubevirt-migration', KUBEVIRT_MIGRATION: 'kubevirt-migration'
INSTANCE_MANAGER_RESOURCES: 'instance-manager-resources'
}; };
export const HCI_ALLOWED_SETTINGS = { export const HCI_ALLOWED_SETTINGS = {
@ -74,17 +71,11 @@ export const HCI_ALLOWED_SETTINGS = {
[HCI_SETTING.OVERCOMMIT_CONFIG]: { kind: 'json', from: 'import' }, [HCI_SETTING.OVERCOMMIT_CONFIG]: { kind: 'json', from: 'import' },
[HCI_SETTING.SUPPORT_BUNDLE_TIMEOUT]: { kind: 'number' }, [HCI_SETTING.SUPPORT_BUNDLE_TIMEOUT]: { kind: 'number' },
[HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: { kind: 'number' }, [HCI_SETTING.SUPPORT_BUNDLE_EXPIRATION]: { kind: 'number' },
[HCI_SETTING.SUPPORT_BUNDLE_FILE_NAME]: {
kind: 'string', canReset: true, featureFlag: 'supportBundleFileNameSetting'
},
[HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: { kind: 'number', featureFlag: 'supportBundleNodeCollectionTimeoutSetting' }, [HCI_SETTING.SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT]: { kind: 'number', 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', canReset: true kind: 'custom', from: 'import', canReset: true
}, },
[HCI_SETTING.RWX_NETWORK]: {
kind: 'json', from: 'import', canReset: true, featureFlag: 'rwxNetworkSetting'
},
[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]: {
@ -127,16 +118,12 @@ export const HCI_ALLOWED_SETTINGS = {
}, },
[HCI_SETTING.KUBEVIRT_MIGRATION]: { [HCI_SETTING.KUBEVIRT_MIGRATION]: {
kind: 'json', from: 'import', canReset: true, featureFlag: 'kubevirtMigration', kind: 'json', from: 'import', canReset: true, featureFlag: 'kubevirtMigration',
},
[HCI_SETTING.INSTANCE_MANAGER_RESOURCES]: {
kind: 'json', from: 'import', featureFlag: 'instanceManagerResourcesSetting'
} }
}; };
export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = { export const HCI_SINGLE_CLUSTER_ALLOWED_SETTING = {
[HCI_SETTING.CLUSTER_REGISTRATION_URL]: { [HCI_SETTING.CLUSTER_REGISTRATION_URL]: {
kind: 'custom', kind: 'url',
from: 'import',
canReset: true, canReset: true,
}, },
[HCI_SETTING.UI_PL]: { [HCI_SETTING.UI_PL]: {

View File

@ -131,102 +131,3 @@ export const PROVIDER = {
value: 'spec.provider', value: 'spec.provider',
align: 'left', align: 'left',
}; };
// Source VM column in migration.harvesterhci.io.virtualmachineimport list page
export const VM_IMPORT_SOURCE_VM = {
name: 'sourceVm',
labelKey: 'harvester.tableHeaders.vmImportSourceVm',
value: 'spec.virtualMachineName',
sort: 'spec.virtualMachineName',
align: 'left',
};
// Source Cluster column in migration.harvesterhci.io.virtualmachineimport list page
export const VM_IMPORT_SOURCE_CLUSTER = {
name: 'sourceCluster',
labelKey: 'harvester.tableHeaders.vmImportSourceCluster',
value: 'spec.sourceCluster.name',
sort: 'spec.sourceCluster.name',
align: 'left',
};
// Import Status column in migration.harvesterhci.io.virtualmachineimport list page
export const VM_IMPORT_STATUS = {
name: 'importStatus',
labelKey: 'harvester.tableHeaders.vmImportStatus',
value: 'status.importStatus',
sort: 'status.importStatus',
align: 'left',
};
// Datacenter column in migration.harvesterhci.io.vmwaresource list page
export const VM_IMPORT_SOURCE_V_DC = {
name: 'datacenter',
labelKey: 'harvester.tableHeaders.vmImportSourceVDatacenter',
value: 'spec.dc',
sort: 'spec.dc',
align: 'left',
};
// Endpoint column in migration.harvesterhci.io.vmwaresource list page
export const VM_IMPORT_SOURCE_V_ENDPOINT = {
name: 'endpoint',
labelKey: 'harvester.tableHeaders.vmImportSourceVEndpoint',
value: 'spec.endpoint',
sort: 'spec.endpoint',
align: 'left',
};
// Cluster Status column in migration.harvesterhci.io.vmwaresource list page
export const VM_IMPORT_SOURCE_V_STATUS = {
name: 'clusterStatus',
labelKey: 'harvester.tableHeaders.vmImportSourceVClusterStatus',
value: 'status.status',
sort: 'status.status',
align: 'left',
};
// Region column in migration.harvesterhci.io.openstacksource list page
export const VM_IMPORT_SOURCE_O_REGION = {
name: 'region',
labelKey: 'harvester.tableHeaders.vmImportSourceORegion',
value: 'spec.region',
sort: 'spec.region',
align: 'left',
};
// Endpoint column in migration.harvesterhci.io.openstacksource list page
export const VM_IMPORT_SOURCE_O_ENDPOINT = {
name: 'endpoint',
labelKey: 'harvester.tableHeaders.vmImportSourceOEndpoint',
value: 'spec.endpoint',
sort: 'spec.endpoint',
align: 'left',
};
// Cluster Status column in migration.harvesterhci.io.openstacksource list page
export const VM_IMPORT_SOURCE_O_STATUS = {
name: 'clusterStatus',
labelKey: 'harvester.tableHeaders.vmImportSourceOClusterStatus',
value: 'status.status',
sort: 'status.status',
align: 'left',
};
// URL column in migration.harvesterhci.io.ovasource list page
export const VM_IMPORT_SOURCE_OVA_URL = {
name: 'url',
labelKey: 'harvester.tableHeaders.vmImportSourceOVAUrl',
value: 'spec.url',
sort: 'spec.url',
align: 'left',
};
// Status column in migration.harvesterhci.io.ovasource list page
export const VM_IMPORT_SOURCE_OVA_STATUS = {
name: 'status',
labelKey: 'harvester.tableHeaders.vmImportSourceOVAStatus',
value: 'status.status',
sort: 'status.status',
align: 'left',
};

View File

@ -29,15 +29,3 @@ export const L2VLAN_MODE = {
ACCESS: 'access', ACCESS: 'access',
TRUNK: 'trunk', TRUNK: 'trunk',
}; };
export const VMIMPORT_SOURCE_PROVIDER = {
VMWARE: 'vmware',
OPENSTACK: 'openstack',
OVA: 'ova',
};
export const VMIMPORT_SOURCE_KINDS = {
VMWARE: 'VmwareSource',
OPENSTACK: 'OpenstackSource',
OVA: 'OvaSource',
};

View File

@ -75,7 +75,7 @@ export default {
<div class="row"> <div class="row">
<div class="col span-6 mb-20"> <div class="col span-6 mb-20">
<LabelValue <LabelValue
:name="t('harvester.schedule.cron.label')" :name="t('harvester.schedule.cron')"
:value="cronExpression" :value="cronExpression"
/> />
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,6 @@ import { Card } from '@components/Card';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton'; import AsyncButton from '@shell/components/AsyncButton';
const VOLUME = 'volume';
const NETWORK = 'network';
const CDROM = 'cdrom';
export default { export default {
name: 'HarvesterHotUnplug', name: 'HarvesterHotUnplug',
@ -44,37 +40,19 @@ export default {
}, },
isVolume() { isVolume() {
return this.modalData.type === VOLUME; return this.modalData.type === 'volume';
}, },
titleKey() { titleKey() {
const keys = { return this.isVolume ? 'harvester.virtualMachine.hotUnplug.detachVolume.title' : 'harvester.virtualMachine.hotUnplug.detachNIC.title';
[VOLUME]: 'harvester.virtualMachine.hotUnplug.detachVolume.title',
[CDROM]: 'harvester.virtualMachine.hotUnplug.ejectCdRomVolume.title',
[NETWORK]: 'harvester.virtualMachine.hotUnplug.detachNIC.title',
};
return keys[this.modalData.type];
}, },
actionLabelKey() { actionLabelKey() {
const keys = { return this.isVolume ? 'harvester.virtualMachine.hotUnplug.detachVolume.actionLabel' : 'harvester.virtualMachine.hotUnplug.detachNIC.actionLabel';
[VOLUME]: 'harvester.virtualMachine.hotUnplug.detachVolume.actionLabels',
[CDROM]: 'harvester.virtualMachine.hotUnplug.ejectCdRomVolume.actionLabels',
[NETWORK]: 'harvester.virtualMachine.hotUnplug.detachNIC.actionLabels',
};
return keys[this.modalData.type];
}, },
successMessageKey() { successMessageKey() {
const keys = { return this.isVolume ? 'harvester.virtualMachine.hotUnplug.detachVolume.success' : 'harvester.virtualMachine.hotUnplug.detachNIC.success';
[VOLUME]: 'harvester.virtualMachine.hotUnplug.detachVolume.success',
[CDROM]: 'harvester.virtualMachine.hotUnplug.ejectCdRomVolume.success',
[NETWORK]: 'harvester.virtualMachine.hotUnplug.detachNIC.success',
};
return keys[this.modalData.type];
} }
}, },
@ -87,12 +65,10 @@ export default {
try { try {
let res; let res;
if (this.modalData.type === VOLUME) { if (this.isVolume) {
res = await this.actionResource.doAction('removeVolume', { diskName: this.name }); res = await this.actionResource.doAction('removeVolume', { diskName: this.name });
} else if (this.modalData.type === NETWORK) {
res = await this.actionResource.doAction('removeNic', { interfaceName: this.name });
} else { } else {
res = await this.actionResource.doAction('ejectCdRomVolume', { deviceName: this.name }); res = await this.actionResource.doAction('removeNic', { interfaceName: this.name });
} }
if (res._status === 200 || res._status === 204) { if (res._status === 200 || res._status === 204) {

View File

@ -1,198 +0,0 @@
<script>
import { exceptionToErrorsArray } from '@shell/utils/error';
import { mapState, mapGetters } from 'vuex';
import { Card } from '@components/Card';
import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { HCI } from '../types';
export default {
name: 'HarvesterInsertCdRomVolume',
emits: ['close'],
components: {
AsyncButton,
Card,
LabeledInput,
LabeledSelect,
Banner
},
props: {
resources: {
type: Array,
required: true
}
},
async fetch() {
try {
this.images = await this.$store.dispatch('harvester/findAll', { type: HCI.IMAGE });
} catch (err) {
this.errors = exceptionToErrorsArray(err);
this.images = [];
}
},
data() {
return {
imageName: '',
images: [],
errors: [],
};
},
computed: {
...mapState('action-menu', ['modalData']),
...mapGetters({ t: 'i18n/t' }),
actionResource() {
return this.resources?.[0];
},
isFormValid() {
return this.imageName !== '';
},
deviceName() {
return this.modalData.name;
},
imagesOption() {
return this.images
.filter((image) => {
const labels = image.metadata?.labels || {};
const type = labels[HCI_ANNOTATIONS.IMAGE_SUFFIX];
return type === 'iso';
})
.map((image) => {
return ({
label: this.imageOptionLabel(image),
value: image.id,
disabled: image.isImportedImage
});
});
}
},
methods: {
close() {
this.imageName = '';
this.errors = [];
this.$emit('close');
},
imageOptionLabel(image) {
return `${ image.metadata.namespace }/${ image.spec.displayName }`;
},
async save(buttonCb) {
if (!this.actionResource) {
buttonCb(false);
return;
}
const payload = {
deviceName: this.deviceName,
imageName: this.imageName
};
try {
const res = await this.actionResource.doAction('insertCdRomVolume', payload);
if ([200, 204].includes(res?._status)) {
this.$store.dispatch('growl/success', {
title: this.t('generic.notification.title.succeed'),
message: this.t('harvester.modal.insertCdRomVolume.success', {
deviceName: this.deviceName,
imageName: this.imageName,
})
}, { root: true });
this.close();
buttonCb(true);
} else {
this.errors = exceptionToErrorsArray(res);
buttonCb(false);
}
} catch (err) {
this.errors = exceptionToErrorsArray(err);
buttonCb(false);
}
}
}
};
</script>
<template>
<Card
ref="modal"
name="modal"
:show-highlight-border="false"
>
<template #title>
<h4
v-clean-html="t('harvester.modal.insertCdRomVolume.title')"
class="text-default-text"
/>
</template>
<template #body>
<LabeledInput
v-model:value="deviceName"
:label="t('generic.name')"
disabled
/>
<LabeledSelect
v-model:value="imageName"
class="mt-20"
:label="t('harvester.modal.insertCdRomVolume.image')"
:options="imagesOption"
required
/>
<Banner
v-for="(err, i) in errors"
:key="i"
:label="err"
color="error"
/>
</template>
<template #actions>
<div class="actions">
<div class="buttons">
<button
type="button"
class="btn role-secondary mr-10"
@click="close"
>
{{ t('generic.cancel') }}
</button>
<AsyncButton
mode="apply"
:disabled="!isFormValid"
@click="save"
/>
</div>
</div>
</template>
</Card>
</template>
<style lang="scss" scoped>
.actions {
width: 100%;
}
.buttons {
display: flex;
justify-content: flex-end;
width: 100%;
}
</style>

View File

@ -1,239 +0,0 @@
<script>
import { mapGetters } from 'vuex';
import { PVC } from '@shell/config/types';
import { exceptionToErrorsArray } from '@shell/utils/error';
import { sortBy } from '@shell/utils/sort';
import { HCI } from '../types';
import { parseVolumeClaimTemplates } from '@pkg/harvester/utils/vm';
import { Card } from '@components/Card';
import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton';
import LabeledSelect from '@shell/components/form/LabeledSelect';
export default {
name: 'HarvesterStorageMigrationDialog',
emits: ['close'],
components: {
AsyncButton, Banner, Card, LabeledSelect
},
props: {
resources: {
type: Array,
required: true
}
},
async fetch() {
this.allPVCs = await this.$store.dispatch('harvester/findAll', { type: PVC });
},
data() {
return {
sourceVolume: '',
targetVolume: '',
errors: [],
allPVCs: [],
};
},
computed: {
...mapGetters({ t: 'i18n/t' }),
actionResource() {
return this.resources[0];
},
sourceVolumeOptions() {
const volumes = this.actionResource.spec?.template?.spec?.volumes || [];
return sortBy(
volumes
.map((v) => v.persistentVolumeClaim?.claimName)
.filter((name) => !!name)
.map((name) => ({
label: name,
value: name
})),
'label'
);
},
namespacePVCs() {
return this.allPVCs.filter((pvc) => pvc.metadata.namespace === this.actionResource.metadata.namespace);
},
vmUsedVolumeNames() {
const allVMs = this.$store.getters['harvester/all'](HCI.VM) || [];
const names = new Set();
allVMs.forEach((vm) => {
// Collect volume names from spec.template.spec.volumes (both PVC and DataVolume references)
const volumes = vm.spec?.template?.spec?.volumes || [];
volumes.forEach((v) => {
const name = v.persistentVolumeClaim?.claimName || v.dataVolume?.name;
if (name) {
names.add(`${ vm.metadata.namespace }/${ name }`);
}
});
// Collect volume names from volumeClaimTemplates annotation
const templates = parseVolumeClaimTemplates(vm);
templates.forEach((t) => {
if (t.metadata?.name) {
names.add(`${ vm.metadata.namespace }/${ t.metadata.name }`);
}
});
});
return names;
},
targetVolumeOptions() {
return sortBy(
this.namespacePVCs
.filter((pvc) => {
// Exclude volumes used by any VM (via spec.volumes or volumeClaimTemplates)
if (this.vmUsedVolumeNames.has(`${ pvc.metadata.namespace }/${ pvc.metadata.name }`)) {
return false;
}
return true;
})
.map((pvc) => ({
label: pvc.metadata.name,
value: pvc.metadata.name
})),
'label'
);
},
disableSave() {
return !this.sourceVolume || !this.targetVolume;
},
},
methods: {
close() {
this.sourceVolume = '';
this.targetVolume = '';
this.errors = [];
this.$emit('close');
},
async apply(buttonDone) {
if (!this.actionResource) {
buttonDone(false);
return;
}
if (!this.sourceVolume) {
const name = this.t('harvester.modal.storageMigration.fields.sourceVolume.label');
this['errors'] = [this.t('validation.required', { key: name })];
buttonDone(false);
return;
}
if (!this.targetVolume) {
const name = this.t('harvester.modal.storageMigration.fields.targetVolume.label');
this['errors'] = [this.t('validation.required', { key: name })];
buttonDone(false);
return;
}
try {
await this.actionResource.doAction('storageMigration', {
sourceVolume: this.sourceVolume,
targetVolume: this.targetVolume
}, {}, false);
buttonDone(true);
this.close();
} catch (err) {
const error = err?.data || err;
this['errors'] = exceptionToErrorsArray(error);
buttonDone(false);
}
},
}
};
</script>
<template>
<Card :show-highlight-border="false">
<template #title>
{{ t('harvester.modal.storageMigration.title') }}
</template>
<template #body>
<LabeledSelect
v-model:value="sourceVolume"
:label="t('harvester.modal.storageMigration.fields.sourceVolume.label')"
:placeholder="t('harvester.modal.storageMigration.fields.sourceVolume.placeholder')"
:options="sourceVolumeOptions"
class="mb-20"
required
/>
<LabeledSelect
v-model:value="targetVolume"
:label="t('harvester.modal.storageMigration.fields.targetVolume.label')"
:placeholder="t('harvester.modal.storageMigration.fields.targetVolume.placeholder')"
:options="targetVolumeOptions"
required
/>
<Banner
v-for="(err, i) in errors"
:key="i"
color="error"
:label="err"
/>
</template>
<template
#actions
class="actions"
>
<div class="buttons">
<button
class="btn role-secondary mr-10"
@click="close"
>
{{ t('generic.cancel') }}
</button>
<AsyncButton
mode="apply"
:disabled="disableSave"
@click="apply"
/>
</div>
</template>
</Card>
</template>
<style lang="scss" scoped>
.actions {
width: 100%;
}
.buttons {
display: flex;
justify-content: flex-end;
width: 100%;
}
</style>

View File

@ -277,9 +277,8 @@ export default {
:label="t('harvester.modal.bundle.namespaces.label')" :label="t('harvester.modal.bundle.namespaces.label')"
:clearable="true" :clearable="true"
:multiple="true" :multiple="true"
:append-to-body="false"
:options="namespaceOptions" :options="namespaceOptions"
class="mb-10 namespace-select" class="mb-10 label-select"
:tooltip="t('harvester.modal.bundle.namespaces.tooltip', _, true)" :tooltip="t('harvester.modal.bundle.namespaces.tooltip', _, true)"
@update:value="updateNamespaces" @update:value="updateNamespaces"
/> />
@ -373,11 +372,6 @@ export default {
padding: 10px 0; padding: 10px 0;
height: 160px; height: 160px;
} }
.namespace-select {
:deep(.vs__dropdown-menu) {
max-height: 210px;
}
}
} }
div { div {

View File

@ -1,5 +1,4 @@
<script> <script>
import { mapGetters } from 'vuex';
import { RadioGroup } from '@components/Form/Radio'; import { RadioGroup } from '@components/Form/Radio';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import { LabeledInput } from '@components/Form/LabeledInput'; import { LabeledInput } from '@components/Form/LabeledInput';
@ -17,7 +16,6 @@ import { sortBy } from '@shell/utils/sort';
import { BACKUP_TYPE } from '../config/types'; import { BACKUP_TYPE } from '../config/types';
import { _EDIT, _CREATE } from '@shell/config/query-params'; import { _EDIT, _CREATE } from '@shell/config/query-params';
import { isBackupTargetSettingEmpty, isBackupTargetSettingUnavailable } from '../utils/setting'; import { isBackupTargetSettingEmpty, isBackupTargetSettingUnavailable } from '../utils/setting';
import CronExpressionEditorModal from '@shell/components/Cron/CronExpressionEditorModal.vue';
export default { export default {
name: 'CreateVMSchedule', name: 'CreateVMSchedule',
@ -30,7 +28,6 @@ export default {
LabeledSelect, LabeledSelect,
MessageLink, MessageLink,
Banner, Banner,
CronExpressionEditorModal
}, },
mixins: [CreateEditView], mixins: [CreateEditView],
@ -89,12 +86,10 @@ export default {
} }
} }
return { settings: [], showModel: false }; return { settings: [] };
}, },
computed: { computed: {
...mapGetters({ t: 'i18n/t' }),
backupTargetResource() { backupTargetResource() {
return this.settings.find( (O) => O.id === 'backup-target'); return this.settings.find( (O) => O.id === 'backup-target');
}, },
@ -177,9 +172,6 @@ export default {
this.value.spec['maxFailure'] = this.value.spec.retain; this.value.spec['maxFailure'] = this.value.spec.retain;
} }
}, },
openModal() {
this.showModel = true;
},
}, },
}; };
</script> </script>
@ -264,28 +256,16 @@ export default {
:weight="99" :weight="99"
class="bordered-table" class="bordered-table"
> >
<div class="cronEditor"> <LabeledInput
<LabeledInput v-model:value="value.spec.cron"
v-model:value="value.spec.cron" class="mb-30"
class="mb-30" type="cron"
type="cron" required
required :mode="mode"
:mode="mode" :label="t('harvester.schedule.cron')"
:label="t('harvester.schedule.cron.label')" placeholder="0 * * * *"
placeholder="0 * * * *" :disabled="isBackupTargetUnAvailable || isView"
:disabled="isBackupTargetUnAvailable || isView" />
/>
<button
class="editCronBtn btn role-primary"
@click="openModal"
>
{{ t('harvester.schedule.cron.editButton') }}
</button>
<CronExpressionEditorModal
v-model:show="showModel"
v-model:cron-expression="value.spec.cron"
/>
</div>
<LabeledInput <LabeledInput
v-model:value.number="value.spec.retain" v-model:value.number="value.spec.retain"
class="mb-30" class="mb-30"
@ -312,16 +292,3 @@ export default {
</Tabbed> </Tabbed>
</CruResource> </CruResource>
</template> </template>
<style lang="scss" scoped>
.cronEditor {
align-items: center;
display: flex;
}
.editCronBtn {
margin-bottom: 30px;
margin-left: 10px;
height: 60px;
}
</style>

View File

@ -6,7 +6,6 @@ import { Checkbox } from '@components/Form/Checkbox';
import CruResource from '@shell/components/CruResource'; import CruResource from '@shell/components/CruResource';
import NameNsDescription from '@shell/components/form/NameNsDescription'; import NameNsDescription from '@shell/components/form/NameNsDescription';
import LabeledSelect from '@shell/components/form/LabeledSelect'; import LabeledSelect from '@shell/components/form/LabeledSelect';
import { set } from '@shell/utils/object';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import KeyValue from '@shell/components/form/KeyValue'; import KeyValue from '@shell/components/form/KeyValue';
import NodeScheduling from '@shell/components/form/NodeScheduling'; import NodeScheduling from '@shell/components/form/NodeScheduling';
@ -23,7 +22,6 @@ import Reserved from './kubevirt.io.virtualmachine/VirtualMachineReserved';
import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume'; import Volume from './kubevirt.io.virtualmachine/VirtualMachineVolume';
import Network from './kubevirt.io.virtualmachine/VirtualMachineNetwork'; import Network from './kubevirt.io.virtualmachine/VirtualMachineNetwork';
import CpuMemory from './kubevirt.io.virtualmachine/VirtualMachineCpuMemory'; import CpuMemory from './kubevirt.io.virtualmachine/VirtualMachineCpuMemory';
import CpuModel from './kubevirt.io.virtualmachine/VirtualMachineCpuModel';
import CloudConfig from './kubevirt.io.virtualmachine/VirtualMachineCloudConfig'; import CloudConfig from './kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey'; import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey';
@ -40,7 +38,6 @@ export default {
Network, Network,
Checkbox, Checkbox,
CpuMemory, CpuMemory,
CpuModel,
CruResource, CruResource,
CloudConfig, CloudConfig,
LabeledSelect, LabeledSelect,
@ -73,12 +70,12 @@ export default {
return { return {
templateId, templateId,
templateValue: null, templateValue: null,
templateSpec: null, templateSpec: null,
versionName: '', versionName: '',
description: '', description: '',
defaultVersion: null, defaultVersion: null,
isDefaultVersion: false, isDefaultVersion: false,
}; };
}, },
@ -157,18 +154,6 @@ export default {
}, },
methods: { methods: {
updateCpuModel(value) {
if (!this.spec?.template?.spec?.domain?.cpu) {
set(this.spec, 'template.spec.domain.cpu', {});
}
if (value && value !== '') {
set(this.spec.template.spec.domain.cpu, 'model', value);
} else {
delete this.spec.template.spec.domain.cpu.model;
}
},
async saveVMT(buttonCb) { async saveVMT(buttonCb) {
this.parseVM(); this.parseVM();
@ -451,17 +436,6 @@ export default {
/> />
</div> </div>
</div> </div>
<div class="row mb-20">
<div class="col span-6">
<CpuModel
:value="spec.template.spec.domain.cpu?.model || ''"
:mode="mode"
@update:value="updateCpuModel"
/>
</div>
</div>
<div class="row mb-20"> <div class="row mb-20">
<a <a
v-if="showAdvanced" v-if="showAdvanced"

View File

@ -1,133 +0,0 @@
<script>
import YAML from 'yaml';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { CONFIG_MAP } from '@shell/config/types';
import { Banner } from '@components/Banner';
const CPU_MODEL_CONFIG_MAP_ID = 'harvester-system/node-cpu-model-configuration';
export default {
name: 'HarvesterCpuModel',
emits: ['update:value'],
components: {
LabeledSelect,
Banner
},
props: {
value: {
type: String,
default: ''
},
mode: {
type: String,
default: 'create',
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
try {
await this.$store.dispatch(`${ inStore }/find`, { type: CONFIG_MAP, id: CPU_MODEL_CONFIG_MAP_ID });
this.fetchError = null;
} catch (e) {
this.fetchError = this.t('harvester.virtualMachine.cpuModel.fetchError', { error: e.message || e });
}
},
data() {
return { fetchError: null };
},
computed: {
localValue: {
get() {
return this.value ?? '';
},
set(val) {
this.$emit('update:value', val ?? '');
}
},
cpuModelConfigMap() {
const inStore = this.$store.getters['currentProduct'].inStore;
return this.$store.getters[`${ inStore }/byId`](
CONFIG_MAP,
CPU_MODEL_CONFIG_MAP_ID
);
},
cpuModelOptions() {
if (!this.cpuModelConfigMap?.data?.cpuModels) {
return [{ label: this.t('generic.default'), value: '' }];
}
let cpuModelsData;
try {
cpuModelsData = YAML.parse(this.cpuModelConfigMap.data?.cpuModels || '');
} catch (e) {
return [{ label: this.t('generic.default'), value: '' }];
}
const options = [];
options.push({
label: this.t('generic.default'),
value: ''
});
// Add global models (host-model, host-passthrough)
const globalModels = cpuModelsData.globalModels || [];
globalModels.forEach((modelName) => {
options.push({
label: modelName,
value: modelName
});
});
// Add regular models with node count
const modelEntries = Object.entries(cpuModelsData.models || {});
// Sort models alphabetically for consistent display
modelEntries.sort((a, b) => a[0].localeCompare(b[0]));
modelEntries.forEach(([modelName, modelInfo]) => {
const readyCount = modelInfo.readyCount || 0;
const label = this.t('harvester.virtualMachine.cpuModel.optionLabel', { modelName, count: readyCount });
options.push({
label,
value: modelName
});
});
return options;
},
},
};
</script>
<template>
<div>
<Banner
v-if="fetchError"
color="error"
class="mb-20"
>
{{ fetchError }}
</Banner>
<LabeledSelect
v-model:value="localValue"
:label="t('harvester.virtualMachine.cpuModel.label')"
:options="cpuModelOptions"
:mode="mode"
:disabled="!!fetchError"
/>
</div>
</template>

View File

@ -31,7 +31,6 @@ export default {
const _hash = { const _hash = {
pciclaims: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.PCI_CLAIM }), pciclaims: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.PCI_CLAIM }),
sriovs: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SR_IOV }), sriovs: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SR_IOV }),
srigpuovs: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SR_IOVGPU_DEVICE }),
}; };
await allHash(_hash); await allHash(_hash);
@ -107,32 +106,19 @@ export default {
}, },
computed: { computed: {
allSriovs() {
const inStore = this.$store.getters['currentProduct'].inStore;
return this.$store.getters[`${ inStore }/all`](HCI.SR_IOV) || [];
},
allSriovGPUs() {
const inStore = this.$store.getters['currentProduct'].inStore;
return this.$store.getters[`${ inStore }/all`](HCI.SR_IOVGPU_DEVICE) || [];
},
parentSriovOptions() { parentSriovOptions() {
return this.allSriovs.map((sriov) => sriov.id); const inStore = this.$store.getters['currentProduct'].inStore;
}, const allSriovs = this.$store.getters[`${ inStore }/all`](HCI.SR_IOV) || [];
parentSriovGPUOptions() {
return this.allSriovGPUs.map((sriovgpu) => sriovgpu.id); return allSriovs.map((sriov) => {
return sriov.id;
});
}, },
parentSriovLabel() { parentSriovLabel() {
return HCI_ANNOTATIONS.PARENT_SRIOV; return HCI_ANNOTATIONS.PARENT_SRIOV;
}, }
parentSriovGPULabel() {
return HCI_ANNOTATIONS.PARENT_SRIOV_GPU;
},
vGPUAsPCIDeviceEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('vGPUAsPCIDevice');
},
}, },
methods: { methods: {
enableGroup(rows = []) { enableGroup(rows = []) {
const row = rows[0]; const row = rows[0];
@ -220,15 +206,6 @@ export default {
:rows="rows" :rows="rows"
@change-rows="changeRows" @change-rows="changeRows"
/> />
<FilterBySriov
v-if="vGPUAsPCIDeviceEnabled"
ref="filterByParentSRIOVGPU"
:parent-sriov-options="parentSriovGPUOptions"
:parent-sriov-label="parentSriovGPULabel"
:label="t('harvester.sriov.parentSriovGPU')"
:rows="rows"
@change-rows="changeRows"
/>
</template> </template>
</ResourceTable> </ResourceTable>
</template> </template>

View File

@ -8,7 +8,6 @@ import { set } from '@shell/utils/object';
import { HCI } from '../../../types'; import { HCI } from '../../../types';
import DeviceList from './DeviceList'; import DeviceList from './DeviceList';
import CompatibilityMatrix from '../CompatibilityMatrix'; import CompatibilityMatrix from '../CompatibilityMatrix';
import MessageLink from '@shell/components/MessageLink';
export default { export default {
name: 'VirtualMachinePCIDevices', name: 'VirtualMachinePCIDevices',
@ -16,8 +15,7 @@ export default {
LabeledSelect, LabeledSelect,
DeviceList, DeviceList,
CompatibilityMatrix, CompatibilityMatrix,
Banner, Banner
MessageLink
}, },
props: { props: {
mode: { mode: {
@ -56,11 +54,6 @@ export default {
const vmDevices = this.value?.domain?.devices?.hostDevices || []; const vmDevices = this.value?.domain?.devices?.hostDevices || [];
const otherDevices = this.otherDevices(vmDevices).map(({ name }) => name); const otherDevices = this.otherDevices(vmDevices).map(({ name }) => name);
const vmDeviceNames = vmDevices.map(({ name }) => name);
this.pciDevices.forEach((row) => {
row.allowDisable = !vmDeviceNames.includes(row.metadata.name);
});
vmDevices.forEach(({ name, deviceName }) => { vmDevices.forEach(({ name, deviceName }) => {
const checkName = (deviceName || '').split('/')?.[1]; const checkName = (deviceName || '').split('/')?.[1];
@ -140,13 +133,6 @@ export default {
return inUse; return inUse;
}, },
toVGpuDevicesPage() {
return {
name: 'harvester-c-cluster-resource',
params: { cluster: this.$store.getters['clusterId'], resource: HCI.VGPU_DEVICE },
};
},
devicesByNode() { devicesByNode() {
return this.enabledDevices?.reduce((acc, device) => { return this.enabledDevices?.reduce((acc, device) => {
const nodeName = device.status?.nodeName; const nodeName = device.status?.nodeName;
@ -241,12 +227,7 @@ export default {
<div class="row"> <div class="row">
<div class="col span-12"> <div class="col span-12">
<Banner color="info"> <Banner color="info">
<MessageLink <t k="harvester.pci.howToUseDevice" />
:to="toVGpuDevicesPage"
prefix-label="harvester.pci.howToUseDevice.prefix"
middle-label="harvester.pci.howToUseDevice.middle"
suffix-label="harvester.pci.howToUseDevice.suffix"
/>
</Banner> </Banner>
<Banner <Banner
v-if="selectedDevices.length > 0" v-if="selectedDevices.length > 0"

View File

@ -48,13 +48,6 @@ export default {
this[key] = res[key]; this[key] = res[key];
} }
const vmDevices = this.value?.domain?.devices?.hostDevices || [];
const vmDeviceNames = vmDevices.map(({ name }) => name);
this.devices.forEach((row) => {
row.allowDisable = !vmDeviceNames.includes(row.metadata.name);
});
this.selectedDevices = (this.value?.domain?.devices?.hostDevices || []) this.selectedDevices = (this.value?.domain?.devices?.hostDevices || [])
.map(({ name }) => name) .map(({ name }) => name)
.filter((name) => this.enabledDevices.find((device) => device?.metadata?.name === name)); .filter((name) => this.enabledDevices.find((device) => device?.metadata?.name === name));

View File

@ -46,13 +46,6 @@ export default {
this[key] = res[key]; this[key] = res[key];
} }
const vmDevices = this.value?.domain?.devices?.gpus || [];
const vmDeviceNames = vmDevices.map(({ name }) => name);
this.devices.forEach((row) => {
row.allowDisable = !vmDeviceNames.includes(row.metadata.name);
});
const vGpus = this.vm.isOff ? [ const vGpus = this.vm.isOff ? [
...(this.value?.domain?.devices?.gpus || []).map(({ name }) => name), ...(this.value?.domain?.devices?.gpus || []).map(({ name }) => name),
] : [ ] : [

View File

@ -13,12 +13,11 @@ import { ucFirst, randomStr } from '@shell/utils/string';
import { removeObject } from '@shell/utils/array'; import { removeObject } from '@shell/utils/array';
import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params'; import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params';
import { PLUGIN_DEVELOPER, DEV } from '@shell/store/prefs'; import { PLUGIN_DEVELOPER, DEV } from '@shell/store/prefs';
import { VOLUME_HOTPLUG_ACTION, SOURCE_TYPE } from '../../../config/harvester-map'; import { SOURCE_TYPE } from '../../../config/harvester-map';
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../config/harvester'; import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../../config/harvester';
import { HCI } from '../../../types'; import { HCI } from '../../../types';
import { VOLUME_MODE } from '@pkg/harvester/config/types'; import { VOLUME_MODE } from '@pkg/harvester/config/types';
import { OFF } from '../../../models/kubevirt.io.virtualmachine'; import { OFF } from '../../../models/kubevirt.io.virtualmachine';
import { EMPTY_IMAGE } from '../../../utils/vm';
export default { export default {
emits: ['update:value'], emits: ['update:value'],
@ -118,10 +117,6 @@ export default {
return this.mode === _CREATE; return this.mode === _CREATE;
}, },
isHotplugCdRomFeatureEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('hotplugCdRom');
},
defaultStorageClass() { defaultStorageClass() {
const defaultStorage = this.$store.getters['harvester/all'](STORAGE_CLASS).find((sc) => sc.isDefault); const defaultStorage = this.$store.getters['harvester/all'](STORAGE_CLASS).find((sc) => sc.isDefault);
@ -151,7 +146,7 @@ export default {
value: { value: {
handler(neu) { handler(neu) {
const rows = clone(neu).map((V) => { const rows = clone(neu).map((V) => {
if (!this.isCreate && V.source !== SOURCE_TYPE.CONTAINER && !V.newCreateId && V.image !== EMPTY_IMAGE) { if (!this.isCreate && V.source !== SOURCE_TYPE.CONTAINER && !V.newCreateId) {
V.to = { V.to = {
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-namespace-id`, name: `${ HARVESTER_PRODUCT }-c-cluster-resource-namespace-id`,
params: { params: {
@ -222,48 +217,8 @@ export default {
} }
}, },
canDoVolumeHotplugAction(volume) { unplugVolume(volume) {
if (!this.isHotplugCdRomFeatureEnabled && volume.type === 'cd-rom') { this.vm.unplugVolume(volume.name);
return false;
}
if (volume.hotpluggable) {
return true;
}
return volume.type === 'cd-rom' && volume.bus === 'sata' && volume.image === EMPTY_IMAGE;
},
getVolumeHotplugAction(volume) {
if (volume.type === 'cd-rom' && volume.bus === 'sata') {
if (volume.image === EMPTY_IMAGE) {
return VOLUME_HOTPLUG_ACTION.INSERT_CDROM_IMAGE;
}
return VOLUME_HOTPLUG_ACTION.EJECT_CDROM_IMAGE;
}
return VOLUME_HOTPLUG_ACTION.DETACH_DISK;
},
getVolumeHotplugActionLabel(volume) {
const labels = {
[VOLUME_HOTPLUG_ACTION.DETACH_DISK]: 'harvester.virtualMachine.hotUnplug.detachVolume.actionLabel',
[VOLUME_HOTPLUG_ACTION.INSERT_CDROM_IMAGE]: 'harvester.modal.insertCdRomVolume.actionLabel',
[VOLUME_HOTPLUG_ACTION.EJECT_CDROM_IMAGE]: 'harvester.virtualMachine.hotUnplug.ejectCdRomVolume.actionLabel',
};
return labels[this.getVolumeHotplugAction(volume)];
},
hotplugVolume(volume) {
const calls = {
[VOLUME_HOTPLUG_ACTION.DETACH_DISK]: () => this.vm.unplugVolume(volume.name),
[VOLUME_HOTPLUG_ACTION.INSERT_CDROM_IMAGE]: () => this.vm.insertCdRomVolume(volume.name),
[VOLUME_HOTPLUG_ACTION.EJECT_CDROM_IMAGE]: () => this.vm.ejectCdRomVolume(volume.name),
};
return calls[this.getVolumeHotplugAction(volume)]();
}, },
componentFor(type) { componentFor(type) {
@ -391,12 +346,12 @@ export default {
<i class="icon icon-x" /> <i class="icon icon-x" />
</button> </button>
<button <button
v-if="canDoVolumeHotplugAction(volume) && isView" v-if="volume.hotpluggable && isView"
type="button" type="button"
class="role-link btn btn-sm remove" class="role-link btn btn-sm remove"
@click="hotplugVolume(volume)" @click="unplugVolume(volume)"
> >
{{ t(getVolumeHotplugActionLabel(volume)) }} {{ t('harvester.virtualMachine.hotUnplug.detachVolume.actionLabel') }}
</button> </button>
</div> </div>
<div> <div>

View File

@ -14,7 +14,6 @@ import { _VIEW } from '@shell/config/query-params';
import LabelValue from '@shell/components/LabelValue'; import LabelValue from '@shell/components/LabelValue';
import { ucFirst } from '@shell/utils/string'; import { ucFirst } from '@shell/utils/string';
import { GIBIBYTE } from '../../../../utils/unit'; import { GIBIBYTE } from '../../../../utils/unit';
import { EMPTY_IMAGE } from '../../../../utils/vm';
export default { export default {
name: 'HarvesterEditVMImage', name: 'HarvesterEditVMImage',
@ -97,20 +96,8 @@ export default {
return this.mode === _VIEW; return this.mode === _VIEW;
}, },
isExistingCdrom() {
return this.value.type === 'cd-rom' && !this.value.newCreateId;
},
isEmptyImage() {
return this.value.image === EMPTY_IMAGE;
},
isHotplugCdRomFeatureEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('hotplugCdRom');
},
imagesOption() { imagesOption() {
const images = this.images return this.images
.filter((image) => { .filter((image) => {
if (!image.isReady) return false; if (!image.isReady) return false;
@ -127,19 +114,6 @@ export default {
value: image.id, value: image.id,
disabled: image.isImportedImage disabled: image.isImportedImage
})); }));
const options = [];
if (this.isHotplugCdRomFeatureEnabled) {
options.push({
label: this.t('harvester.virtualMachine.volume.emptyImage'),
value: EMPTY_IMAGE,
disabled: false
});
}
options.push(...images);
return options;
}, },
imageName() { imageName() {
@ -205,7 +179,6 @@ export default {
'value.type'(neu) { 'value.type'(neu) {
if (neu === 'cd-rom') { if (neu === 'cd-rom') {
this.value['bus'] = 'sata'; this.value['bus'] = 'sata';
this.updateHotpluggable();
this.update(); this.update();
} }
}, },
@ -248,48 +221,12 @@ export default {
return label; return label;
}, },
update() { update() {
this.value.hasDiskError = this.showDiskTooSmallError; this.value.hasDiskError = this.showDiskTooSmallError;
this.$emit('update'); this.$emit('update');
}, },
updateHotpluggable() {
if (this.value.type !== 'cd-rom') {
this.value['hotpluggable'] = false;
} else {
this.value['hotpluggable'] = (this.value.bus === 'sata');
}
},
onTypeChange() {
if (this.value.image === EMPTY_IMAGE && this.value.type !== 'cd-rom') {
this.value['image'] = '';
}
this.updateHotpluggable();
this.update();
},
onBusChange() {
if (this.value.image === EMPTY_IMAGE && this.value.bus !== 'sata') {
this.value['image'] = '';
}
this.updateHotpluggable();
this.update();
},
onImageChange() { onImageChange() {
if (this.value.image === EMPTY_IMAGE) {
this.value['type'] = 'cd-rom';
this.value['bus'] = 'sata';
this.value['size'] = `0${ GIBIBYTE }`;
this.update();
return;
}
const imageResource = this.$store.getters['harvester/all'](HCI.IMAGE)?.find( (I) => this.value.image === I.id); const imageResource = this.$store.getters['harvester/all'](HCI.IMAGE)?.find( (I) => this.value.image === I.id);
const isIsoImage = /iso$/i.test(imageResource?.imageSuffix); const isIsoImage = /iso$/i.test(imageResource?.imageSuffix);
const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize); const imageSize = Math.max(imageResource?.status?.size, imageResource?.status?.virtualSize);
@ -302,8 +239,6 @@ export default {
this.value['bus'] = 'virtio'; this.value['bus'] = 'virtio';
} }
this.updateHotpluggable();
if (imageSize) { if (imageSize) {
let imageSizeGiB = Math.ceil(imageSize / 1024 / 1024 / 1024); let imageSizeGiB = Math.ceil(imageSize / 1024 / 1024 / 1024);
@ -321,10 +256,6 @@ export default {
}, },
checkImageExists(imageId) { checkImageExists(imageId) {
if (imageId === EMPTY_IMAGE) {
return;
}
if (!!imageId && this.imagesOption.length > 0 && !findBy(this.imagesOption, 'value', imageId)) { if (!!imageId && this.imagesOption.length > 0 && !findBy(this.imagesOption, 'value', imageId)) {
this.$store.dispatch('growl/error', { this.$store.dispatch('growl/error', {
title: this.$store.getters['i18n/t']('harvester.vmTemplate.tips.notExistImage.title', { name: imageId }), title: this.$store.getters['i18n/t']('harvester.vmTemplate.tips.notExistImage.title', { name: imageId }),
@ -352,7 +283,6 @@ export default {
> >
<LabeledInput <LabeledInput
v-model:value="value.name" v-model:value="value.name"
:disabled="!isCreate && isExistingCdrom"
:label="t('harvester.fields.name')" :label="t('harvester.fields.name')"
required required
:mode="mode" :mode="mode"
@ -372,11 +302,10 @@ export default {
> >
<LabeledSelect <LabeledSelect
v-model:value="value.type" v-model:value="value.type"
:disabled="!isCreate && isExistingCdrom"
:label="t('harvester.fields.type')" :label="t('harvester.fields.type')"
:options="VOLUME_TYPE" :options="VOLUME_TYPE"
:mode="mode" :mode="mode"
@update:value="onTypeChange" @update:value="update"
/> />
</InputOrDisplay> </InputOrDisplay>
</div> </div>
@ -394,7 +323,7 @@ export default {
> >
<LabeledSelect <LabeledSelect
v-model:value="value.image" v-model:value="value.image"
:disabled="(idx === 0 || isExistingCdrom) && (!isCreate && !value.newCreateId && isVirtualType)" :disabled="idx === 0 && !isCreate && !value.newCreateId && isVirtualType"
:label="t('harvester.fields.image')" :label="t('harvester.fields.image')"
:options="imagesOption" :options="imagesOption"
:mode="mode" :mode="mode"
@ -422,7 +351,7 @@ export default {
:label="t('harvester.fields.size')" :label="t('harvester.fields.size')"
:mode="mode" :mode="mode"
:required="validateRequired" :required="validateRequired"
:disabled="isResizeDisabled || isEmptyImage || (!isCreate && isExistingCdrom)" :disabled="isResizeDisabled"
:suffix="GIBIBYTE" :suffix="GIBIBYTE"
@update:value="update" @update:value="update"
/> />
@ -445,8 +374,7 @@ export default {
:label="t('harvester.virtualMachine.volume.bus')" :label="t('harvester.virtualMachine.volume.bus')"
:mode="mode" :mode="mode"
:options="InterfaceOption" :options="InterfaceOption"
:disabled="!isCreate && isExistingCdrom" @update:value="update"
@update:value="onBusChange"
/> />
</InputOrDisplay> </InputOrDisplay>
</div> </div>

View File

@ -2,7 +2,7 @@
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import Tabbed from '@shell/components/Tabbed'; import Tabbed from '@shell/components/Tabbed';
import { clone, set } from '@shell/utils/object'; import { clone } from '@shell/utils/object';
import Tab from '@shell/components/Tabbed/Tab'; import Tab from '@shell/components/Tabbed/Tab';
import { Checkbox } from '@components/Form/Checkbox'; import { Checkbox } from '@components/Form/Checkbox';
import CruResource from '@shell/components/CruResource'; import CruResource from '@shell/components/CruResource';
@ -32,7 +32,6 @@ import PciDevices from './VirtualMachinePciDevices/index';
import AccessCredentials from './VirtualMachineAccessCredentials'; import AccessCredentials from './VirtualMachineAccessCredentials';
import CloudConfig from './VirtualMachineCloudConfig'; import CloudConfig from './VirtualMachineCloudConfig';
import CpuMemory from './VirtualMachineCpuMemory'; import CpuMemory from './VirtualMachineCpuMemory';
import CpuModel from './VirtualMachineCpuModel';
import Network from './VirtualMachineNetwork'; import Network from './VirtualMachineNetwork';
import Volume from './VirtualMachineVolume'; import Volume from './VirtualMachineVolume';
import SSHKey from './VirtualMachineSSHKey'; import SSHKey from './VirtualMachineSSHKey';
@ -58,7 +57,6 @@ export default {
SSHKey, SSHKey,
Network, Network,
CpuMemory, CpuMemory,
CpuModel,
CloudConfig, CloudConfig,
NodeScheduling, NodeScheduling,
PodAffinity, PodAffinity,
@ -211,10 +209,6 @@ export default {
return false; return false;
}, },
vGPUAsPCIDeviceEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('vGPUAsPCIDevice');
},
usbPassthroughEnabled() { usbPassthroughEnabled() {
return this.$store.getters['harvester-common/getFeatureEnabled']('usbPassthrough'); return this.$store.getters['harvester-common/getFeatureEnabled']('usbPassthrough');
}, },
@ -544,18 +538,6 @@ export default {
return out; return out;
}, },
updateCpuModel(value) {
if (!this.spec?.template?.spec?.domain?.cpu) {
set(this.spec, 'template.spec.domain.cpu', {});
}
if (value && value !== '') {
set(this.spec.template.spec.domain.cpu, 'model', value);
} else {
delete this.spec.template.spec.domain.cpu.model;
}
},
}, },
}; };
</script> </script>
@ -744,7 +726,7 @@ export default {
</Tab> </Tab>
<Tab <Tab
v-if="enabledSriovgpu && !vGPUAsPCIDeviceEnabled" v-if="enabledSriovgpu"
:label="t('harvester.tab.vGpuDevices')" :label="t('harvester.tab.vGpuDevices')"
name="vGpuDevices" name="vGpuDevices"
:weight="-6" :weight="-6"
@ -888,16 +870,6 @@ export default {
</div> </div>
</div> </div>
<div class="row mb-20">
<div class="col span-6">
<CpuModel
v-model:value="cpuModel"
:mode="mode"
@update:value="updateCpuModel"
/>
</div>
</div>
<div class="row mb-20"> <div class="row mb-20">
<a <a
v-if="showAdvanced" v-if="showAdvanced"

View File

@ -1,357 +0,0 @@
<script>
import CruResource from '@shell/components/CruResource';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import NameNsDescription from '@shell/components/form/NameNsDescription';
import { RadioGroup } from '@components/Form/Radio';
import UnitInput from '@shell/components/form/UnitInput';
import CreateEditView from '@shell/mixins/create-edit-view';
import FormValidation from '@shell/mixins/form-validation';
import { SECRET } from '@shell/config/types';
import { randomStr } from '@shell/utils/string';
import { mapGetters } from 'vuex';
export default {
name: 'EditOpenstackSource',
emits: ['update:value'],
components: {
CruResource,
Tabbed,
Tab,
LabeledInput,
LabeledSelect,
NameNsDescription,
RadioGroup,
UnitInput
},
mixins: [CreateEditView, FormValidation],
inheritAttrs: false,
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true,
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.allSecrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET });
},
data() {
if (!this.value.spec) this.value.spec = {};
if (!this.value.spec.credentials) this.value.spec.credentials = {};
const initialMode = this.value.spec.credentials.name ? 'existing' : 'new';
return {
allSecrets: [],
authMode: initialMode,
newUsername: '',
newPassword: '',
newProjectName: '',
newDomainName: '',
newCaCert: '',
// Rules for fields that exist in the value object (Model)
fvFormRuleSets: [
{ path: 'metadata.name', rules: ['nameRequired'] },
{ path: 'spec.endpoint', rules: ['endpointRequired'] },
{ path: 'spec.region', rules: ['regionRequired'] },
],
};
},
computed: {
...mapGetters({ t: 'i18n/t' }),
authModeOptions() {
return [
{ label: this.t('harvester.addons.vmImport.fields.createSecret'), value: 'new' },
{ label: this.t('harvester.addons.vmImport.fields.useSecret'), value: 'existing' }
];
},
secretOptions() {
const currentNamespace = this.value.metadata.namespace || 'default';
return this.allSecrets
.filter((s) => s.metadata.namespace === currentNamespace)
.map((s) => ({
label: s.nameDisplay,
value: s.metadata.name
}));
},
// Define custom rules for the FormValidation mixin
fvExtraRules() {
return {
nameRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.fields.name') }) : undefined,
endpointRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.openstack.fields.endpoint') }) : undefined,
regionRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.openstack.fields.region') }) : undefined,
};
},
// Combine mixin validation + conditional manual checks
isFormValid() {
// Check static fields via Mixin
if (!this.fvFormIsValid) {
return false;
}
// Check conditional fields
if (this.authMode === 'new') {
if (!this.newUsername || !this.newPassword) return false;
if (!this.newProjectName || !this.newDomainName) return false;
} else {
if (!this.value.spec.credentials.name) return false;
}
return true;
}
},
methods: {
usernameRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.username') }) : undefined;
},
passwordRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.password') }) : undefined;
},
projectRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.openstack.fields.projectName') }) : undefined;
},
domainRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.openstack.fields.domainName') }) : undefined;
},
secretRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.selectSecret') }) : undefined;
},
async saveSource(buttonCb) {
const inStore = this.$store.getters['currentProduct'].inStore;
try {
if (this.authMode === 'new') {
const secretName = `${ this.value.metadata.name }-creds-${ randomStr(4).toLowerCase() }`;
const namespace = this.value.metadata.namespace || 'default';
const newSecret = await this.$store.dispatch(`${ inStore }/create`, {
type: SECRET,
metadata: {
name: secretName,
namespace
}
});
newSecret['_type'] = 'Opaque';
newSecret['data'] = {
username: btoa(this.newUsername),
password: btoa(this.newPassword),
project_name: btoa(this.newProjectName),
domain_name: btoa(this.newDomainName),
ca_cert: this.newCaCert ? btoa(this.newCaCert) : undefined
};
await newSecret.save();
this.value.spec.credentials = {
name: secretName,
namespace
};
}
await this.save(buttonCb);
} catch (err) {
this.errors = [err];
buttonCb(false);
}
}
}
};
</script>
<template>
<CruResource
:done-route="doneRoute"
:resource="value"
:mode="mode"
:errors="errors"
:apply-hooks="applyHooks"
:validation-passed="isFormValid"
@finish="saveSource"
@error="e=>errors=e"
>
<NameNsDescription
:value="value"
:mode="mode"
:rules="{ name: fvGetAndReportPathRules('metadata.name') }"
@update:value="$emit('update:value', $event)"
/>
<Tabbed
v-bind="$attrs"
class="mt-15"
:side-tabs="true"
>
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="3"
>
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="value.spec.endpoint"
:label="t('harvester.addons.vmImport.openstack.fields.endpoint')"
:placeholder="t('harvester.addons.vmImport.openstack.placeholders.endpoint')"
:mode="mode"
:rules="fvGetAndReportPathRules('spec.endpoint')"
required
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="value.spec.region"
:label="t('harvester.addons.vmImport.openstack.fields.region')"
:placeholder="t('harvester.addons.vmImport.openstack.placeholders.region')"
:mode="mode"
:rules="fvGetAndReportPathRules('spec.region')"
required
/>
</div>
</div>
</Tab>
<Tab
name="auth"
:label="t('harvester.addons.vmImport.titles.auth')"
:weight="2"
>
<div class="row mb-20">
<div class="col span-12">
<RadioGroup
v-model:value="authMode"
name="authMode"
:options="authModeOptions"
:mode="mode"
/>
</div>
</div>
<div v-if="authMode === 'new'">
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="newUsername"
:label="t('harvester.addons.vmImport.fields.username')"
:mode="mode"
:rules="[usernameRule]"
required
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="newPassword"
type="password"
:label="t('harvester.addons.vmImport.fields.password')"
:mode="mode"
:rules="[passwordRule]"
required
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="newProjectName"
:label="t('harvester.addons.vmImport.openstack.fields.projectName')"
:placeholder="t('harvester.addons.vmImport.openstack.placeholders.projectName')"
:mode="mode"
:rules="[projectRule]"
required
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="newDomainName"
:label="t('harvester.addons.vmImport.openstack.fields.domainName')"
:placeholder="t('harvester.addons.vmImport.openstack.placeholders.domainName')"
:mode="mode"
:rules="[domainRule]"
required
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput
v-model:value="newCaCert"
type="multiline"
:label="t('harvester.addons.vmImport.fields.caCert')"
:placeholder="t('harvester.addons.vmImport.placeholders.caCert')"
:min-height="100"
:mode="mode"
/>
</div>
</div>
</div>
<div v-if="authMode === 'existing'">
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect
v-model:value="value.spec.credentials.name"
:options="secretOptions"
:label="t('harvester.addons.vmImport.fields.selectSecret')"
:mode="mode"
:rules="[secretRule]"
required
/>
</div>
</div>
</div>
</Tab>
<Tab
name="advanced"
:label="t('harvester.addons.vmImport.titles.advanced')"
:weight="1"
>
<div class="row mb-20">
<div class="col span-6">
<UnitInput
v-model:value="value.spec.uploadImageRetryCount"
:label="t('harvester.addons.vmImport.openstack.fields.retryCount')"
:placeholder="t('harvester.addons.vmImport.openstack.placeholders.retryCount')"
suffix="Times"
:mode="mode"
/>
</div>
<div class="col span-6">
<UnitInput
v-model:value="value.spec.uploadImageRetryDelay"
:label="t('harvester.addons.vmImport.openstack.fields.retryDelay')"
:placeholder="t('harvester.addons.vmImport.openstack.placeholders.retryDelay')"
suffix="Seconds"
:mode="mode"
/>
</div>
</div>
</Tab>
</Tabbed>
</CruResource>
</template>

View File

@ -1,322 +0,0 @@
<script>
import CruResource from '@shell/components/CruResource';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import NameNsDescription from '@shell/components/form/NameNsDescription';
import { RadioGroup } from '@components/Form/Radio';
import UnitInput from '@shell/components/form/UnitInput';
import CreateEditView from '@shell/mixins/create-edit-view';
import FormValidation from '@shell/mixins/form-validation';
import { SECRET } from '@shell/config/types';
import { randomStr } from '@shell/utils/string';
import { mapGetters } from 'vuex';
export default {
name: 'EditOvaSource',
emits: ['update:value'],
components: {
CruResource,
Tabbed,
Tab,
LabeledInput,
LabeledSelect,
NameNsDescription,
RadioGroup,
UnitInput
},
mixins: [CreateEditView, FormValidation],
inheritAttrs: false,
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true,
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.allSecrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET });
},
data() {
if (!this.value.spec) this.value.spec = {};
// Auth is optional for OVA (public URLs).
// If credentials.name exists -> Existing.
// If not -> None (default).
let initialMode = 'none';
if (this.value.spec.credentials?.name) {
initialMode = 'existing';
}
return {
allSecrets: [],
authMode: initialMode,
newUsername: '',
newPassword: '',
newCaCert: '', // Key will be "ca.crt"
// Validation Rules for static fields
fvFormRuleSets: [
{ path: 'metadata.name', rules: ['nameRequired'] },
{ path: 'spec.url', rules: ['urlRequired'] },
],
};
},
computed: {
...mapGetters({ t: 'i18n/t' }),
authModeOptions() {
return [
{ label: this.t('harvester.addons.vmImport.fields.none'), value: 'none' },
{ label: this.t('harvester.addons.vmImport.fields.createSecret'), value: 'new' },
{ label: this.t('harvester.addons.vmImport.fields.useSecret'), value: 'existing' }
];
},
secretOptions() {
const currentNamespace = this.value.metadata.namespace || 'default';
return this.allSecrets
.filter((s) => s.metadata.namespace === currentNamespace)
.map((s) => ({
label: s.nameDisplay,
value: s.metadata.name
}));
},
// Define custom rules for the FormValidation mixin
fvExtraRules() {
return {
nameRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.fields.name') }) : undefined,
urlRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.ova.fields.url') }) : undefined,
};
},
isFormValid() {
if (!this.fvFormIsValid) {
return false;
}
if (this.authMode === 'new') {
// At least a username/password OR a CA cert to be provided.
// If the user selected "Create New", they likely intend to enter something.
if (!this.newUsername && !this.newPassword && !this.newCaCert) return false;
} else if (this.authMode === 'existing') {
if (!this.value.spec.credentials?.name) return false;
}
return true;
}
},
watch: {
authMode(newMode) {
if (newMode === 'existing') {
// Bind to value.spec.credentials.name for existing credential
// Ensure 'credentials' object exists first when selected
if (!this.value.spec.credentials) {
this.value.spec.credentials = {
name: '',
namespace: this.value.metadata.namespace || 'default'
};
}
}
}
},
methods: {
secretRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.selectSecret') }) : undefined;
},
async saveSource(buttonCb) {
const inStore = this.$store.getters['currentProduct'].inStore;
try {
if (this.authMode === 'none') {
// Clear any credential reference
delete this.value.spec.credentials;
} else if (this.authMode === 'new') {
const secretName = `${ this.value.metadata.name }-creds-${ randomStr(4).toLowerCase() }`;
const namespace = this.value.metadata.namespace || 'default';
const newSecret = await this.$store.dispatch(`${ inStore }/create`, {
type: SECRET,
metadata: {
name: secretName,
namespace
}
});
newSecret['_type'] = 'Opaque';
newSecret['data'] = {
// Optional fields logic
username: this.newUsername ? btoa(this.newUsername) : undefined,
password: this.newPassword ? btoa(this.newPassword) : undefined,
// vm-import-controller code specifies "ca.crt" with a dot.
'ca.crt': this.newCaCert ? btoa(this.newCaCert) : undefined
};
await newSecret.save();
this.value.spec.credentials = {
name: secretName,
namespace
};
}
await this.save(buttonCb);
} catch (err) {
this.errors = [err];
buttonCb(false);
}
}
}
};
</script>
<template>
<CruResource
:done-route="doneRoute"
:resource="value"
:mode="mode"
:errors="errors"
:apply-hooks="applyHooks"
:validation-passed="isFormValid"
@finish="saveSource"
@error="e=>errors=e"
>
<NameNsDescription
:value="value"
:mode="mode"
:rules="{ name: fvGetAndReportPathRules('metadata.name') }"
@update:value="$emit('update:value', $event)"
/>
<Tabbed
v-bind="$attrs"
class="mt-15"
:side-tabs="true"
>
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="3"
>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput
v-model:value="value.spec.url"
:label="t('harvester.addons.vmImport.ova.fields.url')"
:placeholder="t('harvester.addons.vmImport.ova.placeholders.url')"
tooltip="Supports HTTP and HTTPS protocols."
:mode="mode"
:rules="fvGetAndReportPathRules('spec.url')"
required
/>
</div>
</div>
</Tab>
<Tab
name="auth"
:label="t('harvester.addons.vmImport.titles.auth')"
:weight="2"
>
<div class="row mb-20">
<div class="col span-12">
<RadioGroup
v-model:value="authMode"
name="authMode"
:options="authModeOptions"
:mode="mode"
/>
</div>
</div>
<div v-if="authMode === 'new'">
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="newUsername"
:label="t('harvester.addons.vmImport.fields.username')"
placeholder="(Optional)"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="newPassword"
type="password"
:label="t('harvester.addons.vmImport.fields.password')"
placeholder="(Optional)"
:mode="mode"
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput
v-model:value="newCaCert"
type="multiline"
:label="t('harvester.addons.vmImport.fields.caCert')"
:placeholder="t('harvester.addons.vmImport.placeholders.caCert')"
:min-height="100"
:mode="mode"
/>
</div>
</div>
</div>
<div v-if="authMode === 'existing'">
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect
v-model:value="value.spec.credentials.name"
:options="secretOptions"
:label="t('harvester.addons.vmImport.fields.selectSecret')"
:mode="mode"
:rules="[secretRule]"
required
/>
</div>
</div>
</div>
</Tab>
<Tab
name="advanced"
:label="t('harvester.addons.vmImport.titles.advanced')"
:weight="1"
>
<div class="row mb-20">
<div class="col span-12">
<UnitInput
v-model:value="value.spec.httpTimeoutSeconds"
:label="t('harvester.addons.vmImport.ova.fields.httpTimeout')"
:placeholder="t('harvester.addons.vmImport.ova.placeholders.httpTimeout')"
suffix="Seconds"
:mode="mode"
/>
</div>
</div>
</Tab>
</Tabbed>
</CruResource>
</template>

View File

@ -1,570 +0,0 @@
<script>
import CruResource from '@shell/components/CruResource';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import NameNsDescription from '@shell/components/form/NameNsDescription';
import { Checkbox } from '@components/Form/Checkbox';
import CreateEditView from '@shell/mixins/create-edit-view';
import FormValidation from '@shell/mixins/form-validation';
import { STORAGE_CLASS, NETWORK_ATTACHMENT } from '@shell/config/types';
import { allHash } from '@shell/utils/promise';
import { MANAGEMENT_NETWORK } from '../mixins/harvester-vm';
import { VMIMPORT_SOURCE_PROVIDER, VMIMPORT_SOURCE_KINDS } from '../config/types';
import { HCI } from '../types';
import { isValidDNSLabelName } from '@pkg/utils/regular';
import { mapGetters } from 'vuex';
// Full API types for the fetch dispatch
const VMWARE_SOURCE_TYPE = `${ HCI.MIGRATION }.${ VMIMPORT_SOURCE_KINDS.VMWARE.toLowerCase() }`;
const OPENSTACK_SOURCE_TYPE = `${ HCI.MIGRATION }.${ VMIMPORT_SOURCE_KINDS.OPENSTACK.toLowerCase() }`;
const OVA_SOURCE_TYPE = `${ HCI.MIGRATION }.${ VMIMPORT_SOURCE_KINDS.OVA.toLowerCase() }`;
export default {
name: 'EditVirtualMachineImport',
components: {
CruResource,
Tabbed,
Tab,
LabeledInput,
LabeledSelect,
NameNsDescription,
Checkbox
},
mixins: [CreateEditView, FormValidation],
inheritAttrs: false,
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true,
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
// Fetch all dependencies in parallel to speed up the page load
const hash = {
storageClasses: this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS }),
networks: this.$store.dispatch(`${ inStore }/findAll`, { type: NETWORK_ATTACHMENT }),
vmwareSources: this.$store.dispatch(`${ inStore }/findAll`, { type: VMWARE_SOURCE_TYPE }),
openstackSources: this.$store.dispatch(`${ inStore }/findAll`, { type: OPENSTACK_SOURCE_TYPE }),
ovaSources: this.$store.dispatch(`${ inStore }/findAll`, { type: OVA_SOURCE_TYPE }).catch(() => []),
};
const res = await allHash(hash);
this.allStorageClasses = res.storageClasses;
this.allNetworks = res.networks;
this.vmwareSources = res.vmwareSources;
this.openstackSources = res.openstackSources;
this.ovaSources = res.ovaSources;
},
data() {
// Ensure the spec object exists to prevent 'undefined' errors during rendering
if (!this.value.spec) this.value.spec = {};
if (!this.value.spec.sourceCluster) this.value.spec.sourceCluster = {};
if (!this.value.spec.networkMapping) this.value.spec.networkMapping = [];
// Detect if in Edit mode by checking the existing kind
// This allows to pre-select the correct Provider Type tab
let initialProvider = '';
const existingKind = this.value.spec.sourceCluster.kind;
if (existingKind === VMIMPORT_SOURCE_KINDS.VMWARE) initialProvider = VMIMPORT_SOURCE_PROVIDER.VMWARE;
else if (existingKind === VMIMPORT_SOURCE_KINDS.OPENSTACK) initialProvider = VMIMPORT_SOURCE_PROVIDER.OPENSTACK;
else if (existingKind === VMIMPORT_SOURCE_KINDS.OVA) initialProvider = VMIMPORT_SOURCE_PROVIDER.OVA;
// Construct the unique key (Kind/Namespace/Name) if we are editing an existing resource
let initialSourceKey = null;
if (this.value.spec.sourceCluster.name) {
initialSourceKey = `${ existingKind }/${ this.value.spec.sourceCluster.namespace }/${ this.value.spec.sourceCluster.name }`;
}
return {
allStorageClasses: [],
allNetworks: [],
vmwareSources: [],
openstackSources: [],
ovaSources: [],
// UI State
sourceProviderType: initialProvider,
selectedSourceKey: initialSourceKey,
// Static Options
providerTypeOptions: [
{ label: 'VMware', value: VMIMPORT_SOURCE_PROVIDER.VMWARE },
{ label: 'OpenStack', value: VMIMPORT_SOURCE_PROVIDER.OPENSTACK },
{ label: 'OVA', value: VMIMPORT_SOURCE_PROVIDER.OVA }
],
diskBusOptions: [
// Allow resetting selection / reset to the default behavior (sending null/empty)
{ label: this.t('harvester.addons.vmImport.options.useDefault'), value: '' },
{ label: 'VirtIO', value: 'virtio' },
{ label: 'SCSI', value: 'scsi' },
{ label: 'SATA', value: 'sata' },
{ label: 'USB', value: 'usb' },
],
interfaceModelOptions: [
// Allow resetting selection / reset to the default behavior (sending null/empty)
{ label: this.t('harvester.addons.vmImport.options.useDefault'), value: '' },
{ label: 'VirtIO', value: 'virtio' },
{ label: 'e1000', value: 'e1000' },
{ label: 'e1000e', value: 'e1000e' },
{ label: 'ne2k_pci', value: 'ne2k_pci' },
{ label: 'pcnet', value: 'pcnet' },
{ label: 'rtl8139', value: 'rtl8139' },
],
fvFormRuleSets: [
{ path: 'metadata.name', rules: ['nameRequired'] },
{ path: 'spec.virtualMachineName', rules: ['vmNameRequired', 'rfc1123'] },
],
};
},
created() {
if (this.registerBeforeHook) {
this.registerBeforeHook(this.updateBeforeSave);
}
},
computed: {
...mapGetters({ t: 'i18n/t' }),
// Return only the sources that match the selected Provider Type (VMware or OpenStack)
sourceOptions() {
let list = [];
if (this.sourceProviderType === VMIMPORT_SOURCE_PROVIDER.VMWARE) {
list = this.vmwareSources;
} else if (this.sourceProviderType === VMIMPORT_SOURCE_PROVIDER.OPENSTACK) {
list = this.openstackSources;
} else if (this.sourceProviderType === VMIMPORT_SOURCE_PROVIDER.OVA) {
list = this.ovaSources;
}
return list.map((s) => {
// Fallback for API version/kind if missing on the object
let kind = s.kind;
if (!kind) {
if (this.sourceProviderType === VMIMPORT_SOURCE_PROVIDER.VMWARE) kind = VMIMPORT_SOURCE_KINDS.VMWARE;
else if (this.sourceProviderType === VMIMPORT_SOURCE_PROVIDER.OPENSTACK) kind = VMIMPORT_SOURCE_KINDS.OPENSTACK;
else if (this.sourceProviderType === VMIMPORT_SOURCE_PROVIDER.OVA) kind = VMIMPORT_SOURCE_KINDS.OVA;
}
const apiVersion = s.apiVersion || `${ HCI.MIGRATION }/v1beta1`;
return {
label: s.metadata.name,
value: `${ kind }/${ s.metadata.namespace }/${ s.metadata.name }`,
// We attach the raw metadata so we can easily populate the spec later without re-finding the object
raw: {
kind,
apiVersion,
name: s.metadata.name,
namespace: s.metadata.namespace
}
};
});
},
fvExtraRules() {
return {
nameRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.fields.name') }) : undefined,
vmNameRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.vmName') }) : undefined,
rfc1123:
(val) => {
if (val && !isValidDNSLabelName(val)) {
return this.t('harvester.addons.vmImport.errors.rfc1123');
}
return undefined;
}
};
},
// Perform various form validations before allowing to submit
isFormValid() {
// Check VM Name is valid
const nameError = this.fvNameRule(this.value.spec.virtualMachineName);
if (nameError) return false;
// Check mandatory fields in Basics
if (!this.value.spec.virtualMachineName) return false;
if (!this.selectedSourceKey) return false;
// Check Network Mappings
// If any row is missing source or destination, the form is invalid.
const networks = this.value.spec.networkMapping || [];
const hasInvalidRow = networks.some((row) => !row.sourceNetwork || !row.destinationNetwork);
if (hasInvalidRow) return false;
return true;
},
isNetworkTabInvalid() {
const networks = this.value.spec.networkMapping || [];
// Only error if a row exists AND it is missing fields
return networks.some((row) => !row.sourceNetwork || !row.destinationNetwork);
},
// Filter out internal storage classes
// to prevent selecting a class that might cause the import to fail
storageClassOptions() {
return this.allStorageClasses
.filter((sc) => {
const isInternal = sc.parameters?.['harvesterhci.io/isInternalStorageClass'] === 'true';
return !isInternal;
})
.map((sc) => ({
label: sc.nameDisplay,
value: sc.id
}));
},
networkOptions() {
const mgmtOption = {
label: 'Management Network',
value: MANAGEMENT_NETWORK
};
const vlanOptions = this.allNetworks.map((n) => ({
label: n.nameDisplay || n.metadata.name,
value: n.id
}));
return [mgmtOption, ...vlanOptions];
}
},
methods: {
// Clear the selected cluster if the user switches providers (e.g. VMware -> OpenStack)
// Prevents submitting a VMware cluster name while the kind is OpenStack
onProviderTypeChange(newType) {
this.selectedSourceKey = null;
this.value.spec.sourceCluster = {};
},
// Update the sourceCluster object based on the single dropdown selection
updateSource(key) {
this.selectedSourceKey = key;
const selectedOption = this.sourceOptions.find((o) => o.value === key);
if (selectedOption) {
const {
kind, apiVersion, name, namespace
} = selectedOption.raw;
this.value.spec.sourceCluster = {
kind,
apiVersion,
name,
namespace
};
} else {
this.value.spec.sourceCluster = {};
}
},
addNetworkMapping() {
this.value.spec.networkMapping.push({
sourceNetwork: '',
destinationNetwork: '',
networkInterfaceModel: ''
});
},
removeNetworkMapping(index) {
if (!this.value?.spec?.networkMapping) {
return;
}
if (index >= 0 && index < this.value.spec.networkMapping.length) {
this.value.spec.networkMapping.splice(index, 1);
}
},
requiredRule(val) {
if (!val) {
return this.t('validation.required', { key: this.t('generic.value') });
}
return undefined;
},
// Validates that the input follows Kubernetes Naming Rules (RFC 1123).
// If the source VM has uppercase letters or spaces, the user must be warned
// that they cannot import it until they rename it on the source. See:
// https://docs.harvesterhci.io/v1.6/advanced/addons/vmimport/#source-virtual-machine-name-is-not-rfc1123-compliant
fvNameRule(val) {
if (!val) return undefined; // 'Required' check handles empty state separately
// valid RFC 1123
if (!isValidDNSLabelName(val)) {
return this.t('harvester.addons.vmImport.errors.rfc1123');
}
return undefined;
},
updateBeforeSave() {
// If networkMapping exists, filter out the "Management Network" rows
// Let the vm-import-controller set the default network mapping
if (this.value.spec.networkMapping) {
this.value.spec.networkMapping = this.value.spec.networkMapping.filter((row) => {
return row.destinationNetwork !== MANAGEMENT_NETWORK;
});
}
},
// Only handles complex logic that doesn't fit into simple field rules
async saveOverride(buttonCb) {
const errors = [];
this.errors = [];
// Validate Provider Type
if (!this.sourceProviderType) {
errors.push(this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.sourceProvider') }));
}
// Validate Network Tab
if (this.isNetworkTabInvalid) {
errors.push(this.t('harvester.addons.vmImport.errors.networkMappingRequired'));
}
// Return immediately in case of an error, avoid that `this.save()` runs, preventing `updateBeforeSave` from resetting data.
if (errors.length > 0) {
this.errors = errors;
buttonCb(false);
return;
}
// Only proceed if valid
this.save(buttonCb);
},
}
};
</script>
<template>
<CruResource
:done-route="doneRoute"
:resource="value"
:mode="mode"
:errors="errors"
:apply-hooks="applyHooks"
:validation-passed="fvFormIsValid"
@finish="saveOverride"
@error="e=>errors=e"
>
<NameNsDescription
:value="value"
:mode="mode"
:rules="{ name: fvGetAndReportPathRules('metadata.name') }"
@update:value="$emit('update:value', $event)"
/>
<Tabbed
v-bind="$attrs"
class="mt-15"
:side-tabs="true"
>
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="3"
>
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect
v-model:value="sourceProviderType"
:options="providerTypeOptions"
:label="t('harvester.addons.vmImport.fields.sourceProvider')"
:mode="mode"
:rules="[requiredRule]"
required
@update:value="onProviderTypeChange"
/>
</div>
<div class="col span-6">
<LabeledSelect
:value="selectedSourceKey"
:options="sourceOptions"
:label="t('harvester.addons.vmImport.fields.sourceCluster')"
:placeholder="sourceProviderType ? t('harvester.addons.vmImport.placeholders.selectCluster') : t('harvester.addons.vmImport.placeholders.selectProviderFirst')"
:disabled="!sourceProviderType"
:mode="mode"
:rules="fvGetAndReportPathRules('selectedSourceKey')"
required
@update:value="updateSource"
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="value.spec.virtualMachineName"
:label="t('harvester.addons.vmImport.fields.vmName')"
:placeholder="t('harvester.addons.vmImport.placeholders.matchSource')"
:mode="mode"
:rules="fvGetAndReportPathRules('spec.virtualMachineName')"
required
/>
</div>
<div class="col span-6">
<LabeledSelect
v-model:value="value.spec.storageClass"
:options="storageClassOptions"
:label="t('harvester.addons.vmImport.fields.targetStorageClass')"
:mode="mode"
/>
</div>
</div>
</Tab>
<Tab
name="networking"
:label="t('harvester.addons.vmImport.titles.networking')"
:weight="2"
:error="isNetworkTabInvalid"
>
<div
v-for="(row, i) in value.spec.networkMapping"
:key="i"
class="network-row box mb-10"
>
<div class="row">
<div class="col span-4">
<LabeledInput
v-model:value="row.sourceNetwork"
:label="t('harvester.addons.vmImport.fields.sourceNetwork')"
:mode="mode"
:rules="[requiredRule]"
required
/>
</div>
<div class="col span-4">
<LabeledSelect
v-model:value="row.destinationNetwork"
:options="networkOptions"
:label="t('harvester.addons.vmImport.fields.destNetwork')"
:mode="mode"
:rules="[requiredRule]"
required
/>
</div>
<div class="col span-3">
<LabeledSelect
v-model:value="row.networkInterfaceModel"
:options="interfaceModelOptions"
:label="t('harvester.addons.vmImport.fields.interfaceModel')"
:mode="mode"
/>
</div>
<div class="col span-1 remove-btn-container">
<button
type="button"
class="btn role-link"
@click="removeNetworkMapping(i)"
>
{{ t('harvester.addons.vmImport.actions.remove') }}
</button>
</div>
</div>
</div>
<button
type="button"
class="btn role-secondary"
@click="addNetworkMapping"
>
{{ t('harvester.addons.vmImport.actions.addNetwork') }}
</button>
</Tab>
<Tab
name="advanced"
:label="t('harvester.addons.vmImport.titles.advanced')"
:weight="1"
>
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="value.spec.folder"
:label="t('harvester.addons.vmImport.fields.folder')"
:placeholder="t('harvester.addons.vmImport.placeholders.folderExample')"
:mode="mode"
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect
v-model:value="value.spec.defaultDiskBusType"
:options="diskBusOptions"
:label="t('harvester.addons.vmImport.fields.diskBus')"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledSelect
v-model:value="value.spec.defaultNetworkInterfaceModel"
:options="interfaceModelOptions"
:label="t('harvester.addons.vmImport.fields.defaultInterface')"
:mode="mode"
/>
</div>
</div>
<div class="row">
<div class="col span-12">
<Checkbox
v-model:value="value.spec.skipPreflightChecks"
:label="t('harvester.addons.vmImport.fields.skipPreflight')"
:mode="mode"
/>
<Checkbox
v-model:value="value.spec.forcePowerOff"
:label="t('harvester.addons.vmImport.fields.forcePowerOff')"
:mode="mode"
class="mt-10"
/>
</div>
</div>
</Tab>
</Tabbed>
</CruResource>
</template>
<style lang="scss" scoped>
.network-row {
border: 1px solid var(--border);
padding: 10px;
border-radius: var(--border-radius);
background: var(--body-bg);
}
.remove-btn-container {
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@ -1,302 +0,0 @@
<script>
import CruResource from '@shell/components/CruResource';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import NameNsDescription from '@shell/components/form/NameNsDescription';
import { RadioGroup } from '@components/Form/Radio';
import CreateEditView from '@shell/mixins/create-edit-view';
import FormValidation from '@shell/mixins/form-validation';
import { SECRET } from '@shell/config/types';
import { randomStr } from '@shell/utils/string';
import { mapGetters } from 'vuex';
export default {
name: 'EditVmwareSource',
// Declare the event, fixes a console warning
emits: ['update:value'],
components: {
CruResource,
Tabbed,
Tab,
LabeledInput,
LabeledSelect,
NameNsDescription,
RadioGroup,
},
mixins: [CreateEditView, FormValidation],
inheritAttrs: false,
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true,
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.allSecrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET });
},
data() {
if (!this.value.spec) this.value.spec = {};
if (!this.value.spec.credentials) this.value.spec.credentials = {};
const initialMode = this.value.spec.credentials.name ? 'existing' : 'new';
return {
allSecrets: [],
authMode: initialMode,
newUsername: '',
newPassword: '',
newCaCert: '',
fvFormRuleSets: [
{ path: 'metadata.name', rules: ['nameRequired'] },
{ path: 'spec.endpoint', rules: ['endpointRequired'] },
{ path: 'spec.dc', rules: ['dcRequired'] },
],
};
},
computed: {
...mapGetters({ t: 'i18n/t' }),
authModeOptions() {
return [
{ label: this.t('harvester.addons.vmImport.fields.createSecret'), value: 'new' },
{ label: this.t('harvester.addons.vmImport.fields.useSecret'), value: 'existing' }
];
},
secretOptions() {
const currentNamespace = this.value.metadata.namespace || 'default';
return this.allSecrets
.filter((s) => s.metadata.namespace === currentNamespace)
.map((s) => ({
label: s.nameDisplay,
value: s.metadata.name
}));
},
// Define custom rules for the FormValidation mixin
fvExtraRules() {
return {
nameRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.fields.name') }) : undefined,
endpointRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.vmware.fields.endpoint') }) : undefined,
dcRequired: (val) => !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.vmware.fields.datacenter') }) : undefined,
};
},
isFormValid() {
if (!this.fvFormIsValid) {
return false;
}
if (this.authMode === 'new') {
if (!this.newUsername || !this.newPassword) return false;
} else {
if (!this.value.spec.credentials.name) return false;
}
return true;
}
},
methods: {
usernameRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.username') }) : undefined;
},
passwordRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.password') }) : undefined;
},
secretRule(val) {
return !val ? this.t('validation.required', { key: this.t('harvester.addons.vmImport.fields.selectSecret') }) : undefined;
},
async saveSource(buttonCb) {
const inStore = this.$store.getters['currentProduct'].inStore;
try {
if (this.authMode === 'new') {
const secretName = `${ this.value.metadata.name }-creds-${ randomStr(4).toLowerCase() }`;
const namespace = this.value.metadata.namespace || 'default';
// Create the model with the correct Schema ID (SECRET)
const newSecret = await this.$store.dispatch(`${ inStore }/create`, {
type: SECRET,
metadata: {
name: secretName,
namespace
}
});
// Use '_type' to set the Kubernetes 'type' field.
newSecret['_type'] = 'Opaque';
// base64 encode the data
newSecret['data'] = {
username: btoa(this.newUsername),
password: btoa(this.newPassword),
// Only include CA cert if the user provided one
caCert: this.newCaCert ? btoa(this.newCaCert) : undefined
};
await newSecret.save();
// Link the new secret to the Source
this.value.spec.credentials = {
name: secretName,
namespace
};
}
await this.save(buttonCb);
} catch (err) {
this.errors = [err];
buttonCb(false);
}
}
}
};
</script>
<template>
<CruResource
:done-route="doneRoute"
:resource="value"
:mode="mode"
:errors="errors"
:apply-hooks="applyHooks"
:validation-passed="isFormValid"
@finish="saveSource"
@error="e=>errors=e"
>
<NameNsDescription
:value="value"
:mode="mode"
:rules="{ name: fvGetAndReportPathRules('metadata.name') }"
@update:value="$emit('update:value', $event)"
/>
<Tabbed
v-bind="$attrs"
class="mt-15"
:side-tabs="true"
>
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="2"
>
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="value.spec.endpoint"
:label="t('harvester.addons.vmImport.vmware.fields.endpoint')"
:placeholder="t('harvester.addons.vmImport.vmware.placeholders.endpoint')"
:mode="mode"
:rules="fvGetAndReportPathRules('spec.endpoint')"
required
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="value.spec.dc"
:label="t('harvester.addons.vmImport.vmware.fields.datacenter')"
:placeholder="t('harvester.addons.vmImport.vmware.placeholders.datacenter')"
:tooltip="t('harvester.addons.vmImport.vmware.tooltips.datacenter')"
:mode="mode"
:rules="fvGetAndReportPathRules('spec.dc')"
required
/>
</div>
</div>
</Tab>
<Tab
name="auth"
:label="t('harvester.addons.vmImport.titles.auth')"
:weight="1"
>
<div class="row mb-20">
<div class="col span-12">
<RadioGroup
v-model:value="authMode"
name="authMode"
:options="authModeOptions"
:mode="mode"
/>
</div>
</div>
<div v-if="authMode === 'new'">
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="newUsername"
:label="t('harvester.addons.vmImport.fields.username')"
:mode="mode"
:rules="[usernameRule]"
required
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="newPassword"
type="password"
:label="t('harvester.addons.vmImport.fields.password')"
:mode="mode"
:rules="[passwordRule]"
required
/>
</div>
</div>
<div class="row mb-20">
<div class="col span-12">
<LabeledInput
v-model:value="newCaCert"
type="multiline"
:label="t('harvester.addons.vmImport.fields.caCert')"
:placeholder="t('harvester.addons.vmImport.placeholders.caCert')"
:min-height="100"
:mode="mode"
/>
</div>
</div>
<div class="text-muted">
Note: A new Kubernetes Secret will be created to store these credentials.
</div>
</div>
<div v-if="authMode === 'existing'">
<div class="row mb-20">
<div class="col span-6">
<LabeledSelect
v-model:value="value.spec.credentials.name"
:options="secretOptions"
:label="t('harvester.addons.vmImport.fields.selectSecret')"
:mode="mode"
:rules="[secretRule]"
required
/>
</div>
</div>
</div>
</Tab>
</Tabbed>
</CruResource>
</template>

View File

@ -7,7 +7,6 @@ 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'; import { defineAsyncComponent } from 'vue';
import './styles/vue-flow.scss';
// Init the package // Init the package
export default function (plugin: IPlugin) { export default function (plugin: IPlugin) {

View File

@ -20,7 +20,6 @@ nav:
Monitoring: Monitoring Monitoring: Monitoring
Logging: Logging Logging: Logging
'Monitoring and Logging': Monitoring and Logging 'Monitoring and Logging': Monitoring and Logging
vmimport: Virtual Machine Imports
resourceTable: resourceTable:
groupBy: groupBy:
@ -123,15 +122,6 @@ harvester:
namespace: Namespace namespace: Namespace
message: message:
success: 'Image { name } created successfully.' success: 'Image { name } created successfully.'
storageMigration:
title: Storage Migration
fields:
sourceVolume:
label: Source Volume
placeholder: Select a source volume
targetVolume:
label: Target Volume
placeholder: Select a target volume
migration: migration:
failedMessage: Latest migration failed! failedMessage: Latest migration failed!
title: Migration title: Migration
@ -173,11 +163,6 @@ harvester:
vmNetwork: Virtual Machine Network vmNetwork: Virtual Machine Network
macAddress: MAC Address macAddress: MAC Address
macAddressTooltip: If left blank, the MAC address will be automatically generated. macAddressTooltip: If left blank, the MAC address will be automatically generated.
insertCdRomVolume:
success: '{ imageName } is inserted into device { deviceName }.'
title: Insert Image
image: Image
actionLabel: Insert Image
cpuMemoryHotplug: cpuMemoryHotplug:
success: 'CPU and Memory are updated to the virtual machine { vm }.' success: 'CPU and Memory are updated to the virtual machine { vm }.'
title: Edit CPU and Memory title: Edit CPU and Memory
@ -210,9 +195,6 @@ harvester:
info: Info info: Info
warning: Warning warning: Warning
error: Error error: Error
restartRequired:
title: '{count} {count, plural, =1 {Virtual Machine is} other {Virtual Machines are}} Pending Restart'
message: 'Please restart { vmNames } to apply updated configurations'
action: action:
createVM: Create Virtual Machine createVM: Create Virtual Machine
start: Start start: Start
@ -241,8 +223,6 @@ harvester:
migrate: Migrate migrate: Migrate
cpuAndMemoryHotplug: Edit CPU and Memory cpuAndMemoryHotplug: Edit CPU and Memory
abortMigration: Abort Migration abortMigration: Abort Migration
storageMigration: Storage Migration
cancelStorageMigration: Cancel Storage Migration
createTemplate: Generate Template createTemplate: Generate Template
enableMaintenance: Enable Maintenance Mode enableMaintenance: Enable Maintenance Mode
disableMaintenance: Disable Maintenance Mode disableMaintenance: Disable Maintenance Mode
@ -315,17 +295,6 @@ harvester:
totalSnapshotQuota: Total Snapshot Quota totalSnapshotQuota: Total Snapshot Quota
storageClass: Storage Class storageClass: Storage Class
restore: Restore restore: Restore
vmImportSourceVm: Source VM
vmImportSourceCluster: Source Cluster
vmImportStatus: Import Status
vmImportSourceVDatacenter: Datacenter
vmImportSourceVEndpoint: Endpoint
vmImportSourceVClusterStatus: Cluster Status
vmImportSourceORegion: Region
vmImportSourceOEndpoint: Endpoint
vmImportSourceOClusterStatus: Cluster Status
vmImportSourceOVAUrl: URL
vmImportSourceOVAStatus: Status
tab: tab:
volume: Volumes volume: Volumes
network: Networks network: Networks
@ -366,10 +335,7 @@ harvester:
available: Available Devices available: Available Devices
compatibleNodes: Compatible Nodes compatibleNodes: Compatible Nodes
impossibleSelection: 'There are no hosts with all of the selected devices.' impossibleSelection: 'There are no hosts with all of the selected devices.'
howToUseDevice: howToUseDevice: 'Use the table below to enable PCI passthrough on each device you want to use in this virtual machine.'
prefix: 'Use the table below to enable PCI passthrough on each device you want to use in this virtual machine.<br>For vGPU devices, please enable them on the'
middle: vGPU Devices
suffix: page first.
deviceInTheSameHost: 'You can only select devices on the same host.' deviceInTheSameHost: 'You can only select devices on the same host.'
oldFormatDevices: oldFormatDevices:
help: |- help: |-
@ -391,9 +357,6 @@ harvester:
claimError: Error enabling passthrough on {name} claimError: Error enabling passthrough on {name}
unclaimError: Error disabling passthrough on {name} unclaimError: Error disabling passthrough on {name}
cantUnclaim: You cannot disable passthrough on a device claimed by another user. cantUnclaim: You cannot disable passthrough on a device claimed by another user.
detachWarning:
title: Cannot Disable Passthrough
message: Please detach the device from the VM and save it first before disabling passthrough.
enableGroup: Enable Group enableGroup: Enable Group
disableGroup: Disable Group disableGroup: Disable Group
labelRequired: "This rule should not be manually altered: it ensures that the PCI devices selected for this virtual machine are available on the virtual machine's host." labelRequired: "This rule should not be manually altered: it ensures that the PCI devices selected for this virtual machine are available on the virtual machine's host."
@ -439,7 +402,7 @@ harvester:
volume: volume:
upperType: Volume name upperType: Volume name
lowerType: volume name lowerType: volume name
needAtLeastOneBootable: 'At least one bootable volume is required!' needImageOrExisting: 'At least an image volume or an existing root-disk volume is required!'
image: image:
ruleTip: 'The URL you have entered ends in an extension that we do not support. We only accept image files that end in .img, .iso, .qcow, .qcow2, .raw.' ruleTip: 'The URL you have entered ends in an extension that we do not support. We only accept image files that end in .img, .iso, .qcow, .qcow2, .raw.'
ruleFileTip: 'The file you have chosen ends in an extension that we do not support. We only accept image files that end in .img, .iso, .qcow, .qcow2, .raw.' ruleFileTip: 'The file you have chosen ends in an extension that we do not support. We only accept image files that end in .img, .iso, .qcow, .qcow2, .raw.'
@ -635,10 +598,6 @@ harvester:
virtualMachine: virtualMachine:
label: Virtual Machines label: Virtual Machines
osType: OS Type osType: OS Type
cpuModel:
label: CPU Model
fetchError: 'Failed to load CPU model configuration: {error}'
optionLabel: "{modelName} ({count} {count, plural, one {node} other {nodes}})"
hotplug: hotplug:
title: Enable CPU and memory hotplug title: Enable CPU and memory hotplug
tooltip: The default maximum CPU and maximum memory are {hotPlugTimes} times based on CPU and memory. tooltip: The default maximum CPU and maximum memory are {hotPlugTimes} times based on CPU and memory.
@ -649,10 +608,6 @@ harvester:
title: 'Are you sure that you want to detach volume {name}?' title: 'Are you sure that you want to detach volume {name}?'
actionLabel: Detach Volume actionLabel: Detach Volume
success: 'Volume { name } is detached successfully.' success: 'Volume { name } is detached successfully.'
ejectCdRomVolume:
title: 'Are you sure that you want to eject image from device {name}?'
actionLabel: Eject Image
success: 'Image from device { name } is ejected successfully.'
detachNIC: detachNIC:
title: 'Are you sure that you want to detach network interface {name}?' title: 'Are you sure that you want to detach network interface {name}?'
actionLabel: Detach Network Interface actionLabel: Detach Network Interface
@ -759,7 +714,6 @@ harvester:
unmount: unmount:
title: Are you sure? title: Are you sure?
message: Are you sure you want to unmount this volume? message: Are you sure you want to unmount this volume?
emptyImage: No media
network: network:
title: Network title: Network
addNetwork: Add Network addNetwork: Add Network
@ -1031,9 +985,7 @@ harvester:
createTitle: Create Schedule createTitle: Create Schedule
createButtonText: Create Schedule createButtonText: Create Schedule
scheduleType: Virtual Machine Schedule Type scheduleType: Virtual Machine Schedule Type
cron: cron: Cron Schedule
label: Cron Schedule
editButton: Edit
detail: detail:
namespace: Namespace namespace: Namespace
sourceVM: Source Virtual Machine sourceVM: Source Virtual Machine
@ -1160,25 +1112,6 @@ harvester:
banner: The supported field in ACL match can refer to <a href="https://kubeovn.github.io/docs/v1.14.x/en/guide/subnet/#subnet-acl" target="_blank">KubeOvn Subnet ACL document</a> banner: The supported field in ACL match can refer to <a href="https://kubeovn.github.io/docs/v1.14.x/en/guide/subnet/#subnet-acl" target="_blank">KubeOvn Subnet ACL document</a>
vpc: vpc:
viewTopology: Topology
topology:
loading: Loading topology...
empty: No resources found
visibility:
vpc: VPC
subnets: Subnets
overlayNetworks: Overlay Networks
vms: VMs
labels:
cidr: CIDR
provider: Provider
type: Type
clusterNetwork: Cluster Network
network: Network
subnet: Subnet
ip: IP
mac: MAC
peering: Peering
noAddonEnabled: noAddonEnabled:
prefix: The kubeovn-operator add-on is not enabled, click prefix: The kubeovn-operator add-on is not enabled, click
middle: here middle: here
@ -1280,14 +1213,8 @@ harvester:
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 * 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>.'
rwxNetwork:
warning: 'WARNING: <br/> Any change to rwx-network requires longhorn RWX volumes detached 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.'
shareStorageNetwork: Share Storage Network
dedicatedRwxNetwork: Dedicated RWX Network
shareStorageNetworkWarning: The rwx-network is governed by storage-network, and changes here won't take effect until share-storage-network is set to false.
vmForceDeletionPolicy: vmForceDeletionPolicy:
period: Period period: Period
vmMigrationTimeout: VM Migration Timeout
vmMigrationNetwork: vmMigrationNetwork:
parseError: "Failed to parse existing configuration." parseError: "Failed to parse existing configuration."
fetchError: "Failed to load required network resources: {error}. Please refresh the page or try again later." fetchError: "Failed to load required network resources: {error}. Please refresh the page or try again later."
@ -1351,10 +1278,7 @@ harvester:
deleteImage: Please select an image to delete. deleteImage: Please select an image to delete.
deleteSuccess: "{name} deleted successfully." deleteSuccess: "{name} deleted successfully."
imagePreloadStrategy: Image Preload Strategy imagePreloadStrategy: Image Preload Strategy
nodeUpgradeOption: Node Upgrade Option
restoreVM: Restore VM restoreVM: Restore VM
strategy: Strategy
pauseNodes: Pause Nodes
strategyType: Strategy Type strategyType: Strategy Type
concurrency: Concurrency concurrency: Concurrency
harvesterMonitoring: harvesterMonitoring:
@ -1370,20 +1294,10 @@ harvester:
retention: How long to retain metrics retention: How long to retain metrics
retentionSize: Maximum size of metrics retentionSize: Maximum size of metrics
clusterRegistrationUrl: clusterRegistrationUrl:
url: URL
insecureSkipTLSVerify: Insecure Skip TLS Verify
tip:
prefix: Harvester secures cluster registration via TLS by default. If opt out "Insecure Skip TLS Verify", you must provide custom CA certificates using the
middle: 'additional-ca'
suffix: setting.
message: To completely unset the imported Harvester cluster, please also remove it on the Rancher Dashboard UI via the <code> Virtualization Management </code> page. message: To completely unset the imported Harvester cluster, please also remove it on the Rancher Dashboard UI via the <code> Virtualization Management </code> page.
ntpServers: ntpServers:
isNotIPV4: The address you entered is not IPv4 or host. Please enter a valid IPv4 address or a host address. isNotIPV4: The address you entered is not IPv4 or host. Please enter a valid IPv4 address or a host address.
isDuplicate: There are duplicate NTP server configurations. isDuplicate: There are duplicate NTP server configurations.
instanceManagerResources:
parseError: "Failed to parse configuration: {error}"
v1: "V1 Data Engine"
v2: "V2 Data Engine"
kubevirtMigration: kubevirtMigration:
parseError: "Failed to parse configuration: {error}" parseError: "Failed to parse configuration: {error}"
parallelMigrationsPerCluster: "Parallel Migrations Per Cluster" parallelMigrationsPerCluster: "Parallel Migrations Per Cluster"
@ -1650,85 +1564,10 @@ harvester:
'harvester-system/harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations. 'harvester-system/harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations.
'harvester-csi-driver-lvm': harvester-csi-driver-lvm is an add-on allowing users to create PVC through the LVM with local devices. 'harvester-csi-driver-lvm': harvester-csi-driver-lvm is an add-on allowing users to create PVC through the LVM with local devices.
'descheduler': 'The virtual machine auto balance optimizes workload scheduling by evicting pods that are not optimally placed according to administrator-defined policies.' 'descheduler': 'The virtual machine auto balance optimizes workload scheduling by evicting pods that are not optimally placed according to administrator-defined policies.'
vmImport: vmImport:
titles: titles:
basic: Basic basic: Basic
auth: Authentication
pvc: Volume pvc: Volume
networking: Network Mapping
advanced: Advanced
labels:
vmimport: Virtual Machine Import
vmimportSourceVMWare: Source VMWare
vmimportSourceOpenStack: Source OpenStack
vmimportSourceOVA: Source OVA
fields:
sourceProvider: Source Provider Type
sourceCluster: Source Cluster
vmName: VM Name
targetStorageClass: Target Storage Class
sourceNetwork: Source Network Name
destNetwork: Destination Network
interfaceModel: Interface Model
folder: Folder
diskBus: Default Disk Bus
defaultInterface: Default Network Interface
skipPreflight: Skip Preflight Checks
forcePowerOff: Force Power Off Source VM
username: Username
password: Password
caCert: CA Certificate (PEM)
selectSecret: Select Secret
createSecret: Create New Credentials
useSecret: Use Existing Secret
none: None (Public URL)
placeholders:
selectCluster: Select a cluster...
selectProviderFirst: Select a provider type first
matchSource: Must match the name in the source cluster
folderExample: e.g. /Datacenters/DC1/vm
caCert: "-----BEGIN CERTIFICATE----- ..."
options:
useDefault: Use Default
actions:
addNetwork: Add Network Mapping
remove: Remove
errors:
rfc1123: 'Invalid format. Name must be lowercase, alphanumeric, and cannot contain spaces (e.g. "my-vm-1"). If your Source VM name does not match this, you must rename it on the Source cluster first.'
networkMappingRequired: Every Network Mapping row must have a Source and Destination selected.
openstack:
fields:
endpoint: Identity Service Endpoint
region: Region
projectName: Project Name
domainName: Domain Name
retryCount: Upload Image Retry Count
retryDelay: Upload Image Retry Delay
placeholders:
endpoint: "e.g. https://devstack/identity"
region: e.g. RegionOne
projectName: e.g. admin
domainName: e.g. default
retryCount: "Default: 30"
retryDelay: "Default: 10"
vmware:
fields:
endpoint: vCenter Endpoint
datacenter: Datacenter
placeholders:
endpoint: "e.g. https://vscim/sdk"
datacenter: e.g. DC0
tooltips:
datacenter: The exact name of the Datacenter object in vCenter
ova:
fields:
url: URL
httpTimeout: HTTP Timeout
placeholders:
url: "e.g. https://download.example.com/images/my-vm.ova"
httpTimeout: "Default: 600"
rancherVcluster: rancherVcluster:
accessRancher: Access the Rancher Dashboard accessRancher: Access the Rancher Dashboard
hostname: Hostname hostname: Hostname
@ -1855,8 +1694,7 @@ harvester:
numVFs: Number Of Virtual Functions numVFs: Number Of Virtual Functions
vfAddresses: Virtual Functions Addresses vfAddresses: Virtual Functions Addresses
showMore: Show More showMore: Show More
parentSriov: Filter By Parent SR-IOV Netork Device parentSriov: Filter By Parent SR-IOV
parentSriovGPU: Filter By Parent SR-IOV GPU Device
sriovgpu: sriovgpu:
label: SR-IOV GPU Devices label: SR-IOV GPU Devices
@ -1895,9 +1733,6 @@ harvester:
vgpu: vgpu:
label: vGPU Devices label: vGPU Devices
noPermission: Please contact system administrator to add Harvester add-ons first. noPermission: Please contact system administrator to add Harvester add-ons first.
detachWarning:
title: Cannot Disable vGPU
message: Please detach the device from the VM and save it first before disabling this vGPU device.
goSetting: goSetting:
prefix: The nvidia-driver-toolkit add-on is not enabled, click prefix: The nvidia-driver-toolkit add-on is not enabled, click
middle: here middle: here
@ -1932,9 +1767,6 @@ harvester:
claimError: Error enabling passthrough on {name} claimError: Error enabling passthrough on {name}
unclaimError: Error disabling passthrough on {name} unclaimError: Error disabling passthrough on {name}
cantUnclaim: You cannot disable passthrough on a device claimed by another user. cantUnclaim: You cannot disable passthrough on a device claimed by another user.
detachWarning:
title: Cannot Disable Passthrough
message: Please detach the device from the VM and save it first before disabling passthrough.
enablePassthroughWarning: 'Please re-enable the USB device if the device path changes in the following situations:<br/>&nbsp1) Re-plugging the USB device.<br/>&nbsp2) Rebooting the node.<br/><br/>An incorrect device path may cause passthrough to fail.' enablePassthroughWarning: 'Please re-enable the USB device if the device path changes in the following situations:<br/>&nbsp1) Re-plugging the USB device.<br/>&nbsp2) Rebooting the node.<br/><br/>An incorrect device path may cause passthrough to fail.'
harvesterVlanConfigMigrateDialog: harvesterVlanConfigMigrateDialog:
@ -1997,13 +1829,11 @@ advancedSettings:
'harv-additional-ca': 'Custom CA root certificates for TLS validation.' 'harv-additional-ca': 'Custom CA root certificates for TLS validation.'
'harv-overcommit-config': 'Resource overcommit configuration.' 'harv-overcommit-config': 'Resource overcommit configuration.'
'harv-support-bundle-timeout': 'Support bundle timeout configuration in minutes, use 0 to disable the timeout.' 'harv-support-bundle-timeout': 'Support bundle timeout configuration in minutes, use 0 to disable the timeout.'
'harv-support-bundle-file-name': 'Support bundle file name configuration.'
'harv-support-bundle-expiration': 'Support bundle expiration configuration in minutes.' 'harv-support-bundle-expiration': 'Support bundle expiration configuration in minutes.'
'harv-support-bundle-node-collection-timeout': 'Support bundle node collection timeout configuration in minutes.' 'harv-support-bundle-node-collection-timeout': 'Support bundle node collection timeout configuration in minutes.'
'harv-vm-force-reset-policy': Configuration for the force-reset action when a virtual machine is stuck on a node that is down. 'harv-vm-force-reset-policy': Configuration for the force-reset action when a virtual machine is stuck on a node that is down.
'harv-ssl-parameters': Custom SSL Parameters for TLS validation. 'harv-ssl-parameters': Custom SSL Parameters for TLS validation.
'harv-storage-network': 'Longhorn storage-network setting.' 'harv-storage-network': 'Longhorn storage-network setting.'
'harv-rwx-network': 'Configure RWX network behavior for shared or dedicated storage network usage.'
'harv-support-bundle-namespaces': Select additional namespaces to include in the support bundle. 'harv-support-bundle-namespaces': Select additional namespaces to include in the support bundle.
'harv-auto-disk-provision-paths': Specify the disks(using glob pattern) that Harvester will automatically add as virtual machine storage. 'harv-auto-disk-provision-paths': Specify the disks(using glob pattern) that Harvester will automatically add as virtual machine storage.
'harv-support-bundle-image': Support bundle image configuration. Find different versions in <a href="https://hub.docker.com/r/rancher/support-bundle-kit/tags" target="_blank">rancher/support-bundle-kit</a>. 'harv-support-bundle-image': Support bundle image configuration. Find different versions in <a href="https://hub.docker.com/r/rancher/support-bundle-kit/tags" target="_blank">rancher/support-bundle-kit</a>.
@ -2019,7 +1849,6 @@ advancedSettings:
'harv-rancher-cluster': 'Configure Rancher cluster integration settings for guest cluster management.' 'harv-rancher-cluster': 'Configure Rancher cluster integration settings for guest cluster management.'
'harv-max-hotplug-ratio': 'The ratio for kubevirt to limit the maximum CPU and memory that can be hotplugged to a VM. The value could be an integer between 1 and 20, default to 4.' 'harv-max-hotplug-ratio': 'The ratio for kubevirt to limit the maximum CPU and memory that can be hotplugged to a VM. The value could be an integer between 1 and 20, default to 4.'
'harv-kubevirt-migration': 'Configure cluster-wide KubeVirt live migration parameters.' 'harv-kubevirt-migration': 'Configure cluster-wide KubeVirt live migration parameters.'
'harv-instance-manager-resources': 'Configure resource percentage reservations for Longhorn instance manager V1 and V2. Valid instance manager CPU range between 0 - 40.'
typeLabel: typeLabel:
kubevirt.io.virtualmachine: |- kubevirt.io.virtualmachine: |-
@ -2201,23 +2030,3 @@ typeLabel:
one { IP Pool } one { IP Pool }
other { IP Pools } other { IP Pools }
} }
migration.harvesterhci.io.openstacksource: |-
{count, plural,
one { OpenStack Source }
other { OpenStack Sources }
}
migration.harvesterhci.io.vmwaresource: |-
{count, plural,
one { VMware Source }
other { VMware Sources }
}
migration.harvesterhci.io.ovasource: |-
{count, plural,
one { OVA Source }
other { OVA Sources }
}
migration.harvesterhci.io.virtualmachineimport: |-
{count, plural,
one { Virtual Machine Import }
other { Virtual Machine Imports }
}

View File

@ -64,10 +64,6 @@ export default {
const inStore = this.$store.getters['currentProduct'].inStore; const inStore = this.$store.getters['currentProduct'].inStore;
const rows = this.$store.getters[`${ inStore }/all`](HCI.PCI_DEVICE); const rows = this.$store.getters[`${ inStore }/all`](HCI.PCI_DEVICE);
rows.forEach((row) => {
row.allowDisable = true;
});
return rows; return rows;
} }
}, },

View File

@ -54,13 +54,7 @@ export default {
devices() { devices() {
const inStore = this.$store.getters['currentProduct'].inStore; const inStore = this.$store.getters['currentProduct'].inStore;
const data = this.$store.getters[`${ inStore }/all`](HCI.USB_DEVICE) || []; return this.$store.getters[`${ inStore }/all`](HCI.USB_DEVICE) || [];
data.forEach((row) => {
row.allowDisable = true;
});
return data;
} }
}, },

View File

@ -65,10 +65,6 @@ export default {
const vGpuDevices = this.$store.getters[`${ inStore }/all`](HCI.VGPU_DEVICE) || []; const vGpuDevices = this.$store.getters[`${ inStore }/all`](HCI.VGPU_DEVICE) || [];
const srioVGpuDevices = this.$store.getters[`${ inStore }/all`](HCI.SR_IOVGPU_DEVICE) || []; const srioVGpuDevices = this.$store.getters[`${ inStore }/all`](HCI.SR_IOVGPU_DEVICE) || [];
vGpuDevices.forEach((row) => {
row.allowDisable = true;
});
if (this.hasSRIOVGPUSchema) { if (this.hasSRIOVGPUSchema) {
return vGpuDevices.filter((device) => !!srioVGpuDevices.find((s) => s.isEnabled && s.spec?.nodeName === device.spec?.nodeName)); return vGpuDevices.filter((device) => !!srioVGpuDevices.find((s) => s.isEnabled && s.spec?.nodeName === device.spec?.nodeName));
} }

View File

@ -155,15 +155,6 @@ export default {
return location; return location;
}, },
viewTopology(group) {
const vpc = group.key;
const resource = this.$store.getters[`harvester/byId`](HCI.VPC, vpc);
if (resource && resource.goToDetail) {
resource.goToDetail();
}
},
showVpcAction(event, group) { showVpcAction(event, group) {
const vpc = group.key; const vpc = group.key;
@ -227,14 +218,6 @@ export default {
> >
{{ t('harvester.vpc.createSubnet') }} {{ t('harvester.vpc.createSubnet') }}
</router-link> </router-link>
<button
type="button"
class="btn btn-sm role-secondary mr-5"
@click="viewTopology(group)"
>
<i class="icon icon-globe mr-5" />
{{ t('harvester.vpc.viewTopology') }}
</button>
<button <button
type="button" type="button"
class="btn btn-sm role-multi-action actions mr-10" class="btn btn-sm role-multi-action actions mr-10"

View File

@ -110,12 +110,11 @@ export default {
data() { data() {
return { return {
hasNode: false, hasNode: false,
allVMs: [], allVMs: [],
allVMIs: [], allVMIs: [],
allNodeNetworks: [], allNodeNetworks: [],
allClusterNetworks: [], allClusterNetworks: [],
restartNotificationDisplayed: false,
HCI HCI
}; };
}, },
@ -175,48 +174,6 @@ export default {
this['allVMIs'] = vmis; this['allVMIs'] = vmis;
}, },
beforeUnmount() {
// clear restart message before component unmount
this.$store.dispatch('growl/clear');
},
watch: {
allVMs: {
handler(neu) {
const vmNames = [];
neu.forEach((vm) => {
if (vm.isRestartRequired) {
vmNames.push(vm.metadata.name);
}
});
const count = vmNames.length;
if ( count === 0 && this.restartNotificationDisplayed) {
this.restartNotificationDisplayed = false;
return;
}
if (count > 0) {
// clear old notification before showing new one
if (this.restartNotificationDisplayed) {
this.$store.dispatch('growl/clear');
}
}
if (count > 0 && vmNames.length > 0) {
this.$store.dispatch('growl/warning', {
title: this.t('harvester.notification.restartRequired.title', { count }),
message: this.t('harvester.notification.restartRequired.message', { vmNames: vmNames.join(', ') }),
timeout: 10000,
}, { root: true });
this.restartNotificationDisplayed = true;
}
},
deep: true,
}
},
methods: { methods: {
lockIconTooltipMessage(row) { lockIconTooltipMessage(row) {
const message = ''; const message = '';
@ -286,12 +243,6 @@ export default {
</div> </div>
</template> </template>
<style lang="scss">
.growl-container {
z-index: 56 !important; // set to be lower than the vm action menu (z-index: 57)
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.state { .state {
display: flex; display: flex;

View File

@ -1,60 +0,0 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import Loading from '@shell/components/Loading';
import { SCHEMA } from '@shell/config/types';
import { HCI } from '../types';
const schema = {
id: HCI.VMIMPORT_SOURCE_O,
type: SCHEMA,
attributes: {
kind: HCI.VMIMPORT_SOURCE_O,
namespaced: true
},
metadata: { name: HCI.VMIMPORT_SOURCE_O },
};
export default {
name: 'HarvesterVMImportSourceO',
components: { ResourceTable, Loading },
inheritAttrs: false,
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.rows = await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VMIMPORT_SOURCE_O });
const configSchema = this.$store.getters[`${ inStore }/schemaFor`](HCI.VMIMPORT_SOURCE_O);
if (!configSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
this.$store.dispatch('type-map/configureType', { match: HCI.VMIMPORT_SOURCE_O, isCreatable: false });
}
},
data() {
return { rows: [] };
},
computed: {
schema() {
return schema;
}
},
typeDisplay() {
return this.$store.getters['type-map/labelFor'](schema, 99);
}
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<ResourceTable
v-else
v-bind="$attrs"
:groupable="true"
:schema="schema"
:rows="rows"
key-field="_key"
/>
</template>

View File

@ -1,60 +0,0 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import Loading from '@shell/components/Loading';
import { SCHEMA } from '@shell/config/types';
import { HCI } from '../types';
const schema = {
id: HCI.VMIMPORT_SOURCE_OVA,
type: SCHEMA,
attributes: {
kind: HCI.VMIMPORT_SOURCE_OVA,
namespaced: true
},
metadata: { name: HCI.VMIMPORT_SOURCE_OVA },
};
export default {
name: 'HarvesterVMImportSourceOVA',
components: { ResourceTable, Loading },
inheritAttrs: false,
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.rows = await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VMIMPORT_SOURCE_OVA });
const configSchema = this.$store.getters[`${ inStore }/schemaFor`](HCI.VMIMPORT_SOURCE_OVA);
if (!configSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
this.$store.dispatch('type-map/configureType', { match: HCI.VMIMPORT_SOURCE_OVA, isCreatable: false });
}
},
data() {
return { rows: [] };
},
computed: {
schema() {
return schema;
}
},
typeDisplay() {
return this.$store.getters['type-map/labelFor'](schema, 99);
}
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<ResourceTable
v-else
v-bind="$attrs"
:groupable="true"
:schema="schema"
:rows="rows"
key-field="_key"
/>
</template>

View File

@ -1,60 +0,0 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import Loading from '@shell/components/Loading';
import { SCHEMA } from '@shell/config/types';
import { HCI } from '../types';
const schema = {
id: HCI.VMIMPORT,
type: SCHEMA,
attributes: {
kind: HCI.VMIMPORT,
namespaced: true
},
metadata: { name: HCI.VMIMPORT },
};
export default {
name: 'HarvesterVMImportVirtualMachine',
components: { ResourceTable, Loading },
inheritAttrs: false,
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.rows = await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VMIMPORT });
const configSchema = this.$store.getters[`${ inStore }/schemaFor`](HCI.VMIMPORT);
if (!configSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
this.$store.dispatch('type-map/configureType', { match: HCI.VMIMPORT, isCreatable: false });
}
},
data() {
return { rows: [] };
},
computed: {
schema() {
return schema;
}
},
typeDisplay() {
return this.$store.getters['type-map/labelFor'](schema, 99);
}
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<ResourceTable
v-else
v-bind="$attrs"
:groupable="true"
:schema="schema"
:rows="rows"
key-field="_key"
/>
</template>

View File

@ -1,60 +0,0 @@
<script>
import ResourceTable from '@shell/components/ResourceTable';
import Loading from '@shell/components/Loading';
import { SCHEMA } from '@shell/config/types';
import { HCI } from '../types';
const schema = {
id: HCI.VMIMPORT_SOURCE_V,
type: SCHEMA,
attributes: {
kind: HCI.VMIMPORT_SOURCE_V,
namespaced: true
},
metadata: { name: HCI.VMIMPORT_SOURCE_V },
};
export default {
name: 'HarvesterVMImportSourceV',
components: { ResourceTable, Loading },
inheritAttrs: false,
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.rows = await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VMIMPORT_SOURCE_V });
const configSchema = this.$store.getters[`${ inStore }/schemaFor`](HCI.VMIMPORT_SOURCE_V);
if (!configSchema?.collectionMethods.find((x) => x.toLowerCase() === 'post')) {
this.$store.dispatch('type-map/configureType', { match: HCI.VMIMPORT_SOURCE_V, isCreatable: false });
}
},
data() {
return { rows: [] };
},
computed: {
schema() {
return schema;
}
},
typeDisplay() {
return this.$store.getters['type-map/labelFor'](schema, 99);
}
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<ResourceTable
v-else
v-bind="$attrs"
:groupable="true"
:schema="schema"
:rows="rows"
key-field="_key"
/>
</template>

View File

@ -22,7 +22,7 @@ import {
} from '../../config/harvester-map'; } from '../../config/harvester-map';
import { HCI_SETTING } from '../../config/settings'; import { HCI_SETTING } from '../../config/settings';
import { HCI } from '../../types'; import { HCI } from '../../types';
import { parseVolumeClaimTemplates, EMPTY_IMAGE } from '../../utils/vm'; import { parseVolumeClaimTemplates } from '../../utils/vm';
import impl, { QGA_JSON, USB_TABLET } from './impl'; import impl, { QGA_JSON, USB_TABLET } from './impl';
import { GIBIBYTE } from '../../utils/unit'; import { GIBIBYTE } from '../../utils/unit';
import { VOLUME_MODE } from '@pkg/harvester/config/types'; import { VOLUME_MODE } from '@pkg/harvester/config/types';
@ -182,7 +182,6 @@ export default {
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW, immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
terminationGracePeriodSeconds: '', terminationGracePeriodSeconds: '',
cpuPinning: false, cpuPinning: false,
cpuModel: '',
}; };
}, },
@ -395,7 +394,6 @@ export default {
const efiPersistentStateEnabled = this.isEFIPersistentStateEnabled(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);
const cpuModel = spec.template.spec.domain.cpu?.model || '';
const secretRef = this.getSecret(spec); const secretRef = this.getSecret(spec);
const accessCredentials = this.getAccessCredentials(spec); const accessCredentials = this.getAccessCredentials(spec);
@ -433,7 +431,6 @@ export default {
this['tpmPersistentStateEnabled'] = tpmPersistentStateEnabled; this['tpmPersistentStateEnabled'] = tpmPersistentStateEnabled;
this['secureBoot'] = secureBoot; this['secureBoot'] = secureBoot;
this['cpuPinning'] = cpuPinning; this['cpuPinning'] = cpuPinning;
this['cpuModel'] = cpuModel;
this['hasCreateVolumes'] = hasCreateVolumes; this['hasCreateVolumes'] = hasCreateVolumes;
this['networkRows'] = networkRows; this['networkRows'] = networkRows;
@ -511,15 +508,12 @@ export default {
const type = DISK?.cdrom ? CD_ROM : DISK?.disk ? HARD_DISK : ''; const type = DISK?.cdrom ? CD_ROM : DISK?.disk ? HARD_DISK : '';
if (type === CD_ROM && volume === undefined) { if (volume?.containerDisk) { // SOURCE_TYPE.CONTAINER
// Empty CD_ROM
source = SOURCE_TYPE.IMAGE;
image = EMPTY_IMAGE;
size = `0${ GIBIBYTE }`;
} else if (volume.containerDisk) { // SOURCE_TYPE.CONTAINER
source = SOURCE_TYPE.CONTAINER; source = SOURCE_TYPE.CONTAINER;
container = volume.containerDisk.image; container = volume.containerDisk.image;
} else if (volume.persistentVolumeClaim && volume.persistentVolumeClaim?.claimName) { }
if (volume.persistentVolumeClaim && volume.persistentVolumeClaim?.claimName) {
volumeName = volume.persistentVolumeClaim.claimName; volumeName = volume.persistentVolumeClaim.claimName;
const DVT = _volumeClaimTemplates.find( (T) => T.metadata.name === volumeName); const DVT = _volumeClaimTemplates.find( (T) => T.metadata.name === volumeName);
@ -707,41 +701,25 @@ export default {
} }
}, },
needVolumeRelatedInfo(R) {
// return [needVolume, needVolumeClaimTemplate]
if (R.source === SOURCE_TYPE.CONTAINER) {
return [true, false];
}
if (R.source === SOURCE_TYPE.IMAGE && R.image === EMPTY_IMAGE) {
return [false, false];
}
return [true, true];
},
parseDiskRows(disk) { parseDiskRows(disk) {
const disks = []; const disks = [];
const volumes = []; const volumes = [];
const diskNameLabels = [];
const volumeClaimTemplates = []; const volumeClaimTemplates = [];
disk.forEach( (R, index) => { disk.forEach( (R, index) => {
const _disk = this.parseDisk(R, index);
disks.push(_disk);
const prefixName = this.value.metadata?.name || ''; const prefixName = this.value.metadata?.name || '';
const dataVolumeName = this.parseDataVolumeName(R, prefixName); const dataVolumeName = this.parseDataVolumeName(R, prefixName);
const [needVolume, needVolumeClaimTemplate] = this.needVolumeRelatedInfo(R);
if (needVolume) { const _disk = this.parseDisk(R, index);
const _volume = this.parseVolume(R, dataVolumeName); const _volume = this.parseVolume(R, dataVolumeName);
const _dataVolumeTemplate = this.parseVolumeClaimTemplate(R, dataVolumeName);
volumes.push(_volume); disks.push(_disk);
} volumes.push(_volume);
if (needVolumeClaimTemplate) { diskNameLabels.push(dataVolumeName);
const _dataVolumeTemplate = this.parseVolumeClaimTemplate(R, dataVolumeName);
if (R.source !== SOURCE_TYPE.CONTAINER) {
volumeClaimTemplates.push(_dataVolumeTemplate); volumeClaimTemplates.push(_dataVolumeTemplate);
} }
}); });

View File

@ -1,7 +1,6 @@
import SteveModel from '@shell/plugins/steve/steve-class'; import SteveModel from '@shell/plugins/steve/steve-class';
import { escapeHtml } from '@shell/utils/string'; import { escapeHtml } from '@shell/utils/string';
import { HCI } from '../types'; import { HCI } from '../types';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
const STATUS_DISPLAY = { const STATUS_DISPLAY = {
enabled: { enabled: {
@ -33,7 +32,7 @@ export default class PCIDevice extends SteveModel {
out.push( out.push(
{ {
action: 'enablePassthroughBulk', action: 'enablePassthroughBulk',
enabled: !this.isEnabling && !this.isvGPUDevice, enabled: !this.isEnabling,
icon: 'icon icon-fw icon-dot', icon: 'icon icon-fw icon-dot',
label: 'Enable Passthrough', label: 'Enable Passthrough',
bulkable: true, bulkable: true,
@ -42,7 +41,7 @@ export default class PCIDevice extends SteveModel {
}, },
{ {
action: 'disablePassthrough', action: 'disablePassthrough',
enabled: this.isEnabling && this.claimedByMe && !this.isvGPUDevice, enabled: this.isEnabling && this.claimedByMe,
icon: 'icon icon-fw icon-dot-open', icon: 'icon icon-fw icon-dot-open',
label: 'Disable Passthrough', label: 'Disable Passthrough',
bulkable: true, bulkable: true,
@ -53,14 +52,6 @@ export default class PCIDevice extends SteveModel {
return out; return out;
} }
get isvGPUDevice() {
if (!this.vGPUAsPCIDeviceFeatureEnabled) {
return false;
}
return !!this.metadata?.labels?.[HCI_ANNOTATIONS.PARENT_SRIOV_GPU];
}
get canYaml() { get canYaml() {
return false; return false;
} }
@ -153,12 +144,6 @@ export default class PCIDevice extends SteveModel {
// 'disable' passthrough deletes claim // 'disable' passthrough deletes claim
// backend should return error if device is in use // backend should return error if device is in use
async disablePassthrough() { async disablePassthrough() {
if (!this.allowDisable) {
this.showDetachWarning();
return;
}
try { try {
if (!this.claimedByMe) { if (!this.claimedByMe) {
throw new Error(this.$rootGetters['i18n/t']('harvester.pci.cantUnclaim', { name: escapeHtml(this.metadata.name) })); throw new Error(this.$rootGetters['i18n/t']('harvester.pci.cantUnclaim', { name: escapeHtml(this.metadata.name) }));
@ -184,24 +169,4 @@ export default class PCIDevice extends SteveModel {
get groupByDevice() { get groupByDevice() {
return this.status?.description; return this.status?.description;
} }
get vGPUAsPCIDeviceFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('vGPUAsPCIDevice');
}
showDetachWarning() {
this.$dispatch('growl/warning', {
title: this.$rootGetters['i18n/t']('harvester.pci.detachWarning.title'),
message: this.$rootGetters['i18n/t']('harvester.pci.detachWarning.message'),
timeout: 5000
}, { root: true });
}
get allowDisable() {
return this._allowDisable;
}
set allowDisable(value) {
this._allowDisable = value;
}
} }

View File

@ -133,12 +133,6 @@ export default class USBDevice extends SteveModel {
// 'disable' passthrough deletes claim // 'disable' passthrough deletes claim
// backend should return error if device is in use // backend should return error if device is in use
async disablePassthrough() { async disablePassthrough() {
if (!this.allowDisable) {
this.showDetachWarning();
return;
}
try { try {
if (!this.claimedByMe) { if (!this.claimedByMe) {
throw new Error(this.$rootGetters['i18n/t']('harvester.usb.cantUnclaim', { name: escapeHtml(this.metadata.name) })); throw new Error(this.$rootGetters['i18n/t']('harvester.usb.cantUnclaim', { name: escapeHtml(this.metadata.name) }));
@ -164,20 +158,4 @@ export default class USBDevice extends SteveModel {
get groupByDevice() { get groupByDevice() {
return this.status?.description; return this.status?.description;
} }
showDetachWarning() {
this.$dispatch('growl/warning', {
title: this.$rootGetters['i18n/t']('harvester.usb.detachWarning.title'),
message: this.$rootGetters['i18n/t']('harvester.usb.detachWarning.message'),
timeout: 5000
}, { root: true });
}
get allowDisable() {
return this._allowDisable;
}
set allowDisable(value) {
this._allowDisable = value;
}
} }

View File

@ -100,12 +100,6 @@ export default class VGpuDevice extends SteveModel {
} }
async disableVGpu() { async disableVGpu() {
if (!this.allowDisable) {
this.showDetachWarning();
return;
}
const { vGPUTypeName, enabled } = this.spec; const { vGPUTypeName, enabled } = this.spec;
try { try {
@ -132,20 +126,4 @@ export default class VGpuDevice extends SteveModel {
get vGpuAvailableTypes() { get vGpuAvailableTypes() {
return this.status?.availableTypes ? Object.keys(this.status.availableTypes) : []; return this.status?.availableTypes ? Object.keys(this.status.availableTypes) : [];
} }
showDetachWarning() {
this.$dispatch('growl/warning', {
title: this.$rootGetters['i18n/t']('harvester.vgpu.detachWarning.title'),
message: this.$rootGetters['i18n/t']('harvester.vgpu.detachWarning.message'),
timeout: 5000
}, { root: true });
}
get allowDisable() {
return this._allowDisable;
}
set allowDisable(value) {
this._allowDisable = value;
}
} }

View File

@ -536,7 +536,8 @@ export default class HciNode extends HarvesterResource {
get isStopped() { get isStopped() {
const inventory = this.inventory || {}; const inventory = this.inventory || {};
return inventory.status?.machinePowerState === 'off'; return inventory.spec?.powerActionRequested === 'shutdown' &&
inventory.status?.powerAction?.actionStatus === 'complete';
} }
get isStopping() { get isStopping() {
@ -552,7 +553,8 @@ export default class HciNode extends HarvesterResource {
get isStarted() { get isStarted() {
const inventory = this.inventory || {}; const inventory = this.inventory || {};
return inventory.status?.machinePowerState === 'on'; return inventory.spec?.powerActionRequested === 'poweron' &&
inventory.status?.powerAction?.actionStatus === 'complete';
} }
get isStarting() { get isStarting() {

View File

@ -52,19 +52,11 @@ export default class HciSetting extends HarvesterResource {
}); });
} }
get clusterRegistrationTLSVerifyFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('clusterRegistrationTLSVerify');
}
get customValue() { get customValue() {
if (this.metadata.name === HCI_SETTING.STORAGE_NETWORK) { if (this.metadata.name === HCI_SETTING.STORAGE_NETWORK) {
try { try {
return JSON.stringify(JSON.parse(this.value), null, 2); return JSON.stringify(JSON.parse(this.value), null, 2);
} catch (e) {} } catch (e) {}
} else if (this.metadata.name === HCI_SETTING.CLUSTER_REGISTRATION_URL) {
try {
return this.clusterRegistrationTLSVerifyFeatureEnabled ? JSON.stringify(JSON.parse(this.value), null, 2) : this.value;
} catch (e) {}
} }
return false; return false;

View File

@ -153,7 +153,7 @@ export default class VirtVm extends HarvesterResource {
}, },
{ {
action: 'takeVMSnapshot', action: 'takeVMSnapshot',
enabled: (!!this.actions?.snapshot || !!this.actions?.backup), enabled: (!!this.actions?.snapshot || !!this.action?.backup),
icon: 'icon icon-snapshot', icon: 'icon icon-snapshot',
label: this.t('harvester.action.vmSnapshot') label: this.t('harvester.action.vmSnapshot')
}, },
@ -183,7 +183,7 @@ export default class VirtVm extends HarvesterResource {
}, },
{ {
action: 'ejectCDROM', action: 'ejectCDROM',
enabled: !this.hotplugCdRomEnabled && !!this.actions?.ejectCdRom, enabled: !!this.actions?.ejectCdRom,
icon: 'icon icon-delete', icon: 'icon icon-delete',
label: this.t('harvester.action.ejectCDROM') label: this.t('harvester.action.ejectCDROM')
}, },
@ -199,18 +199,6 @@ export default class VirtVm extends HarvesterResource {
icon: 'icon icon-close', icon: 'icon icon-close',
label: this.t('harvester.action.abortMigration') label: this.t('harvester.action.abortMigration')
}, },
{
action: 'storageMigration',
enabled: !!this.actions?.storageMigration,
icon: 'icon icon-copy',
label: this.t('harvester.action.storageMigration')
},
{
action: 'cancelStorageMigration',
enabled: !!this.actions?.cancelStorageMigration,
icon: 'icon icon-close',
label: this.t('harvester.action.cancelStorageMigration')
},
{ {
action: 'addHotplugVolume', action: 'addHotplugVolume',
enabled: !!this.actions?.addVolume, enabled: !!this.actions?.addVolume,
@ -380,13 +368,6 @@ export default class VirtVm extends HarvesterResource {
}); });
} }
storageMigration(resources = this) {
this.$dispatch('promptModal', {
resources,
component: 'HarvesterStorageMigrationDialog'
});
}
backupVM(resources = this) { backupVM(resources = this) {
this.$dispatch('promptModal', { this.$dispatch('promptModal', {
resources, resources,
@ -420,17 +401,6 @@ export default class VirtVm extends HarvesterResource {
}); });
} }
ejectCdRomVolume(diskName) {
const resources = this;
this.$dispatch('promptModal', {
resources,
name: diskName,
type: 'cdrom',
component: 'HarvesterHotUnplug',
});
}
unplugNIC(networkName) { unplugNIC(networkName) {
const resources = this; const resources = this;
@ -539,10 +509,6 @@ export default class VirtVm extends HarvesterResource {
this.doActionGrowl('abortMigration', {}); this.doActionGrowl('abortMigration', {});
} }
cancelStorageMigration() {
this.doActionGrowl('cancelStorageMigration', {});
}
createTemplate(resources = this) { createTemplate(resources = this) {
this.$dispatch('promptModal', { this.$dispatch('promptModal', {
resources, resources,
@ -557,16 +523,6 @@ export default class VirtVm extends HarvesterResource {
}); });
} }
insertCdRomVolume(diskName) {
const resources = this;
this.$dispatch('promptModal', {
resources,
name: diskName,
component: 'HarvesterInsertCdRomVolume',
});
}
addHotplugNic(resources = this) { addHotplugNic(resources = this) {
this.$dispatch('promptModal', { this.$dispatch('promptModal', {
resources, resources,
@ -793,11 +749,11 @@ export default class VirtVm extends HarvesterResource {
} }
get isPending() { get isPending() {
if ((this && if (this &&
!this.isVMExpectedRunning && !this.isVMExpectedRunning &&
this.isVMCreated && this.isVMCreated &&
this.vmi?.status?.phase === VMIPhase.Pending this.vmi?.status?.phase === VMIPhase.Pending
) || (this.metadata?.annotations?.[HCI_ANNOTATIONS.CLONE_BACKEND_STORAGE_STATUS] === 'cloning')) { ) {
return { status: VMIPhase.Pending }; return { status: VMIPhase.Pending };
} }
@ -1224,15 +1180,11 @@ export default class VirtVm extends HarvesterResource {
); );
} }
get isRestartRequired() { get stateDescription() {
const conditions = get(this, 'status.conditions'); const conditions = get(this, 'status.conditions');
const restartRequired = findBy(conditions, 'type', 'RestartRequired'); const restartRequired = findBy(conditions, 'type', 'RestartRequired');
return restartRequired && restartRequired.status === 'True'; if (restartRequired && restartRequired.status === 'True') {
}
get stateDescription() {
if (this.isRestartRequired) {
return this.t('harvester.virtualMachine.hotplug.restartVMMessage'); return this.t('harvester.virtualMachine.hotplug.restartVMMessage');
} }
@ -1311,10 +1263,6 @@ export default class VirtVm extends HarvesterResource {
return this.$rootGetters['harvester-common/getFeatureEnabled']('hotplugNic'); return this.$rootGetters['harvester-common/getFeatureEnabled']('hotplugNic');
} }
get hotplugCdRomEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('hotplugCdRom');
}
get isBackupTargetUnavailable() { get isBackupTargetUnavailable() {
const allSettings = this.$rootGetters['harvester/all'](HCI.SETTING) || []; const allSettings = this.$rootGetters['harvester/all'](HCI.SETTING) || [];
const backupTargetSetting = allSettings.find( (O) => O.id === 'backup-target'); const backupTargetSetting = allSettings.find( (O) => O.id === 'backup-target');

View File

@ -1,13 +1,13 @@
{ {
"name": "harvester", "name": "harvester",
"description": "Rancher UI Extension for Harvester", "description": "Rancher UI Extension for Harvester",
"version": "1.8.0-rc2", "version": "1.7.1",
"private": false, "private": false,
"rancher": { "rancher": {
"annotations": { "annotations": {
"catalog.cattle.io/display-name": "Harvester", "catalog.cattle.io/display-name": "Harvester",
"catalog.cattle.io/kube-version": ">= 1.16.0-0", "catalog.cattle.io/kube-version": ">= 1.16.0-0",
"catalog.cattle.io/rancher-version": ">= 2.14.0-0", "catalog.cattle.io/rancher-version": ">= 2.13.0-0",
"catalog.cattle.io/ui-extensions-version": ">= 3.0.0 < 4.0.0" "catalog.cattle.io/ui-extensions-version": ">= 3.0.0 < 4.0.0"
} }
}, },

View File

@ -98,11 +98,6 @@ export default {
if (getters['schemaFor'](HCI.UPGRADE)) { if (getters['schemaFor'](HCI.UPGRADE)) {
hash.upgrades = dispatch('findAll', { type: HCI.UPGRADE }); hash.upgrades = dispatch('findAll', { type: HCI.UPGRADE });
} }
// Pre-fetch all HCI.UPGRADE_LOG data within loadCluster to ensure HarvesterUpgradeHeader has the necessary data. This is required because the header is dynamically loaded before the user enters the cluster in Rancher integration mode.
// See more details in https://github.com/harvester/harvester-ui-extension/pull/715
if (getters['schemaFor'](HCI.UPGRADE_LOG)) {
hash.upgradeLogs = dispatch('findAll', { type: HCI.UPGRADE_LOG });
}
const res: any = await allHash(hash); const res: any = await allHash(hash);

View File

@ -1,4 +0,0 @@
@import '@vue-flow/core/dist/style.css';
@import '@vue-flow/core/dist/theme-default.css';
@import '@vue-flow/controls/dist/style.css';
@import '@vue-flow/minimap/dist/style.css';

View File

@ -18,7 +18,6 @@ export const HCI = {
CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork', CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork',
SUBNET: 'kubeovn.io.subnet', SUBNET: 'kubeovn.io.subnet',
VPC: 'kubeovn.io.vpc', VPC: 'kubeovn.io.vpc',
IP: 'kubeovn.io.ip',
VM_IMAGE_DOWNLOADER: 'harvesterhci.io.virtualmachineimagedownloader', VM_IMAGE_DOWNLOADER: 'harvesterhci.io.virtualmachineimagedownloader',
SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle', SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle',
NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition', NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',
@ -57,11 +56,6 @@ export const HCI = {
IP_POOL: 'loadbalancer.harvesterhci.io.ippool', IP_POOL: 'loadbalancer.harvesterhci.io.ippool',
HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig', HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig',
LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup', LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup',
VMIMPORT_SOURCE_V: 'migration.harvesterhci.io.vmwaresource',
VMIMPORT_SOURCE_O: 'migration.harvesterhci.io.openstacksource',
VMIMPORT_SOURCE_OVA: 'migration.harvesterhci.io.ovasource',
VMIMPORT: 'migration.harvesterhci.io.virtualmachineimport',
MIGRATION: 'migration.harvesterhci.io',
}; };
export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot'; export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot';

View File

@ -1,99 +0,0 @@
/**
* Dynamically toggles SideNav entries based on the enabled status of a specific Addon.
*
* @param {Object} store - The Vuex store instance.
* @param {String} productName - The product name (e.g. 'harvester').
* @param {Object} config - Configuration object.
* @param {String} config.addonName - The name of the addon to watch.
* @param {String} config.resourceType - The schema ID for addons.
* @param {String} config.navGroup - The group name in the side nav.
* @param {Array<String>} config.types - Array of Resource IDs to show/hide.
*/
export function registerAddonSideNav(store, productName, {
addonName, resourceType, navGroup, types
}) {
if (typeof window === 'undefined') {
return;
}
// Forces the SideNav component to re-render by toggling a dummy user preference.
// Necessary because the menu component does not automatically detect
// changes to the allowed types list.
const kickSideNav = () => {
const TRIGGER = 'ui.refresh.trigger';
store.dispatch('type-map/addFavorite', TRIGGER);
// SideNav component seem to ignore rapid state changes.
// Wait 600ms to ensure the toggle event triggers a re-render.
setTimeout(() => {
store.dispatch('type-map/removeFavorite', TRIGGER);
}, 600);
};
// Adds or removes the resource IDs from the product visibility whitelist.
const setMenuVisibility = (visible) => {
if (visible) {
store.commit('type-map/basicType', {
product: productName,
group: navGroup,
types
});
} else {
// Manually delete the keys from the state object to hide them.
const basicTypes = store.state['type-map'].basicTypes[productName];
if (basicTypes) {
types.forEach((t) => delete basicTypes[t]);
}
}
kickSideNav();
};
// Start polling to check if the store is ready.
let attempts = 0;
const MAX_ATTEMPTS = 60;
const waitForStore = setInterval(() => {
attempts++;
try {
// Check if the Schema definitions are loaded.
const hasSchema = store.getters[`${ productName }/schemaFor`] &&
store.getters[`${ productName }/schemaFor`](resourceType);
// Check if the resource list data is fully loaded to prevent race conditions.
const hasData = store.getters[`${ productName }/haveAll`] &&
store.getters[`${ productName }/haveAll`](resourceType);
if (hasSchema && hasData) {
// Store is ready. Stop polling.
clearInterval(waitForStore);
// Watch the specific addon resource for changes to its enabled status.
store.watch(
(state, getters) => {
const addons = getters[`${ productName }/all`](resourceType);
const addon = addons.find((a) => a.metadata.name === addonName);
return addon?.spec?.enabled === true;
},
(isEnabled) => {
setMenuVisibility(isEnabled);
},
{ immediate: true, deep: true }
);
} else if (hasSchema && !hasData) {
// If the schema is ready but the data is missing, request the list from the API.
// Ensures the script does not wait indefinitely if the UI has not loaded the addons yet.
store.dispatch(`${ productName }/findAll`, { type: resourceType });
} else if (attempts >= MAX_ATTEMPTS) {
// Stop checking if the store does not load within the timeout limit.
clearInterval(waitForStore);
}
} catch (e) {
// Ignore errors if the store module is not yet registered and wait for the next attempt.
if (attempts >= MAX_ATTEMPTS) clearInterval(waitForStore);
}
}, 1000);
}

View File

@ -9,5 +9,3 @@ export function parseVolumeClaimTemplates(data) {
return out; return out;
} }
export const EMPTY_IMAGE = 'EMPTY_IMAGE';

View File

@ -69,7 +69,7 @@ export function vmDisks(spec, getters, errors, validatorArgs, displayKey, value)
validName(getters, errors, D.name, diskNames, prefix, type, lowerType, upperType); validName(getters, errors, D.name, diskNames, prefix, type, lowerType, upperType);
}); });
let hasBootableVolume = false; let requiredVolume = false;
_volumes.forEach((V, idx) => { _volumes.forEach((V, idx) => {
const { type, typeValue } = getVolumeType(getters, V, _volumeClaimTemplates, value); const { type, typeValue } = getVolumeType(getters, V, _volumeClaimTemplates, value);
@ -77,7 +77,7 @@ export function vmDisks(spec, getters, errors, validatorArgs, displayKey, value)
const prefix = V.name || idx + 1; const prefix = V.name || idx + 1;
if ([SOURCE_TYPE.IMAGE, SOURCE_TYPE.ATTACH_VOLUME, SOURCE_TYPE.CONTAINER].includes(type)) { if ([SOURCE_TYPE.IMAGE, SOURCE_TYPE.ATTACH_VOLUME, SOURCE_TYPE.CONTAINER].includes(type)) {
hasBootableVolume = true; requiredVolume = true;
} }
if (type === SOURCE_TYPE.NEW || type === SOURCE_TYPE.IMAGE) { if (type === SOURCE_TYPE.NEW || type === SOURCE_TYPE.IMAGE) {
@ -137,10 +137,10 @@ export function vmDisks(spec, getters, errors, validatorArgs, displayKey, value)
}); });
/** /**
* At least one bootable volume must be provided. (Verify only when create.) * At least one volume must be create. (Verify only when create.)
*/ */
if (!hasBootableVolume && !value.links) { if ((!requiredVolume || _volumes.length === 0) && !value.links) {
errors.push(getters['i18n/t']('harvester.validation.vm.volume.needAtLeastOneBootable')); errors.push(getters['i18n/t']('harvester.validation.vm.volume.needImageOrExisting'));
} }
return errors; return errors;

486
yarn.lock
View File

@ -11,6 +11,14 @@
event-pubsub "4.3.0" event-pubsub "4.3.0"
js-message "1.0.7" js-message "1.0.7"
"@ampproject/remapping@^2.2.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
dependencies:
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
"@aws-crypto/sha256-browser@5.2.0": "@aws-crypto/sha256-browser@5.2.0":
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e"
@ -919,7 +927,7 @@
dependencies: dependencies:
"@babel/highlight" "^7.10.4" "@babel/highlight" "^7.10.4"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.8.3": "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.8.3":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7"
integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q== integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==
@ -928,33 +936,33 @@
js-tokens "^4.0.0" js-tokens "^4.0.0"
picocolors "^1.1.1" picocolors "^1.1.1"
"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.4", "@babel/compat-data@^7.28.6": "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.2", "@babel/compat-data@^7.25.4":
version "7.28.6" version "7.25.4"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.6.tgz#103f466803fa0f059e82ccac271475470570d74c" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb"
integrity sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg== integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==
"@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.7.2", "@babel/core@^7.8.0": "@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.7.2", "@babel/core@^7.7.5", "@babel/core@^7.8.0":
version "7.28.6" version "7.25.2"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.6.tgz#531bf883a1126e53501ba46eb3bb414047af507f" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77"
integrity sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw== integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==
dependencies: dependencies:
"@babel/code-frame" "^7.28.6" "@ampproject/remapping" "^2.2.0"
"@babel/generator" "^7.28.6" "@babel/code-frame" "^7.24.7"
"@babel/helper-compilation-targets" "^7.28.6" "@babel/generator" "^7.25.0"
"@babel/helper-module-transforms" "^7.28.6" "@babel/helper-compilation-targets" "^7.25.2"
"@babel/helpers" "^7.28.6" "@babel/helper-module-transforms" "^7.25.2"
"@babel/parser" "^7.28.6" "@babel/helpers" "^7.25.0"
"@babel/template" "^7.28.6" "@babel/parser" "^7.25.0"
"@babel/traverse" "^7.28.6" "@babel/template" "^7.25.0"
"@babel/types" "^7.28.6" "@babel/traverse" "^7.25.2"
"@jridgewell/remapping" "^2.3.5" "@babel/types" "^7.25.2"
convert-source-map "^2.0.0" convert-source-map "^2.0.0"
debug "^4.1.0" debug "^4.1.0"
gensync "^1.0.0-beta.2" gensync "^1.0.0-beta.2"
json5 "^2.2.3" json5 "^2.2.3"
semver "^6.3.1" semver "^6.3.1"
"@babel/generator@^7.28.6", "@babel/generator@^7.7.2": "@babel/generator@^7.25.0", "@babel/generator@^7.28.6", "@babel/generator@^7.7.2":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1"
integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw== integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==
@ -980,14 +988,14 @@
"@babel/traverse" "^7.24.7" "@babel/traverse" "^7.24.7"
"@babel/types" "^7.24.7" "@babel/types" "^7.24.7"
"@babel/helper-compilation-targets@^7.12.16", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7", "@babel/helper-compilation-targets@^7.24.8", "@babel/helper-compilation-targets@^7.25.2", "@babel/helper-compilation-targets@^7.28.6": "@babel/helper-compilation-targets@^7.12.16", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7", "@babel/helper-compilation-targets@^7.24.8", "@babel/helper-compilation-targets@^7.25.2":
version "7.28.6" version "7.25.2"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c"
integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==
dependencies: dependencies:
"@babel/compat-data" "^7.28.6" "@babel/compat-data" "^7.25.2"
"@babel/helper-validator-option" "^7.27.1" "@babel/helper-validator-option" "^7.24.8"
browserslist "^4.24.0" browserslist "^4.23.1"
lru-cache "^5.1.1" lru-cache "^5.1.1"
semver "^6.3.1" semver "^6.3.1"
@ -1037,13 +1045,13 @@
"@babel/traverse" "^7.28.5" "@babel/traverse" "^7.28.5"
"@babel/types" "^7.28.5" "@babel/types" "^7.28.5"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.24.7", "@babel/helper-module-imports@^7.28.6": "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.24.7":
version "7.28.6" version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b"
integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==
dependencies: dependencies:
"@babel/traverse" "^7.28.6" "@babel/traverse" "^7.24.7"
"@babel/types" "^7.28.6" "@babel/types" "^7.24.7"
"@babel/helper-module-imports@~7.22.15": "@babel/helper-module-imports@~7.22.15":
version "7.22.15" version "7.22.15"
@ -1052,14 +1060,15 @@
dependencies: dependencies:
"@babel/types" "^7.22.15" "@babel/types" "^7.22.15"
"@babel/helper-module-transforms@^7.24.7", "@babel/helper-module-transforms@^7.24.8", "@babel/helper-module-transforms@^7.25.0", "@babel/helper-module-transforms@^7.28.6": "@babel/helper-module-transforms@^7.24.7", "@babel/helper-module-transforms@^7.24.8", "@babel/helper-module-transforms@^7.25.0", "@babel/helper-module-transforms@^7.25.2":
version "7.28.6" version "7.25.2"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6"
integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==
dependencies: dependencies:
"@babel/helper-module-imports" "^7.28.6" "@babel/helper-module-imports" "^7.24.7"
"@babel/helper-validator-identifier" "^7.28.5" "@babel/helper-simple-access" "^7.24.7"
"@babel/traverse" "^7.28.6" "@babel/helper-validator-identifier" "^7.24.7"
"@babel/traverse" "^7.25.2"
"@babel/helper-optimise-call-expression@^7.27.1": "@babel/helper-optimise-call-expression@^7.27.1":
version "7.27.1" version "7.27.1"
@ -1117,10 +1126,10 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
"@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.24.8", "@babel/helper-validator-option@^7.27.1": "@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.24.8":
version "7.27.1" version "7.24.8"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d"
integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==
"@babel/helper-wrap-function@^7.25.0": "@babel/helper-wrap-function@^7.25.0":
version "7.25.0" version "7.25.0"
@ -1131,13 +1140,13 @@
"@babel/traverse" "^7.25.0" "@babel/traverse" "^7.25.0"
"@babel/types" "^7.25.0" "@babel/types" "^7.25.0"
"@babel/helpers@^7.28.6": "@babel/helpers@^7.25.0":
version "7.28.6" version "7.25.6"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.6.tgz#57ee60141829ba2e102f30711ffe3afab357cc60"
integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== integrity sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==
dependencies: dependencies:
"@babel/template" "^7.28.6" "@babel/template" "^7.25.0"
"@babel/types" "^7.28.6" "@babel/types" "^7.25.6"
"@babel/highlight@^7.10.4": "@babel/highlight@^7.10.4":
version "7.24.7" version "7.24.7"
@ -1149,7 +1158,7 @@
js-tokens "^4.0.0" js-tokens "^4.0.0"
picocolors "^1.0.0" picocolors "^1.0.0"
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.28.0", "@babel/parser@^7.28.6", "@babel/parser@^7.7.0": "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.28.0", "@babel/parser@^7.28.6", "@babel/parser@^7.7.0":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd"
integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ== integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==
@ -1942,7 +1951,7 @@
"@babel/parser" "^7.28.6" "@babel/parser" "^7.28.6"
"@babel/types" "^7.28.6" "@babel/types" "^7.28.6"
"@babel/traverse@^7.23.9", "@babel/traverse@^7.24.7", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": "@babel/traverse@^7.23.9", "@babel/traverse@^7.24.7", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e"
integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg== integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==
@ -1955,7 +1964,7 @@
"@babel/types" "^7.28.6" "@babel/types" "^7.28.6"
debug "^4.3.1" debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": "@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df"
integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg== integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==
@ -2260,7 +2269,7 @@
js-yaml "^3.13.1" js-yaml "^3.13.1"
resolve-from "^5.0.0" resolve-from "^5.0.0"
"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": "@istanbuljs/schema@^0.1.2":
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
@ -2442,14 +2451,6 @@
"@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/sourcemap-codec" "^1.5.0"
"@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/remapping@^2.3.5":
version "2.3.5"
resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
dependencies:
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/resolve-uri@^3.1.0": "@jridgewell/resolve-uri@^3.1.0":
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
@ -2633,15 +2634,15 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@rancher/icons@2.0.55": "@rancher/icons@2.0.53":
version "2.0.55" version "2.0.53"
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.55.tgz#394159bddbf786c17a12bc38e59b88346b30dcf4" resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.53.tgz#0cbfd0f7d16bd8c99683654d83de99e77d2424c9"
integrity sha512-hPcmsvfYNO36dJ7/lb3JbJC5BOnHbwBylic7HRqhSCSIcFlFaKnRt9aB/hvv3ip0Wo6J5yVEv2o7oXGErXkWFw== integrity sha512-FkJsVZihlbZiaXI5E42W05jQGlV8HRUrWbIK2zg2JkGCLUO3mvratLGL2Yjx8dFz34y37h11DsH9+nFPoHuppA==
"@rancher/shell@3.0.9-rc.6": "@rancher/shell@3.0.8-rc.8":
version "3.0.9-rc.6" version "3.0.8-rc.8"
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.9-rc.6.tgz#d22add189481368a07df54c61d437f024236f639" resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.8-rc.8.tgz#19f316cdad1c4d9c828880d908eb4c2273961e24"
integrity sha512-qhTq7Ohsm/pToKRh010KVWLS4gH9g5BP55Ih0T62ZfTMIpO5JjiJkL/EtrRwlll47NKU1GHZ+lqh9yb3HJgDBg== integrity sha512-cAIZL755HCjHNjfkIdG8DgresnRqv2TV2KpIkhMTgz6c8wdjozBvnrtz3dMaPfwfKtnGT+wUN+wbN0rjfQ1vug==
dependencies: dependencies:
"@aws-sdk/client-ec2" "3.863.0" "@aws-sdk/client-ec2" "3.863.0"
"@aws-sdk/client-eks" "3.879.0" "@aws-sdk/client-eks" "3.879.0"
@ -2653,7 +2654,7 @@
"@babel/preset-typescript" "7.16.7" "@babel/preset-typescript" "7.16.7"
"@novnc/novnc" "1.2.0" "@novnc/novnc" "1.2.0"
"@popperjs/core" "2.11.8" "@popperjs/core" "2.11.8"
"@rancher/icons" "2.0.55" "@rancher/icons" "2.0.53"
"@smithy/fetch-http-handler" "5.1.1" "@smithy/fetch-http-handler" "5.1.1"
"@types/is-url" "1.2.30" "@types/is-url" "1.2.30"
"@types/node" "20.10.8" "@types/node" "20.10.8"
@ -2667,22 +2668,22 @@
"@vue/vue3-jest" "27.0.0" "@vue/vue3-jest" "27.0.0"
add "2.0.6" add "2.0.6"
ansi_up "5.0.0" ansi_up "5.0.0"
axios "1.13.2" axios "1.12.2"
axios-retry "3.1.9" axios-retry "3.1.9"
babel-eslint "10.1.0" babel-eslint "10.1.0"
babel-plugin-module-resolver "5.0.2" babel-plugin-module-resolver "4.0.0"
babel-preset-vue "2.0.2" babel-preset-vue "2.0.2"
cache-loader "4.1.0" cache-loader "4.1.0"
chart.js "4.5.1" chart.js "4.4.8"
clipboard-polyfill "4.0.1" clipboard-polyfill "4.0.1"
codemirror ">=5.64.0 <6" codemirror ">=5.64.0 <6"
codemirror-editor-vue3 "2.8.0" codemirror-editor-vue3 "2.8.0"
color "5.0.3" color "4.2.3"
cookie "0.7.0" cookie "0.7.0"
cookie-universal "2.2.2" cookie-universal "2.2.2"
core-js "3.45.0" core-js "3.45.0"
cron-validator "1.4.0" cron-validator "1.4.0"
cronstrue "3.9.0" cronstrue "2.53.0"
cross-env "7.0.3" cross-env "7.0.3"
css-loader "6.7.3" css-loader "6.7.3"
csv-loader "3.0.3" csv-loader "3.0.3"
@ -2691,7 +2692,7 @@
d3 "7.3.0" d3 "7.3.0"
d3-selection "3.0.0" d3-selection "3.0.0"
dayjs "1.11.18" dayjs "1.11.18"
defu "6.1.4" defu "5.0.1"
diff2html "3.4.24" diff2html "3.4.24"
dompurify "3.2.5" dompurify "3.2.5"
element-matches "^0.1.2" element-matches "^0.1.2"
@ -2720,17 +2721,17 @@
jexl "2.3.0" jexl "2.3.0"
jquery "3.5.1" jquery "3.5.1"
js-cookie "3.0.5" js-cookie "3.0.5"
js-yaml "4.1.1" js-yaml "4.1.0"
js-yaml-loader "1.2.2" js-yaml-loader "1.2.2"
jsdiff "1.1.1" jsdiff "1.1.1"
jsonpath-plus "10.3.0" jsonpath-plus "10.3.0"
jsrsasign "11.0.0" jsrsasign "11.0.0"
jszip "3.10.1" jszip "3.10.1"
lodash "4.17.23" lodash "4.17.21"
marked "4.0.17" marked "4.0.17"
node-polyfill-webpack-plugin "3.0.0" node-polyfill-webpack-plugin "3.0.0"
nodemon "2.0.22" nodemon "2.0.22"
nyc "17.1.0" nyc "15.1.0"
papaparse "5.3.0" papaparse "5.3.0"
portal-vue "~3.0.0" portal-vue "~3.0.0"
sass "1.89.2" sass "1.89.2"
@ -2755,7 +2756,7 @@
vuedraggable "4.1.0" vuedraggable "4.1.0"
vuex "4.1.0" vuex "4.1.0"
webpack-bundle-analyzer "4.10.2" webpack-bundle-analyzer "4.10.2"
webpack-virtual-modules "0.6.2" webpack-virtual-modules "0.4.3"
worker-loader "3.0.8" worker-loader "3.0.8"
xterm "5.2.1" xterm "5.2.1"
xterm-addon-canvas "0.5.0" xterm-addon-canvas "0.5.0"
@ -3429,10 +3430,10 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/lodash@4.17.24": "@types/lodash@4.17.23":
version "4.17.24" version "4.17.23"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.23.tgz#c1bb06db218acc8fc232da0447473fc2fb9d9841"
integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ== integrity sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==
"@types/mime@^1": "@types/mime@^1":
version "1.3.5" version "1.3.5"
@ -3452,9 +3453,9 @@
"@types/node" "*" "@types/node" "*"
"@types/node@*", "@types/node@20.10.8", "@types/node@^14.14.31", "@types/node@~20.19.0": "@types/node@*", "@types/node@20.10.8", "@types/node@^14.14.31", "@types/node@~20.19.0":
version "20.19.37" version "20.19.33"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.37.tgz#b4fb4033408dd97becce63ec932c9ec57a9e2919" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.33.tgz#ac8364c623b72d43125f0e7dd722bbe968f0c65e"
integrity sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw== integrity sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==
dependencies: dependencies:
undici-types "~6.21.0" undici-types "~6.21.0"
@ -3559,11 +3560,6 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba"
integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==
"@types/web-bluetooth@^0.0.20":
version "0.0.20"
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597"
integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==
"@types/webpack-env@^1.15.2": "@types/webpack-env@^1.15.2":
version "1.18.5" version "1.18.5"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.5.tgz#eccda0b04fe024bed505881e2e532f9c119169bf" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.18.5.tgz#eccda0b04fe024bed505881e2e532f9c119169bf"
@ -3725,35 +3721,6 @@
"@typescript-eslint/types" "5.62.0" "@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@vue-flow/background@^1.3.0":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778"
integrity sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==
"@vue-flow/controls@^1.1.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@vue-flow/controls/-/controls-1.1.3.tgz#40866b553101fbef22d2b9a043965ed76fca4b2c"
integrity sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==
"@vue-flow/core@^1.33.5":
version "1.48.2"
resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.48.2.tgz#cef8641b17f6220c257d4208bdb2082cee882225"
integrity sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==
dependencies:
"@vueuse/core" "^10.5.0"
d3-drag "^3.0.0"
d3-interpolate "^3.0.1"
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
"@vue-flow/minimap@^1.4.0":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@vue-flow/minimap/-/minimap-1.5.4.tgz#c9c3badea49d4166aa9cdc713017397d9df7574c"
integrity sha512-l4C+XTAXnRxsRpUdN7cAVFBennC1sVRzq4bDSpVK+ag7tdMczAnhFYGgbLkUw3v3sY6gokyWwMl8CDonp8eB2g==
dependencies:
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
"@vue/babel-helper-vue-jsx-merge-props@^1.4.0": "@vue/babel-helper-vue-jsx-merge-props@^1.4.0":
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2" resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2"
@ -4149,28 +4116,6 @@
resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a" resolved "https://registry.yarnpkg.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a"
integrity sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA== integrity sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==
"@vueuse/core@^10.5.0":
version "10.11.1"
resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.11.1.tgz#15d2c0b6448d2212235b23a7ba29c27173e0c2c6"
integrity sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==
dependencies:
"@types/web-bluetooth" "^0.0.20"
"@vueuse/metadata" "10.11.1"
"@vueuse/shared" "10.11.1"
vue-demi ">=0.14.8"
"@vueuse/metadata@10.11.1":
version "10.11.1"
resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.11.1.tgz#209db7bb5915aa172a87510b6de2ca01cadbd2a7"
integrity sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==
"@vueuse/shared@10.11.1":
version "10.11.1"
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.11.1.tgz#62b84e3118ae6e1f3ff38f4fbe71b0c5d0f10938"
integrity sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==
dependencies:
vue-demi ">=0.14.8"
"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1":
version "1.12.1" version "1.12.1"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb"
@ -4735,10 +4680,10 @@ axios-retry@3.1.9:
dependencies: dependencies:
is-retry-allowed "^1.1.0" is-retry-allowed "^1.1.0"
axios@1.13.2, axios@^1.7.9: axios@1.12.2, axios@^1.7.9:
version "1.13.2" version "1.12.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.2.tgz#9ada120b7b5ab24509553ec3e40123521117f687" resolved "https://registry.yarnpkg.com/axios/-/axios-1.12.2.tgz#6c307390136cf7a2278d09cec63b136dfc6e6da7"
integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA== integrity sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==
dependencies: dependencies:
follow-redirects "^1.15.6" follow-redirects "^1.15.6"
form-data "^4.0.4" form-data "^4.0.4"
@ -4827,16 +4772,16 @@ babel-plugin-jsx-v-model@^2.0.1:
html-tags "^2.0.0" html-tags "^2.0.0"
svg-tags "^1.0.0" svg-tags "^1.0.0"
babel-plugin-module-resolver@5.0.2: babel-plugin-module-resolver@4.0.0:
version "5.0.2" version "4.0.0"
resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.2.tgz#cdeac5d4aaa3b08dd1ac23ddbf516660ed2d293e" resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.0.0.tgz#8f3a3d9d48287dc1d3b0d5595113adabd36a847f"
integrity sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg== integrity sha512-3pdEq3PXALilSJ6dnC4wMWr0AZixHRM4utpdpBR9g5QG7B7JwWyukQv7a9hVxkbGFl+nQbrHDqqQOIBtTXTP/Q==
dependencies: dependencies:
find-babel-config "^2.1.1" find-babel-config "^1.2.0"
glob "^9.3.3" glob "^7.1.6"
pkg-up "^3.1.0" pkg-up "^3.1.0"
reselect "^4.1.7" reselect "^4.0.0"
resolve "^1.22.8" resolve "^1.13.1"
babel-plugin-polyfill-corejs2@^0.4.10: babel-plugin-polyfill-corejs2@^0.4.10:
version "0.4.11" version "0.4.11"
@ -4924,11 +4869,6 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
baseline-browser-mapping@^2.9.0:
version "2.9.18"
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz#c8281693035a9261b10d662a5379650a6c2d1ff7"
integrity sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==
batch@0.6.1: batch@0.6.1:
version "0.6.1" version "0.6.1"
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
@ -5131,16 +5071,15 @@ browserify-zlib@^0.2.0:
dependencies: dependencies:
pako "~1.0.5" pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.16.3, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.23.3, browserslist@^4.24.0: browserslist@^4.0.0, browserslist@^4.16.3, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.23.1, browserslist@^4.23.3:
version "4.28.1" version "4.23.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800"
integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==
dependencies: dependencies:
baseline-browser-mapping "^2.9.0" caniuse-lite "^1.0.30001646"
caniuse-lite "^1.0.30001759" electron-to-chromium "^1.5.4"
electron-to-chromium "^1.5.263" node-releases "^2.0.18"
node-releases "^2.0.27" update-browserslist-db "^1.1.0"
update-browserslist-db "^1.2.0"
bser@2.1.1: bser@2.1.1:
version "2.1.1" version "2.1.1"
@ -5298,10 +5237,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2" lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0" lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001759: caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001646:
version "1.0.30001766" version "1.0.30001655"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz#b6f6b55cb25a2d888d9393104d14751c6a7d6f7a" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz#0ce881f5a19a2dcfda2ecd927df4d5c1684b982f"
integrity sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA== integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==
case-sensitive-paths-webpack-plugin@^2.3.0: case-sensitive-paths-webpack-plugin@^2.3.0:
version "2.4.0" version "2.4.0"
@ -5348,10 +5287,10 @@ char-regex@^1.0.2:
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
chart.js@4.5.1: chart.js@4.4.8:
version "4.5.1" version "4.4.8"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.5.1.tgz#19dd1a9a386a3f6397691672231cb5fc9c052c35" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.8.tgz#54645b638e9d585099bc16b892947b5e6cd2a552"
integrity sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw== integrity sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==
dependencies: dependencies:
"@kurkle/color" "^0.3.0" "@kurkle/color" "^0.3.0"
@ -5557,13 +5496,6 @@ color-convert@^2.0.1:
dependencies: dependencies:
color-name "~1.1.4" color-name "~1.1.4"
color-convert@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-3.1.3.tgz#db6627b97181cb8facdfce755ae26f97ab0711f1"
integrity sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==
dependencies:
color-name "^2.0.0"
color-name@1.1.3: color-name@1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
@ -5574,11 +5506,6 @@ color-name@^1.0.0, color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-name@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.1.0.tgz#0b677385c1c4b4edfdeaf77e38fa338e3a40b693"
integrity sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==
color-string@^1.9.0: color-string@^1.9.0:
version "1.9.1" version "1.9.1"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
@ -5587,13 +5514,6 @@ color-string@^1.9.0:
color-name "^1.0.0" color-name "^1.0.0"
simple-swizzle "^0.2.2" simple-swizzle "^0.2.2"
color-string@^2.1.3:
version "2.1.4"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-2.1.4.tgz#9dcf566ff976e23368c8bd673f5c35103ab41058"
integrity sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==
dependencies:
color-name "^2.0.0"
color@4.2.3: color@4.2.3:
version "4.2.3" version "4.2.3"
resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
@ -5602,14 +5522,6 @@ color@4.2.3:
color-convert "^2.0.1" color-convert "^2.0.1"
color-string "^1.9.0" color-string "^1.9.0"
color@5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/color/-/color-5.0.3.tgz#f79390b1b778e222ffbb54304d3dbeaef633f97f"
integrity sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==
dependencies:
color-convert "^3.1.3"
color-string "^2.1.3"
colord@^2.9.1: colord@^2.9.1:
version "2.9.3" version "2.9.3"
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
@ -5919,7 +5831,7 @@ cron-validator@1.4.0:
resolved "https://registry.yarnpkg.com/cron-validator/-/cron-validator-1.4.0.tgz#77ed4277b086c22e74ee65640f8456747afd4885" resolved "https://registry.yarnpkg.com/cron-validator/-/cron-validator-1.4.0.tgz#77ed4277b086c22e74ee65640f8456747afd4885"
integrity sha512-wGcJ9FCy65iaU6egSH8b5dZYJF7GU/3Jh06wzaT9lsa5dbqExjljmu+0cJ8cpKn+vUyZa/EM4WAxeLR6SypJXw== integrity sha512-wGcJ9FCy65iaU6egSH8b5dZYJF7GU/3Jh06wzaT9lsa5dbqExjljmu+0cJ8cpKn+vUyZa/EM4WAxeLR6SypJXw==
cronstrue@2.59.0, cronstrue@3.9.0: cronstrue@2.53.0, cronstrue@2.59.0:
version "2.59.0" version "2.59.0"
resolved "https://registry.yarnpkg.com/cronstrue/-/cronstrue-2.59.0.tgz#a250b2b04eebabf35518725018e501ff94370fb1" resolved "https://registry.yarnpkg.com/cronstrue/-/cronstrue-2.59.0.tgz#a250b2b04eebabf35518725018e501ff94370fb1"
integrity sha512-YKGmAy84hKH+hHIIER07VCAHf9u0Ldelx1uU6EBxsRPDXIA1m5fsKmJfyC3xBhw6cVC/1i83VdbL4PvepTrt8A== integrity sha512-YKGmAy84hKH+hHIIER07VCAHf9u0Ldelx1uU6EBxsRPDXIA1m5fsKmJfyC3xBhw6cVC/1i83VdbL4PvepTrt8A==
@ -5942,7 +5854,7 @@ cross-spawn@^6.0.0:
shebang-command "^1.2.0" shebang-command "^1.2.0"
which "^1.2.9" which "^1.2.9"
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.6" version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
@ -6229,7 +6141,7 @@ d3-delaunay@6:
resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
"d3-drag@2 - 3", d3-drag@3, d3-drag@^3.0.0: "d3-drag@2 - 3", d3-drag@3:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
@ -6284,7 +6196,7 @@ d3-hierarchy@3:
resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6"
integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3, d3-interpolate@^3.0.1: "d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
@ -6330,7 +6242,7 @@ d3-scale@4:
d3-time "2.1.1 - 3" d3-time "2.1.1 - 3"
d3-time-format "2 - 4" d3-time-format "2 - 4"
"d3-selection@2 - 3", d3-selection@3, d3-selection@3.0.0, d3-selection@^3.0.0: "d3-selection@2 - 3", d3-selection@3, d3-selection@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
@ -6372,7 +6284,7 @@ d3-shape@3:
d3-interpolate "1 - 3" d3-interpolate "1 - 3"
d3-timer "1 - 3" d3-timer "1 - 3"
d3-zoom@3, d3-zoom@^3.0.0: d3-zoom@3:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
@ -6572,10 +6484,10 @@ define-properties@^1.1.3, define-properties@^1.2.1:
has-property-descriptors "^1.0.0" has-property-descriptors "^1.0.0"
object-keys "^1.1.1" object-keys "^1.1.1"
defu@6.1.4: defu@5.0.1:
version "6.1.4" version "5.0.1"
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" resolved "https://registry.yarnpkg.com/defu/-/defu-5.0.1.tgz#a034278f9b032bf0845d261aa75e9ad98da878ac"
integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== integrity sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==
delaunator@5: delaunator@5:
version "5.0.1" version "5.0.1"
@ -6829,21 +6741,16 @@ ejs@3.1.10:
dependencies: dependencies:
jake "^10.8.5" jake "^10.8.5"
electron-to-chromium@^1.5.263: electron-to-chromium@^1.5.4:
version "1.5.278" version "1.5.13"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.278.tgz#807a5e321f012a41bfd64e653f35993c9af95493" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6"
integrity sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw== integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==
element-matches@^0.1.2: element-matches@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/element-matches/-/element-matches-0.1.2.tgz#7345cb71e965bd2b12f725e524591c102198361a" resolved "https://registry.yarnpkg.com/element-matches/-/element-matches-0.1.2.tgz#7345cb71e965bd2b12f725e524591c102198361a"
integrity sha512-yWh1otcs3OKUWDvu/IxyI36ZI3WNaRZlI0uG/DK6fu0pap0VYZ0J5pEGTk1zakme+hT0OKHwhlHc0N5TJhY6yQ== integrity sha512-yWh1otcs3OKUWDvu/IxyI36ZI3WNaRZlI0uG/DK6fu0pap0VYZ0J5pEGTk1zakme+hT0OKHwhlHc0N5TJhY6yQ==
elkjs@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.11.1.tgz#d27fcdbbf5a8aeeff80420d17d1666f78cfc8544"
integrity sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==
elliptic@^6.5.3, elliptic@^6.5.5: elliptic@^6.5.3, elliptic@^6.5.5:
version "6.5.7" version "6.5.7"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
@ -7042,7 +6949,7 @@ es6-error@^4.0.1:
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escalade@^3.1.1, escalade@^3.2.0: escalade@^3.1.1, escalade@^3.1.2:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
@ -7713,12 +7620,13 @@ finalhandler@~1.1.2:
statuses "~1.5.0" statuses "~1.5.0"
unpipe "~1.0.0" unpipe "~1.0.0"
find-babel-config@^2.1.1: find-babel-config@^1.2.0:
version "2.1.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-2.1.2.tgz#2841b1bfbbbcdb971e1e39df8cbc43dafa901716" resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.2.2.tgz#41199b5cb9154dcb2fdc351cbe70eaf9198d5111"
integrity sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg== integrity sha512-oK59njMyw2y3yxto1BCfVK7MQp/OYf4FleHu0RgosH3riFJ1aOuo/7naLDLAObfrgn3ueFhw5sAT/cp0QuJI3Q==
dependencies: dependencies:
json5 "^2.2.3" json5 "^1.0.2"
path-exists "^3.0.0"
find-cache-dir@^3.0.0, find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: find-cache-dir@^3.0.0, find-cache-dir@^3.2.0, find-cache-dir@^3.3.1:
version "3.3.2" version "3.3.2"
@ -7814,14 +7722,6 @@ foreground-child@^2.0.0:
cross-spawn "^7.0.0" cross-spawn "^7.0.0"
signal-exit "^3.0.2" signal-exit "^3.0.2"
foreground-child@^3.3.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
dependencies:
cross-spawn "^7.0.6"
signal-exit "^4.0.1"
forever-agent@~0.6.1: forever-agent@~0.6.1:
version "0.6.1" version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@ -8070,7 +7970,7 @@ glob-to-regexp@^0.4.1:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
glob@7.2.3, glob@^10.3.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^9.3.3: glob@7.2.3, glob@^10.3.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.2.3" version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@ -9020,6 +8920,16 @@ istanbul-lib-hook@^3.0.0:
dependencies: dependencies:
append-transform "^2.0.0" append-transform "^2.0.0"
istanbul-lib-instrument@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d"
integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==
dependencies:
"@babel/core" "^7.7.5"
"@istanbuljs/schema" "^0.1.2"
istanbul-lib-coverage "^3.0.0"
semver "^6.3.0"
istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d"
@ -9031,17 +8941,6 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0:
istanbul-lib-coverage "^3.2.0" istanbul-lib-coverage "^3.2.0"
semver "^6.3.0" semver "^6.3.0"
istanbul-lib-instrument@^6.0.2:
version "6.0.3"
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765"
integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==
dependencies:
"@babel/core" "^7.23.9"
"@babel/parser" "^7.23.9"
"@istanbuljs/schema" "^0.1.3"
istanbul-lib-coverage "^3.2.0"
semver "^7.5.4"
istanbul-lib-processinfo@^2.0.2: istanbul-lib-processinfo@^2.0.2:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169"
@ -9570,10 +9469,10 @@ js-yaml-loader@1.2.2:
loader-utils "^1.2.3" loader-utils "^1.2.3"
un-eval "^1.2.0" un-eval "^1.2.0"
js-yaml@4.1.1, js-yaml@^4.1.0: js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.1" version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies: dependencies:
argparse "^2.0.1" argparse "^2.0.1"
@ -10001,10 +9900,10 @@ lodash.upperfirst@^4.3.1:
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
lodash@4.17.23, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
version "4.17.23" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@^4.0.0, log-symbols@^4.1.0: log-symbols@^4.0.0, log-symbols@^4.1.0:
version "4.1.0" version "4.1.0"
@ -10458,10 +10357,10 @@ node-preload@^0.2.1:
dependencies: dependencies:
process-on-spawn "^1.0.0" process-on-spawn "^1.0.0"
node-releases@^2.0.27: node-releases@^2.0.18:
version "2.0.27" version "2.0.18"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
nodemon@2.0.22: nodemon@2.0.22:
version "2.0.22" version "2.0.22"
@ -10544,10 +10443,10 @@ nwsapi@^2.2.0:
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8"
integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==
nyc@17.1.0: nyc@15.1.0:
version "17.1.0" version "15.1.0"
resolved "https://registry.yarnpkg.com/nyc/-/nyc-17.1.0.tgz#b6349a401a62ffeb912bd38ea9a018839fdb6eb1" resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02"
integrity sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ== integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==
dependencies: dependencies:
"@istanbuljs/load-nyc-config" "^1.0.0" "@istanbuljs/load-nyc-config" "^1.0.0"
"@istanbuljs/schema" "^0.1.2" "@istanbuljs/schema" "^0.1.2"
@ -10556,12 +10455,12 @@ nyc@17.1.0:
decamelize "^1.2.0" decamelize "^1.2.0"
find-cache-dir "^3.2.0" find-cache-dir "^3.2.0"
find-up "^4.1.0" find-up "^4.1.0"
foreground-child "^3.3.0" foreground-child "^2.0.0"
get-package-type "^0.1.0" get-package-type "^0.1.0"
glob "^7.1.6" glob "^7.1.6"
istanbul-lib-coverage "^3.0.0" istanbul-lib-coverage "^3.0.0"
istanbul-lib-hook "^3.0.0" istanbul-lib-hook "^3.0.0"
istanbul-lib-instrument "^6.0.2" istanbul-lib-instrument "^4.0.0"
istanbul-lib-processinfo "^2.0.2" istanbul-lib-processinfo "^2.0.2"
istanbul-lib-report "^3.0.0" istanbul-lib-report "^3.0.0"
istanbul-lib-source-maps "^4.0.0" istanbul-lib-source-maps "^4.0.0"
@ -11488,10 +11387,10 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
qs@6.11.0, qs@6.15.0, qs@6.7.0, qs@^6.12.3, qs@~6.10.3: qs@6.11.0, qs@6.14.1, qs@6.7.0, qs@^6.12.3, qs@~6.10.3:
version "6.15.0" version "6.14.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.0.tgz#db8fd5d1b1d2d6b5b33adaf87429805f1909e7b3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159"
integrity sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==
dependencies: dependencies:
side-channel "^1.1.0" side-channel "^1.1.0"
@ -11743,7 +11642,7 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
reselect@^4.1.7: reselect@^4.0.0:
version "4.1.8" version "4.1.8"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
@ -11770,12 +11669,12 @@ resolve.exports@^1.1.0:
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999"
integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.4, resolve@^1.22.8: resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.4:
version "1.22.11" version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
dependencies: dependencies:
is-core-module "^2.16.1" is-core-module "^2.13.0"
path-parse "^1.0.7" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0" supports-preserve-symlinks-flag "^1.0.0"
@ -11978,7 +11877,7 @@ semver-compare@^1.0.0:
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
"semver@2 || 3 || 4 || 5", semver@7.7.4, semver@^5.5.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@~7.0.0: "semver@2 || 3 || 4 || 5", semver@7.7.4, semver@^5.5.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0, semver@^7.6.3, semver@~7.0.0:
version "7.7.4" version "7.7.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==
@ -12216,11 +12115,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
signal-exit@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
simple-swizzle@^0.2.2: simple-swizzle@^0.2.2:
version "0.2.2" version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
@ -13150,13 +13044,13 @@ untildify@^4.0.0:
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
update-browserslist-db@^1.2.0: update-browserslist-db@^1.1.0:
version "1.2.3" version "1.1.0"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e"
integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
dependencies: dependencies:
escalade "^3.2.0" escalade "^3.1.2"
picocolors "^1.1.1" picocolors "^1.0.1"
uri-js@^4.2.2: uri-js@^4.2.2:
version "4.4.1" version "4.4.1"
@ -13263,11 +13157,6 @@ vue-component-type-helpers@^2.0.0:
resolved "https://registry.yarnpkg.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.0.tgz#de5fa802b6beae7125595ec0d3d5195a22691623" resolved "https://registry.yarnpkg.com/vue-component-type-helpers/-/vue-component-type-helpers-2.2.0.tgz#de5fa802b6beae7125595ec0d3d5195a22691623"
integrity sha512-cYrAnv2me7bPDcg9kIcGwjJiSB6Qyi08+jLDo9yuvoFQjzHiPTzML7RnkJB1+3P6KMsX/KbCD4QE3Tv/knEllw== integrity sha512-cYrAnv2me7bPDcg9kIcGwjJiSB6Qyi08+jLDo9yuvoFQjzHiPTzML7RnkJB1+3P6KMsX/KbCD4QE3Tv/knEllw==
vue-demi@>=0.14.8:
version "0.14.10"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
vue-draggable-next@^2.2.1: vue-draggable-next@^2.2.1:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/vue-draggable-next/-/vue-draggable-next-2.3.0.tgz#ba83154f60b8a3c24059c18b8060b72200a4c673" resolved "https://registry.yarnpkg.com/vue-draggable-next/-/vue-draggable-next-2.3.0.tgz#ba83154f60b8a3c24059c18b8060b72200a4c673"
@ -13535,12 +13424,7 @@ webpack-sources@^3.2.3:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack-virtual-modules@0.6.2: webpack-virtual-modules@0.4.3, webpack-virtual-modules@^0.4.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"
integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
webpack-virtual-modules@^0.4.2:
version "0.4.3" version "0.4.3"
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz#cd597c6d51d5a5ecb473eea1983a58fa8a17ded9" resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz#cd597c6d51d5a5ecb473eea1983a58fa8a17ded9"
integrity sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw== integrity sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw==