mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-13 09:36:05 +08:00
Compare commits
75
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db27a174f9 | ||
|
|
fb190996b6 | ||
|
|
1c8880d9b0 | ||
|
|
bcd8929ba3 | ||
|
|
24834396b9 | ||
|
|
280878ebb3 | ||
|
|
51da6a551c | ||
|
|
010dd710fc | ||
|
|
788c583210 | ||
|
|
ee8f214b7f | ||
|
|
27c1e64a05 | ||
|
|
68d67a7cbc | ||
|
|
6a7691456f | ||
|
|
66f6c36759 | ||
|
|
eb77d03b97 | ||
|
|
22deb0e1f6 | ||
|
|
f096147aa3 | ||
|
|
d73a9413be | ||
|
|
63ff8ce8fe | ||
|
|
b3b467f7b5 | ||
|
|
20557ee788 | ||
|
|
8ab432fb52 | ||
|
|
61624dc0ff | ||
|
|
d1367fc9a9 | ||
|
|
18c973c8c4 | ||
|
|
c9b26ab4bb | ||
|
|
2b9d093b38 | ||
|
|
1b3666e15c | ||
|
|
0bccb9a71f | ||
|
|
6d4cd09568 | ||
|
|
44078bf7cb | ||
|
|
1d55319aba | ||
|
|
3b7a0c30a7 | ||
|
|
d4562a91d1 | ||
|
|
080f9cdb29 | ||
|
|
b19da16b83 | ||
|
|
16a92c5e96 | ||
|
|
51a76eefe2 | ||
|
|
690652f204 | ||
|
|
a0f21d5902 | ||
|
|
f0929100f0 | ||
|
|
78ca4bb663 | ||
|
|
eb917c9d18 | ||
|
|
8815c70361 | ||
|
|
eb4c0504c8 | ||
|
|
82e7b0da19 | ||
|
|
92f34fa64f | ||
|
|
6039f21320 | ||
|
|
3cbaf97399 | ||
|
|
bcde1744e1 | ||
|
|
e991c9bcc8 | ||
|
|
6e709bc00f | ||
|
|
20d40860ce | ||
|
|
1496c17f3d | ||
|
|
e99e4ef34f | ||
|
|
73e022172b | ||
|
|
7e86c9fae0 | ||
|
|
b4b49222c3 | ||
|
|
b937a2147b | ||
|
|
07f2b02a9d | ||
|
|
35b6949cdc | ||
|
|
c577f589e7 | ||
|
|
b1387820ed | ||
|
|
45f72749f5 | ||
|
|
a65b3f443e | ||
|
|
21d415b5f3 | ||
|
|
7493fe2dfc | ||
|
|
e71d12f67f | ||
|
|
d3b77e2e41 | ||
|
|
73126b953c | ||
|
|
937afd9c9a | ||
|
|
3658caf067 | ||
|
|
a067751cba | ||
|
|
94566b2b54 | ||
|
|
d47bf1d712 |
@@ -8,16 +8,12 @@
|
|||||||
- [ ] Yes, the backend owner is @
|
- [ ] Yes, the backend owner is @
|
||||||
- [ ] No, frontend-only.
|
- [ ] No, frontend-only.
|
||||||
|
|
||||||
### Related Issue
|
### Related Issue #
|
||||||
<!-- Link the issue as harvester/harvester#<issue_number> -->
|
<!-- Link the issue as harvester/harvester#<issue_number> -->
|
||||||
harvester/harvester#<issue_number>
|
harvester/harvester#<issue_number>
|
||||||
|
|
||||||
|
|
||||||
### Test Steps
|
### Test screenshot or video (Required)
|
||||||
<!-- Provide the test steps for reviewer to verify the issue -->
|
<!-- Attach screenshot or video of the changes and eventual comparison if you find it necessary -->
|
||||||
1. Go to ... page
|
|
||||||
|
|
||||||
### Test screenshot or video
|
|
||||||
<!-- Attach screenshot or video of the changes and eventual comparison -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
name: Build Standalone on PR Merge
|
name: Build Standalone on PR Merge
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch: # manually trigger on Github Actions only support PR branch pushed to harvester/harvester-ui-extension
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
@@ -30,35 +29,3 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
with:
|
with:
|
||||||
CI_BRANCH: ${{github.ref_name}}
|
CI_BRANCH: ${{github.ref_name}}
|
||||||
comment-on-pr:
|
|
||||||
name: Comment build result on PR
|
|
||||||
# Only for manual runs so the existing push / pull_request flows are untouched.
|
|
||||||
if: github.event_name == 'workflow_dispatch'
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- name: Add build artifact comment to the PR opened from this branch
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
BRANCH: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
# Match scripts/version: sanitize the branch and map main -> latest.
|
|
||||||
if [ "$BRANCH" = "main" ]; then
|
|
||||||
DIR="latest"
|
|
||||||
else
|
|
||||||
DIR=$(printf '%s' "$BRANCH" | sed -E 's/[^a-zA-Z0-9.-]+/-/g')
|
|
||||||
fi
|
|
||||||
URL="https://releases.rancher.com/harvester-ui/dashboard/${DIR}/index.html"
|
|
||||||
|
|
||||||
PR=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --base main --head "$BRANCH" --json number --jq '.[0].number // empty')
|
|
||||||
if [ -z "$PR" ]; then
|
|
||||||
echo "No open PR found for branch $BRANCH, skipping comment."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
BODY=$(printf '🚀 **UI Artifact Build Status:** `SUCCESS` ✅\n> ui-index: %s\n> ui-source: external' "$URL")
|
|
||||||
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "$BODY"
|
|
||||||
|
|||||||
+6
-8
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester-ui-extension",
|
"name": "harvester-ui-extension",
|
||||||
"version": "1.10.0-dev",
|
"version": "1.9.1-rc3",
|
||||||
"private": false,
|
"private": false,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.0.0"
|
"node": ">=24.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-class-static-block": "7.29.7",
|
"@babel/plugin-transform-class-static-block": "7.29.7",
|
||||||
"@rancher/shell": "3.0.13",
|
"@rancher/shell": "3.0.12-rc.7",
|
||||||
"@vue-flow/background": "^1.3.0",
|
"@vue-flow/background": "^1.3.0",
|
||||||
"@vue-flow/controls": "^1.1.1",
|
"@vue-flow/controls": "^1.1.1",
|
||||||
"@vue-flow/core": "^1.33.5",
|
"@vue-flow/core": "^1.33.5",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"yaml": "^2.5.1"
|
"yaml": "^2.5.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/node": "25.9.6",
|
"@types/node": "25.9.5",
|
||||||
"cronstrue": "2.59.0",
|
"cronstrue": "2.59.0",
|
||||||
"d3-color": "3.1.0",
|
"d3-color": "3.1.0",
|
||||||
"ejs": "3.1.10",
|
"ejs": "3.1.10",
|
||||||
@@ -48,17 +48,15 @@
|
|||||||
"dev": "NODE_ENV=dev ./node_modules/.bin/vue-cli-service serve",
|
"dev": "NODE_ENV=dev ./node_modules/.bin/vue-cli-service serve",
|
||||||
"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/@rancher/shell/scripts/lint --max-warnings 0 --ext .js,.ts,.vue .",
|
"lint": "./node_modules/.bin/eslint --max-warnings 0 --ext .js,.ts,.vue .",
|
||||||
"lint:fix": "./node_modules/@rancher/shell/scripts/lint --fix --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",
|
||||||
"parse-tag-name": "./node_modules/@rancher/shell/scripts/extension/parse-tag-name",
|
"parse-tag-name": "./node_modules/@rancher/shell/scripts/extension/parse-tag-name",
|
||||||
"commitlint": "commitlint --edit",
|
"commitlint": "commitlint --edit",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"agents:generate": "./scripts/generate-agent-and-persona-mds.sh",
|
"agents:generate": "./scripts/generate-agent-and-persona-mds.sh"
|
||||||
"prelint": "npm --prefix node_modules/@rancher/shell/eslint-legacy install eslint-plugin-jest@^24 --no-save --no-audit --no-fund --legacy-peer-deps --loglevel=error",
|
|
||||||
"prelint:fix": "npm --prefix node_modules/@rancher/shell/eslint-legacy install eslint-plugin-jest@^24 --no-save --no-audit --no-fund --legacy-peer-deps --loglevel=error"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/load": "^19.8.1",
|
"@commitlint/load": "^19.8.1",
|
||||||
|
|||||||
@@ -81,10 +81,7 @@ const FEATURE_FLAGS = {
|
|||||||
'lvmDmThinOnly',
|
'lvmDmThinOnly',
|
||||||
'highPerformanceStorage',
|
'highPerformanceStorage',
|
||||||
],
|
],
|
||||||
'v1.10.0': [
|
'v1.10.0': [],
|
||||||
'maintenanceModeConditions',
|
|
||||||
'maintenanceModeDrainTimeoutSetting',
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateFeatureFlags = () => {
|
const generateFeatureFlags = () => {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const HCI = {
|
|||||||
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
|
VM_NAME_PREFIX: 'harvesterhci.io/vmNamePrefix',
|
||||||
VM_DISPLAY_NAME: 'harvesterhci.io/vmDisplayName',
|
VM_DISPLAY_NAME: 'harvesterhci.io/vmDisplayName',
|
||||||
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
|
VM_RESERVED_MEMORY: 'harvesterhci.io/reservedMemory',
|
||||||
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status', // Deprecated >= v1.10.0
|
MAINTENANCE_STATUS: 'harvesterhci.io/maintain-status',
|
||||||
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
|
HOST_CUSTOM_NAME: 'harvesterhci.io/host-custom-name',
|
||||||
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
|
HOST_CONSOLE_URL: 'harvesterhci.io/host-console-url',
|
||||||
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
RESTORE_NAME: 'restore.harvesterhci.io/name',
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ export const HCI_SETTING = {
|
|||||||
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',
|
INSTANCE_MANAGER_RESOURCES: 'instance-manager-resources',
|
||||||
CLUSTER_POD_SECURITY_STANDARD: 'cluster-pod-security-standard',
|
CLUSTER_POD_SECURITY_STANDARD: 'cluster-pod-security-standard'
|
||||||
MAINTENANCE_MODE_DRAIN_TIMEOUT: 'maintenance-mode-drain-timeout'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HCI_ALLOWED_SETTINGS = {
|
export const HCI_ALLOWED_SETTINGS = {
|
||||||
@@ -149,9 +148,6 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
},
|
},
|
||||||
[HCI_SETTING.CLUSTER_POD_SECURITY_STANDARD]: {
|
[HCI_SETTING.CLUSTER_POD_SECURITY_STANDARD]: {
|
||||||
kind: 'json', from: 'import', canReset: true, featureFlag: 'clusterPodSecurityStandardSetting'
|
kind: 'json', from: 'import', canReset: true, featureFlag: 'clusterPodSecurityStandardSetting'
|
||||||
},
|
|
||||||
[HCI_SETTING.MAINTENANCE_MODE_DRAIN_TIMEOUT]: {
|
|
||||||
kind: 'number', canReset: true, featureFlag: 'maintenanceModeDrainTimeoutSetting'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ export const VOLUME_MODE = {
|
|||||||
FILE_SYSTEM: 'Filesystem'
|
FILE_SYSTEM: 'Filesystem'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ACCESS_MODE = {
|
|
||||||
READ_WRITE_MANY: 'ReadWriteMany',
|
|
||||||
READ_WRITE_ONCE: 'ReadWriteOnce',
|
|
||||||
READ_ONLY_MANY: 'ReadOnlyMany',
|
|
||||||
READ_WRITE_ONCE_POD: 'ReadWriteOncePod',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const NETWORK_PROTOCOL = {
|
export const NETWORK_PROTOCOL = {
|
||||||
IPv4: 'IPv4',
|
IPv4: 'IPv4',
|
||||||
IPv6: 'IPv6',
|
IPv6: 'IPv6',
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import OverviewKeypairs from '../kubevirt.io.virtualmachine/VirtualMachineTabs/V
|
|||||||
import Volume from '../../edit/kubevirt.io.virtualmachine/VirtualMachineVolume';
|
import Volume from '../../edit/kubevirt.io.virtualmachine/VirtualMachineVolume';
|
||||||
import Network from '../../edit/kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
import Network from '../../edit/kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
||||||
import CloudConfig from '../../edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
import CloudConfig from '../../edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
||||||
import { getBootDevices } from '../../utils/vm';
|
|
||||||
const UNDEFINED = 'n/a';
|
const UNDEFINED = 'n/a';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -75,8 +74,18 @@ export default {
|
|||||||
return image?.spec?.displayName || '-';
|
return image?.spec?.displayName || '-';
|
||||||
},
|
},
|
||||||
|
|
||||||
bootDevices() {
|
disks() {
|
||||||
return getBootDevices(this?.spec?.template?.spec?.domain?.devices);
|
const disks = this?.spec?.template?.spec?.domain?.devices?.disks || [];
|
||||||
|
|
||||||
|
return disks.filter((disk) => {
|
||||||
|
return !!disk.bootOrder;
|
||||||
|
}).sort((a, b) => {
|
||||||
|
if (a.bootOrder < b.bootOrder) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
cdroms() {
|
cdroms() {
|
||||||
@@ -92,10 +101,8 @@ export default {
|
|||||||
getDeviceType(o) {
|
getDeviceType(o) {
|
||||||
if (o.disk) {
|
if (o.disk) {
|
||||||
return 'Disk';
|
return 'Disk';
|
||||||
} else if (o.cdrom) {
|
|
||||||
return 'CD-ROM';
|
|
||||||
} else {
|
} else {
|
||||||
return 'Network';
|
return 'CD-ROM';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isEmpty(o) {
|
isEmpty(o) {
|
||||||
@@ -177,10 +184,10 @@ export default {
|
|||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="(device, i) in bootDevices"
|
v-for="(disk, i) in disks"
|
||||||
:key="i"
|
:key="i"
|
||||||
>
|
>
|
||||||
{{ device.bootOrder }}. {{ device.name }} ({{ getDeviceType(device) }})
|
{{ disk.bootOrder }}. {{ disk.name }} ({{ getDeviceType(disk) }})
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -226,7 +233,6 @@ export default {
|
|||||||
>
|
>
|
||||||
<Network
|
<Network
|
||||||
v-model:value="networkRows"
|
v-model:value="networkRows"
|
||||||
:network-boot-first="networkBootFirst"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import OverviewKeypairs from '../kubevirt.io.virtualmachine/VirtualMachineTabs/V
|
|||||||
import Volume from '../../edit/kubevirt.io.virtualmachine/VirtualMachineVolume';
|
import Volume from '../../edit/kubevirt.io.virtualmachine/VirtualMachineVolume';
|
||||||
import Network from '../../edit/kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
import Network from '../../edit/kubevirt.io.virtualmachine/VirtualMachineNetwork';
|
||||||
import CloudConfig from '../../edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
import CloudConfig from '../../edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig';
|
||||||
import { getBootDevices } from '../../utils/vm';
|
|
||||||
const UNDEFINED = 'n/a';
|
const UNDEFINED = 'n/a';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -75,8 +74,18 @@ export default {
|
|||||||
return image?.spec?.displayName || '-';
|
return image?.spec?.displayName || '-';
|
||||||
},
|
},
|
||||||
|
|
||||||
bootDevices() {
|
disks() {
|
||||||
return getBootDevices(this?.spec?.template?.spec?.domain?.devices);
|
const disks = this?.spec?.template?.spec?.domain?.devices?.disks || [];
|
||||||
|
|
||||||
|
return disks.filter((disk) => {
|
||||||
|
return !!disk.bootOrder;
|
||||||
|
}).sort((a, b) => {
|
||||||
|
if (a.bootOrder < b.bootOrder) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
cdroms() {
|
cdroms() {
|
||||||
@@ -92,10 +101,8 @@ export default {
|
|||||||
getDeviceType(o) {
|
getDeviceType(o) {
|
||||||
if (o.disk) {
|
if (o.disk) {
|
||||||
return 'Disk';
|
return 'Disk';
|
||||||
} else if (o.cdrom) {
|
|
||||||
return 'CD-ROM';
|
|
||||||
} else {
|
} else {
|
||||||
return 'Network';
|
return 'CD-ROM';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isEmpty(o) {
|
isEmpty(o) {
|
||||||
@@ -177,10 +184,10 @@ export default {
|
|||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="(device, i) in bootDevices"
|
v-for="(disk, i) in disks"
|
||||||
:key="i"
|
:key="i"
|
||||||
>
|
>
|
||||||
{{ device.bootOrder }}. {{ device.name }} ({{ getDeviceType(device) }})
|
{{ disk.bootOrder }}. {{ disk.name }} ({{ getDeviceType(disk) }})
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -227,7 +234,6 @@ export default {
|
|||||||
>
|
>
|
||||||
<Network
|
<Network
|
||||||
v-model:value="networkRows"
|
v-model:value="networkRows"
|
||||||
:network-boot-first="networkBootFirst"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
+16
-9
@@ -6,7 +6,6 @@ import HarvesterIpAddress from '../../../formatters/HarvesterIpAddress';
|
|||||||
import VMConsoleBar from '../../../components/VMConsoleBar';
|
import VMConsoleBar from '../../../components/VMConsoleBar';
|
||||||
import { HCI } from '../../../types';
|
import { HCI } from '../../../types';
|
||||||
import { getVmCPUMemoryValues } from '../../../utils/cpuMemory';
|
import { getVmCPUMemoryValues } from '../../../utils/cpuMemory';
|
||||||
import { getBootDevices } from '../../../utils/vm';
|
|
||||||
|
|
||||||
const UNDEFINED = 'n/a';
|
const UNDEFINED = 'n/a';
|
||||||
|
|
||||||
@@ -70,8 +69,18 @@ export default {
|
|||||||
return image?.spec?.displayName || 'N/A';
|
return image?.spec?.displayName || 'N/A';
|
||||||
},
|
},
|
||||||
|
|
||||||
bootDevices() {
|
disks() {
|
||||||
return getBootDevices(this.value?.spec?.template?.spec?.domain?.devices);
|
const disks = this.value?.spec?.template?.spec?.domain?.devices?.disks || [];
|
||||||
|
|
||||||
|
return disks.filter((disk) => {
|
||||||
|
return !!disk.bootOrder;
|
||||||
|
}).sort((a, b) => {
|
||||||
|
if (a.bootOrder < b.bootOrder) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
cdroms() {
|
cdroms() {
|
||||||
@@ -113,10 +122,8 @@ export default {
|
|||||||
getDeviceType(o) {
|
getDeviceType(o) {
|
||||||
if (o.disk) {
|
if (o.disk) {
|
||||||
return 'Disk';
|
return 'Disk';
|
||||||
} else if (o.cdrom) {
|
|
||||||
return 'CD-ROM';
|
|
||||||
} else {
|
} else {
|
||||||
return 'Network';
|
return 'CD-ROM';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isEmpty(o) {
|
isEmpty(o) {
|
||||||
@@ -212,16 +219,16 @@ export default {
|
|||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<InputOrDisplay
|
<InputOrDisplay
|
||||||
:name="t('harvester.virtualMachine.detail.details.bootOrder')"
|
:name="t('harvester.virtualMachine.detail.details.bootOrder')"
|
||||||
:value="bootDevices"
|
:value="disks"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
>
|
>
|
||||||
<template #value>
|
<template #value>
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="(device, i) in bootDevices"
|
v-for="(disk, i) in disks"
|
||||||
:key="i"
|
:key="i"
|
||||||
>
|
>
|
||||||
{{ device.bootOrder }}. {{ device.name }} ({{ getDeviceType(device) }})
|
{{ disk.bootOrder }}. {{ disk.name }} ({{ getDeviceType(disk) }})
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ export default {
|
|||||||
|
|
||||||
this['diskRows'] = diskRows;
|
this['diskRows'] = diskRows;
|
||||||
this['networkRows'] = this.getNetworkRows(neu, { fromTemplate: false, init: false });
|
this['networkRows'] = this.getNetworkRows(neu, { fromTemplate: false, init: false });
|
||||||
this['networkBootFirst'] = this.isNetworkBootFirst(neu.spec);
|
|
||||||
|
|
||||||
// getInitConfig() seeds these on created(); refresh them here too so the
|
// getInitConfig() seeds these on created(); refresh them here too so the
|
||||||
// VM-wide performance panel does not go stale when the spec changes.
|
// VM-wide performance panel does not go stale when the spec changes.
|
||||||
@@ -277,7 +276,6 @@ export default {
|
|||||||
>
|
>
|
||||||
<Network
|
<Network
|
||||||
v-model:value="networkRows"
|
v-model:value="networkRows"
|
||||||
:network-boot-first="networkBootFirst"
|
|
||||||
mode="view"
|
mode="view"
|
||||||
:vm="value"
|
:vm="value"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import { VOLUME_MODE, ACCESS_MODE } from '@pkg/harvester/config/types';
|
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
||||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
import ArrayList from '@shell/components/form/ArrayList';
|
import ArrayList from '@shell/components/form/ArrayList';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
import { Checkbox } from '@components/Form/Checkbox';
|
||||||
@@ -10,10 +10,6 @@ import { HCI_SETTING } from '../../config/settings';
|
|||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { _EDIT, _CREATE } from '@shell/config/query-params';
|
import { _EDIT, _CREATE } from '@shell/config/query-params';
|
||||||
|
|
||||||
const {
|
|
||||||
READ_WRITE_ONCE, READ_ONLY_MANY, READ_WRITE_MANY, READ_WRITE_ONCE_POD
|
|
||||||
} = ACCESS_MODE;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CDISettings',
|
name: 'CDISettings',
|
||||||
|
|
||||||
@@ -94,10 +90,10 @@ export default {
|
|||||||
|
|
||||||
accessModeOptions() {
|
accessModeOptions() {
|
||||||
return [
|
return [
|
||||||
{ label: READ_WRITE_ONCE, value: READ_WRITE_ONCE },
|
{ label: 'ReadWriteOnce', value: 'ReadWriteOnce' },
|
||||||
{ label: READ_ONLY_MANY, value: READ_ONLY_MANY },
|
{ label: 'ReadOnlyMany', value: 'ReadOnlyMany' },
|
||||||
{ label: READ_WRITE_MANY, value: READ_WRITE_MANY },
|
{ label: 'ReadWriteMany', value: 'ReadWriteMany' },
|
||||||
{ label: READ_WRITE_ONCE_POD, value: READ_WRITE_ONCE_POD },
|
{ label: 'ReadWriteOncePod', value: 'ReadWriteOncePod' },
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ export default {
|
|||||||
>
|
>
|
||||||
<Network
|
<Network
|
||||||
v-model:value="networkRows"
|
v-model:value="networkRows"
|
||||||
v-model:network-boot-first="networkBootFirst"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -27,11 +27,9 @@ import { HCI, VOLUME_SNAPSHOT } from '../types';
|
|||||||
import { LVM_DRIVER } from '../models/harvester/storage.k8s.io.storageclass';
|
import { LVM_DRIVER } from '../models/harvester/storage.k8s.io.storageclass';
|
||||||
import { DATA_ENGINE_V2 } from '../models/harvester/persistentvolumeclaim';
|
import { DATA_ENGINE_V2 } from '../models/harvester/persistentvolumeclaim';
|
||||||
import { GIBIBYTE } from '../utils/unit';
|
import { GIBIBYTE } from '../utils/unit';
|
||||||
import { VOLUME_MODE, ACCESS_MODE } from '@pkg/harvester/config/types';
|
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
||||||
import { isInternalStorageClass } from '../utils/storage-class';
|
import { isInternalStorageClass } from '../utils/storage-class';
|
||||||
|
|
||||||
const { READ_WRITE_MANY, READ_WRITE_ONCE, READ_ONLY_MANY } = ACCESS_MODE;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterVolume',
|
name: 'HarvesterVolume',
|
||||||
|
|
||||||
@@ -87,7 +85,7 @@ export default {
|
|||||||
if (this.mode === _CREATE) {
|
if (this.mode === _CREATE) {
|
||||||
// default volumeMode to Block
|
// default volumeMode to Block
|
||||||
this.value.spec.volumeMode = VOLUME_MODE.BLOCK;
|
this.value.spec.volumeMode = VOLUME_MODE.BLOCK;
|
||||||
this.value.spec.accessModes = [READ_WRITE_MANY];
|
this.value.spec.accessModes = ['ReadWriteMany'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const storage = this.value?.spec?.resources?.requests?.storage || null;
|
const storage = this.value?.spec?.resources?.requests?.storage || null;
|
||||||
@@ -172,7 +170,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
accessModeOptions() {
|
accessModeOptions() {
|
||||||
return [READ_WRITE_ONCE, READ_WRITE_MANY, READ_ONLY_MANY];
|
return ['ReadWriteOnce', 'ReadWriteMany', 'ReadOnlyMany'];
|
||||||
},
|
},
|
||||||
|
|
||||||
imageOption() {
|
imageOption() {
|
||||||
@@ -368,7 +366,7 @@ export default {
|
|||||||
|
|
||||||
getAccessMode() {
|
getAccessMode() {
|
||||||
if (!this.longhornV2LVMSupport) {
|
if (!this.longhornV2LVMSupport) {
|
||||||
return [READ_WRITE_MANY];
|
return ['ReadWriteMany'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.value?.spec?.accessModes && this.value?.spec?.accessModes?.length > 0) {
|
if (this.value?.spec?.accessModes && this.value?.spec?.accessModes?.length > 0) {
|
||||||
@@ -384,7 +382,7 @@ export default {
|
|||||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.value.thirdPartyStorageFeatureEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.value.thirdPartyStorageFeatureEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return readWriteOnce ? [READ_WRITE_ONCE] : [READ_WRITE_MANY];
|
return readWriteOnce ? ['ReadWriteOnce'] : ['ReadWriteMany'];
|
||||||
},
|
},
|
||||||
buildDataVolumeObj() {
|
buildDataVolumeObj() {
|
||||||
const storage = {
|
const storage = {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import InputOrDisplay from '@shell/components/InputOrDisplay';
|
import InputOrDisplay from '@shell/components/InputOrDisplay';
|
||||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
import { Checkbox } from '@components/Form/Checkbox';
|
|
||||||
|
|
||||||
import { clone } from '@shell/utils/object';
|
import { clone } from '@shell/utils/object';
|
||||||
import { _CREATE, _VIEW } from '@shell/config/query-params';
|
import { _CREATE, _VIEW } from '@shell/config/query-params';
|
||||||
@@ -34,7 +33,7 @@ export default {
|
|||||||
emits: ['update'],
|
emits: ['update'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
LabeledInput, LabeledSelect, InputOrDisplay, Checkbox
|
LabeledInput, LabeledSelect, InputOrDisplay
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
@@ -170,7 +169,6 @@ export default {
|
|||||||
this.value.isPod = true;
|
this.value.isPod = true;
|
||||||
this.value.macAddress = '';
|
this.value.macAddress = '';
|
||||||
this.value.staticIp = '';
|
this.value.staticIp = '';
|
||||||
this.value.networkBoot = false;
|
|
||||||
} else {
|
} else {
|
||||||
this.value.isPod = false;
|
this.value.isPod = false;
|
||||||
}
|
}
|
||||||
@@ -287,22 +285,6 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="!isMasquerade"
|
|
||||||
data-testid="input-hen-networkBoot"
|
|
||||||
class="row mb-20"
|
|
||||||
>
|
|
||||||
<div class="col span-12">
|
|
||||||
<Checkbox
|
|
||||||
v-model:value="value.networkBoot"
|
|
||||||
:label="t('harvester.virtualMachine.network.networkBoot.label')"
|
|
||||||
:tooltip="t('harvester.virtualMachine.network.networkBoot.tip')"
|
|
||||||
:mode="mode"
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!isMasquerade && isSingle">
|
<div v-if="!isMasquerade && isSingle">
|
||||||
<div class="row mb-20">
|
<div class="row mb-20">
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import InfoBox from '@shell/components/InfoBox';
|
import InfoBox from '@shell/components/InfoBox';
|
||||||
import { RadioGroup } from '@components/Form/Radio';
|
|
||||||
import { NETWORK_ATTACHMENT } from '@shell/config/types';
|
import { NETWORK_ATTACHMENT } from '@shell/config/types';
|
||||||
import { sortBy } from '@shell/utils/sort';
|
import { sortBy } from '@shell/utils/sort';
|
||||||
import { clone } from '@shell/utils/object';
|
import { clone } from '@shell/utils/object';
|
||||||
@@ -11,11 +10,7 @@ import { HCI as HCI_ANNOTATIONS } from '../../../config/labels-annotations';
|
|||||||
import Base from './base';
|
import Base from './base';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: { InfoBox, Base },
|
||||||
InfoBox, Base, RadioGroup
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['update:value', 'update:networkBootFirst'],
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
vm: {
|
vm: {
|
||||||
@@ -40,11 +35,6 @@ export default {
|
|||||||
isSingle: {
|
isSingle: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
|
||||||
|
|
||||||
networkBootFirst: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -92,20 +82,6 @@ export default {
|
|||||||
|
|
||||||
vmState() {
|
vmState() {
|
||||||
return this.vm?.stateDisplay;
|
return this.vm?.stateDisplay;
|
||||||
},
|
|
||||||
|
|
||||||
hasNetworkBoot() {
|
|
||||||
return this.rows.some((R) => R.networkBoot);
|
|
||||||
},
|
|
||||||
|
|
||||||
networkBootOrderOptions() {
|
|
||||||
return [{
|
|
||||||
label: this.t('harvester.virtualMachine.network.networkBoot.first'),
|
|
||||||
value: true
|
|
||||||
}, {
|
|
||||||
label: this.t('harvester.virtualMachine.network.networkBoot.last'),
|
|
||||||
value: false
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -242,22 +218,6 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</InfoBox>
|
</InfoBox>
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="hasNetworkBoot"
|
|
||||||
data-testid="input-hen-networkBootFirst"
|
|
||||||
class="mb-20"
|
|
||||||
>
|
|
||||||
<RadioGroup
|
|
||||||
:value="networkBootFirst"
|
|
||||||
name="networkBootFirst"
|
|
||||||
:label="t('harvester.virtualMachine.network.networkBoot.order')"
|
|
||||||
:options="networkBootOrderOptions"
|
|
||||||
:mode="mode"
|
|
||||||
:row="true"
|
|
||||||
@update:value="$emit('update:networkBootFirst', $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="!isView"
|
v-if="!isView"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -17,12 +17,10 @@ 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 { 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, ACCESS_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';
|
import { EMPTY_IMAGE } from '../../../utils/vm';
|
||||||
|
|
||||||
const { READ_WRITE_MANY } = ACCESS_MODE;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['update:value', 'update:blockMultiQueue', 'update:ioThreadsPolicy', 'update:ioThreadCount'],
|
emits: ['update:value', 'update:blockMultiQueue', 'update:ioThreadsPolicy', 'update:ioThreadCount'],
|
||||||
|
|
||||||
@@ -72,7 +70,7 @@ export default {
|
|||||||
|
|
||||||
customAccessMode: {
|
customAccessMode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: READ_WRITE_MANY
|
default: 'ReadWriteMany'
|
||||||
},
|
},
|
||||||
|
|
||||||
resourceType: {
|
resourceType: {
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ import { LVM_DRIVER } from '../../../../models/harvester/storage.k8s.io.storagec
|
|||||||
import { DATA_ENGINE_V2 } from '../../../../models/harvester/persistentvolumeclaim';
|
import { DATA_ENGINE_V2 } from '../../../../models/harvester/persistentvolumeclaim';
|
||||||
import { GIBIBYTE } from '../../../../utils/unit';
|
import { GIBIBYTE } from '../../../../utils/unit';
|
||||||
import { isInternalStorageClass } from '../../../../utils/storage-class';
|
import { isInternalStorageClass } from '../../../../utils/storage-class';
|
||||||
import { VOLUME_MODE, ACCESS_MODE } from '@pkg/harvester/config/types';
|
import { VOLUME_MODE } from '@pkg/harvester/config/types';
|
||||||
|
|
||||||
const { READ_WRITE_MANY, READ_WRITE_ONCE } = ACCESS_MODE;
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HarvesterEditVolume',
|
name: 'HarvesterEditVolume',
|
||||||
@@ -208,7 +206,7 @@ export default {
|
|||||||
|
|
||||||
getAccessMode(storageClassName) {
|
getAccessMode(storageClassName) {
|
||||||
if (!this.longhornV2LVMSupport) {
|
if (!this.longhornV2LVMSupport) {
|
||||||
return READ_WRITE_MANY;
|
return 'ReadWriteMany';
|
||||||
}
|
}
|
||||||
|
|
||||||
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
const storageClass = this.storageClasses.find((sc) => sc.name === storageClassName);
|
||||||
@@ -219,7 +217,7 @@ export default {
|
|||||||
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.thirdPartyStorageClassEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
readWriteOnce = storageClass.provisioner === LVM_DRIVER || (!this.thirdPartyStorageClassEnabled && storageClass.parameters?.dataEngine === DATA_ENGINE_V2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return readWriteOnce ? READ_WRITE_ONCE : READ_WRITE_MANY;
|
return readWriteOnce ? 'ReadWriteOnce' : 'ReadWriteMany';
|
||||||
},
|
},
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|||||||
@@ -791,7 +791,6 @@ export default {
|
|||||||
>
|
>
|
||||||
<Network
|
<Network
|
||||||
v-model:value="networkRows"
|
v-model:value="networkRows"
|
||||||
v-model:network-boot-first="networkBootFirst"
|
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
:is-single="isSingle"
|
:is-single="isSingle"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -286,7 +286,6 @@ harvester:
|
|||||||
createTemplate: Generate Template
|
createTemplate: Generate Template
|
||||||
enableMaintenance: Enable Maintenance Mode
|
enableMaintenance: Enable Maintenance Mode
|
||||||
disableMaintenance: Disable Maintenance Mode
|
disableMaintenance: Disable Maintenance Mode
|
||||||
clearMaintenance: Clear Maintenance Mode Error
|
|
||||||
enableCPUManager: Enable CPU Manager
|
enableCPUManager: Enable CPU Manager
|
||||||
disableCPUManager: Disable CPU Manager
|
disableCPUManager: Disable CPU Manager
|
||||||
cordon: Cordon
|
cordon: Cordon
|
||||||
@@ -872,12 +871,6 @@ harvester:
|
|||||||
title: Network
|
title: Network
|
||||||
addNetwork: Add Network
|
addNetwork: Add Network
|
||||||
addPort: Add Port
|
addPort: Add Port
|
||||||
networkBoot:
|
|
||||||
label: Enable network boot (PXE)
|
|
||||||
tip: Adds this interface to the VM boot order so the guest firmware can boot from the network. Requires a DHCP/TFTP (PXE) server reachable on the selected network; the management network does not provide one.
|
|
||||||
order: Network boot order
|
|
||||||
first: Before volumes
|
|
||||||
last: After volumes
|
|
||||||
cloudConfig:
|
cloudConfig:
|
||||||
title: Cloud Configuration
|
title: Cloud Configuration
|
||||||
createTemplateTitle: 'Create {name}'
|
createTemplateTitle: 'Create {name}'
|
||||||
@@ -2542,7 +2535,6 @@ advancedSettings:
|
|||||||
'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.'
|
'harv-instance-manager-resources': 'Configure resource percentage reservations for Longhorn instance manager V1 and V2. Valid instance manager CPU range between 0 - 40.'
|
||||||
'harv-cluster-pod-security-standard': 'Enforce Kubernetes Pod Security Standards (PSS) at the cluster level.'
|
'harv-cluster-pod-security-standard': 'Enforce Kubernetes Pod Security Standards (PSS) at the cluster level.'
|
||||||
'harv-maintenance-mode-drain-timeout': 'Maintenance mode drain timeout in minutes. Use 0 to disable the timeout.'
|
|
||||||
|
|
||||||
typeLabel:
|
typeLabel:
|
||||||
kubevirt.io.virtualmachine: |-
|
kubevirt.io.virtualmachine: |-
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ import { HCI } from '../../types';
|
|||||||
import { parseVolumeClaimTemplates, EMPTY_IMAGE } from '../../utils/vm';
|
import { parseVolumeClaimTemplates, EMPTY_IMAGE } 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, ACCESS_MODE, FILESYSTEM_SOURCE_TYPE } from '@pkg/harvester/config/types';
|
import { VOLUME_MODE, FILESYSTEM_SOURCE_TYPE } from '@pkg/harvester/config/types';
|
||||||
|
|
||||||
const { READ_WRITE_MANY } = ACCESS_MODE;
|
|
||||||
|
|
||||||
const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine';
|
const LONGHORN_V2_DATA_ENGINE = 'longhorn-system/v2-data-engine';
|
||||||
|
|
||||||
@@ -160,7 +158,6 @@ export default {
|
|||||||
imageId: '',
|
imageId: '',
|
||||||
diskRows: [],
|
diskRows: [],
|
||||||
networkRows: [],
|
networkRows: [],
|
||||||
networkBootFirst: true,
|
|
||||||
filesystemRows: [],
|
filesystemRows: [],
|
||||||
machineType: '',
|
machineType: '',
|
||||||
machineTypes: [],
|
machineTypes: [],
|
||||||
@@ -283,7 +280,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
customAccessMode() {
|
customAccessMode() {
|
||||||
return this.storageClassSetting.accessModes || READ_WRITE_MANY;
|
return this.storageClassSetting.accessModes || 'ReadWriteMany';
|
||||||
},
|
},
|
||||||
|
|
||||||
isWindows() {
|
isWindows() {
|
||||||
@@ -385,7 +382,6 @@ export default {
|
|||||||
const diskRows = this.getDiskRows(vm, volumeBackups);
|
const diskRows = this.getDiskRows(vm, volumeBackups);
|
||||||
|
|
||||||
const networkRows = this.getNetworkRows(vm, { fromTemplate, init });
|
const networkRows = this.getNetworkRows(vm, { fromTemplate, init });
|
||||||
const networkBootFirst = this.isNetworkBootFirst(spec);
|
|
||||||
const hasCreateVolumes = this.getHasCreatedVolumes(spec) || [];
|
const hasCreateVolumes = this.getHasCreatedVolumes(spec) || [];
|
||||||
|
|
||||||
let { userData = undefined, networkData = undefined } = this.getCloudInitNoCloud(spec);
|
let { userData = undefined, networkData = undefined } = this.getCloudInitNoCloud(spec);
|
||||||
@@ -463,7 +459,6 @@ export default {
|
|||||||
|
|
||||||
this['hasCreateVolumes'] = hasCreateVolumes;
|
this['hasCreateVolumes'] = hasCreateVolumes;
|
||||||
this['networkRows'] = networkRows;
|
this['networkRows'] = networkRows;
|
||||||
this['networkBootFirst'] = networkBootFirst;
|
|
||||||
this['imageId'] = imageId;
|
this['imageId'] = imageId;
|
||||||
|
|
||||||
this['diskRows'] = diskRows;
|
this['diskRows'] = diskRows;
|
||||||
@@ -521,7 +516,7 @@ export default {
|
|||||||
id: randomStr(5),
|
id: randomStr(5),
|
||||||
source: SOURCE_TYPE.IMAGE,
|
source: SOURCE_TYPE.IMAGE,
|
||||||
name: 'disk-0',
|
name: 'disk-0',
|
||||||
accessMode: READ_WRITE_MANY, // root disk only support LHv1 volume, should be RWX
|
accessMode: 'ReadWriteMany', // root disk only support LHv1 volume, should be RWX
|
||||||
bus,
|
bus,
|
||||||
volumeName: '',
|
volumeName: '',
|
||||||
size,
|
size,
|
||||||
@@ -590,7 +585,7 @@ export default {
|
|||||||
const pvcResource = allPVCs.find( (O) => O.id === `${ namespace }/${ volume?.persistentVolumeClaim?.claimName }`);
|
const pvcResource = allPVCs.find( (O) => O.id === `${ namespace }/${ volume?.persistentVolumeClaim?.claimName }`);
|
||||||
|
|
||||||
source = SOURCE_TYPE.ATTACH_VOLUME;
|
source = SOURCE_TYPE.ATTACH_VOLUME;
|
||||||
accessMode = pvcResource?.spec?.accessModes?.[0] || READ_WRITE_MANY;
|
accessMode = pvcResource?.spec?.accessModes?.[0] || 'ReadWriteMany';
|
||||||
size = pvcResource?.spec?.resources?.requests?.storage || '10Gi';
|
size = pvcResource?.spec?.resources?.requests?.storage || '10Gi';
|
||||||
storageClassName = pvcResource?.spec?.storageClassName;
|
storageClassName = pvcResource?.spec?.storageClassName;
|
||||||
volumeMode = pvcResource?.spec?.volumeMode || VOLUME_MODE.BLOCK;
|
volumeMode = pvcResource?.spec?.volumeMode || VOLUME_MODE.BLOCK;
|
||||||
@@ -679,29 +674,12 @@ export default {
|
|||||||
model: I.model,
|
model: I.model,
|
||||||
networkName: isPod ? MANAGEMENT_NETWORK : network?.multus?.networkName,
|
networkName: isPod ? MANAGEMENT_NETWORK : network?.multus?.networkName,
|
||||||
staticIp: annotations[`${ staticIpPrefix }${ I.name }`] || '',
|
staticIp: annotations[`${ staticIpPrefix }${ I.name }`] || '',
|
||||||
networkBoot: !!I.bootOrder,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Network boot devices are placed either before or after all volumes in the boot sequence.
|
|
||||||
* Derive which one from an existing spec, so a round trip through the form keeps it.
|
|
||||||
*/
|
|
||||||
isNetworkBootFirst(spec) {
|
|
||||||
const devices = spec?.template?.spec?.domain?.devices || {};
|
|
||||||
const nicOrders = (devices.interfaces || []).map((I) => I.bootOrder).filter(Boolean);
|
|
||||||
const diskOrders = (devices.disks || []).map((D) => D.bootOrder).filter(Boolean);
|
|
||||||
|
|
||||||
if (nicOrders.length === 0 || diskOrders.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.min(...nicOrders) < Math.min(...diskOrders);
|
|
||||||
},
|
|
||||||
|
|
||||||
parseVM() {
|
parseVM() {
|
||||||
this.userData = this.getUserData({ osType: this.osType, installAgent: this.installAgent });
|
this.userData = this.getUserData({ osType: this.osType, installAgent: this.installAgent });
|
||||||
this.parseOther();
|
this.parseOther();
|
||||||
@@ -914,10 +892,9 @@ export default {
|
|||||||
const disks = [];
|
const disks = [];
|
||||||
const volumes = [];
|
const volumes = [];
|
||||||
const volumeClaimTemplates = [];
|
const volumeClaimTemplates = [];
|
||||||
const bootOrderOffset = this.networkBootFirst ? this.networkRows.filter((R) => R.networkBoot).length : 0;
|
|
||||||
|
|
||||||
disk.forEach( (R, index) => {
|
disk.forEach( (R, index) => {
|
||||||
const _disk = this.parseDisk(R, index + bootOrderOffset);
|
const _disk = this.parseDisk(R, index);
|
||||||
|
|
||||||
disks.push(_disk);
|
disks.push(_disk);
|
||||||
|
|
||||||
@@ -1175,11 +1152,9 @@ export default {
|
|||||||
const networks = [];
|
const networks = [];
|
||||||
const interfaces = [];
|
const interfaces = [];
|
||||||
|
|
||||||
let bootOrder = this.networkBootFirst ? 1 : this.diskRows.length + 1;
|
|
||||||
|
|
||||||
networkRow.forEach( (R) => {
|
networkRow.forEach( (R) => {
|
||||||
const _network = this.parseNetwork(R);
|
const _network = this.parseNetwork(R);
|
||||||
const _interface = this.parseInterface(R, R.networkBoot ? bootOrder++ : undefined);
|
const _interface = this.parseInterface(R);
|
||||||
|
|
||||||
networks.push(_network);
|
networks.push(_network);
|
||||||
interfaces.push(_interface);
|
interfaces.push(_interface);
|
||||||
@@ -1416,9 +1391,8 @@ export default {
|
|||||||
case SOURCE_TYPE.IMAGE: {
|
case SOURCE_TYPE.IMAGE: {
|
||||||
const image = this.images.find( (I) => R.image === I.id);
|
const image = this.images.find( (I) => R.image === I.id);
|
||||||
|
|
||||||
out.spec.storageClassName = R.realName ? R.storageClassName : (R.storageClassName || image?.storageClassName);
|
|
||||||
|
|
||||||
if (image) {
|
if (image) {
|
||||||
|
out.spec.storageClassName = image.storageClassName;
|
||||||
out.metadata.annotations = { [HCI_ANNOTATIONS.IMAGE_ID]: image.id };
|
out.metadata.annotations = { [HCI_ANNOTATIONS.IMAGE_ID]: image.id };
|
||||||
} else {
|
} else {
|
||||||
out.metadata.annotations = { [HCI_ANNOTATIONS.IMAGE_ID]: '' };
|
out.metadata.annotations = { [HCI_ANNOTATIONS.IMAGE_ID]: '' };
|
||||||
@@ -1435,7 +1409,7 @@ export default {
|
|||||||
return arr.map( (id) => this.getSSHValue(id)).filter( (O) => O !== undefined);
|
return arr.map( (id) => this.getSSHValue(id)).filter( (O) => O !== undefined);
|
||||||
},
|
},
|
||||||
|
|
||||||
parseInterface(R, bootOrder) {
|
parseInterface(R) {
|
||||||
const _interface = {};
|
const _interface = {};
|
||||||
const type = R.type;
|
const type = R.type;
|
||||||
|
|
||||||
@@ -1445,10 +1419,6 @@ export default {
|
|||||||
_interface.macAddress = R.macAddress;
|
_interface.macAddress = R.macAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bootOrder) {
|
|
||||||
_interface.bootOrder = bootOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
_interface.model = R.model;
|
_interface.model = R.model;
|
||||||
_interface.name = R.name;
|
_interface.name = R.name;
|
||||||
|
|
||||||
@@ -1956,11 +1926,6 @@ export default {
|
|||||||
delete merged['bridge'];
|
delete merged['bridge'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// boot order is owned by the form, don't inherit a stale one from the old spec
|
|
||||||
if (!iface.bootOrder) {
|
|
||||||
delete merged.bootOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,13 +23,6 @@ const ALLOW_SYSTEM_LABEL_KEYS = [
|
|||||||
|
|
||||||
const HEALTHY = 'healthy';
|
const HEALTHY = 'healthy';
|
||||||
const WARNING = 'warning';
|
const WARNING = 'warning';
|
||||||
const MAINTENANCE_MODE = 'MaintenanceMode';
|
|
||||||
const MAINTENANCE_ERROR = 'Error';
|
|
||||||
const MAINTENANCE_ENTERING_REASONS = ['Validating', 'Draining', 'Evacuating'];
|
|
||||||
const MAINTENANCE_COMPLETED = 'Completed';
|
|
||||||
// Legacy `harvesterhci.io/maintain-status` annotation values (clusters < v1.10)
|
|
||||||
const LEGACY_MAINTENANCE_RUNNING = 'running';
|
|
||||||
const LEGACY_MAINTENANCE_COMPLETED = 'completed';
|
|
||||||
|
|
||||||
export default class HciNode extends HarvesterResource {
|
export default class HciNode extends HarvesterResource {
|
||||||
get _availableActions() {
|
get _availableActions() {
|
||||||
@@ -65,14 +58,6 @@ export default class HciNode extends HarvesterResource {
|
|||||||
total: 1
|
total: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearMaintenance = {
|
|
||||||
action: 'clearMaintenanceMode',
|
|
||||||
enabled: this.hasAction('clearMaintenanceMode'),
|
|
||||||
icon: 'icon icon-fw icon-close',
|
|
||||||
label: this.t('harvester.action.clearMaintenance'),
|
|
||||||
total: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const enableCPUManager = {
|
const enableCPUManager = {
|
||||||
action: 'enableCPUManager',
|
action: 'enableCPUManager',
|
||||||
enabled: this.cpuPinningFeatureEnabled && this.hasAction('enableCPUManager') && !this.isCPUManagerEnableInProgress && !this.isCPUManagerEnabled && !this.isEtcd, // witness node doesn't have CPU manager
|
enabled: this.cpuPinningFeatureEnabled && this.hasAction('enableCPUManager') && !this.isCPUManagerEnableInProgress && !this.isCPUManagerEnabled && !this.isEtcd, // witness node doesn't have CPU manager
|
||||||
@@ -118,7 +103,6 @@ export default class HciNode extends HarvesterResource {
|
|||||||
uncordon,
|
uncordon,
|
||||||
enableMaintenance,
|
enableMaintenance,
|
||||||
disableMaintenance,
|
disableMaintenance,
|
||||||
clearMaintenance,
|
|
||||||
enableCPUManager,
|
enableCPUManager,
|
||||||
disableCPUManager,
|
disableCPUManager,
|
||||||
shutDown,
|
shutDown,
|
||||||
@@ -214,10 +198,6 @@ export default class HciNode extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get stateDisplay() {
|
get stateDisplay() {
|
||||||
if (this.isMaintenanceModeError) {
|
|
||||||
return super.stateDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isEnteringMaintenance) {
|
if (this.isEnteringMaintenance) {
|
||||||
return 'Entering maintenance mode';
|
return 'Entering maintenance mode';
|
||||||
}
|
}
|
||||||
@@ -266,10 +246,6 @@ export default class HciNode extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get stateDescription() {
|
get stateDescription() {
|
||||||
if (this.isMaintenanceModeError) {
|
|
||||||
return super.stateDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentIP = this.metadata?.annotations?.[HCI_ANNOTATIONS.CURRENT_IP];
|
const currentIP = this.metadata?.annotations?.[HCI_ANNOTATIONS.CURRENT_IP];
|
||||||
const initIP = this.metadata?.annotations?.[HCI_ANNOTATIONS.INIT_IP];
|
const initIP = this.metadata?.annotations?.[HCI_ANNOTATIONS.INIT_IP];
|
||||||
|
|
||||||
@@ -349,11 +325,7 @@ export default class HciNode extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disableMaintenanceMode() {
|
disableMaintenanceMode() {
|
||||||
this.doActionGrowl('disableMaintenanceMode', {});
|
this.doAction('disableMaintenanceMode', {});
|
||||||
}
|
|
||||||
|
|
||||||
clearMaintenanceMode() {
|
|
||||||
this.doActionGrowl('clearMaintenanceMode', {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enableCPUManager() {
|
enableCPUManager() {
|
||||||
@@ -371,32 +343,11 @@ export default class HciNode extends HarvesterResource {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get maintenanceModeCondition() {
|
|
||||||
return (this.status?.conditions || []).find(
|
|
||||||
(c) => c.type === MAINTENANCE_MODE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get maintenanceModeConditionsEnabled() {
|
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('maintenanceModeConditions');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clusters < v1.10 only expose the `harvesterhci.io/maintain-status` annotation
|
|
||||||
get legacyMaintenanceStatus() {
|
|
||||||
return this.metadata?.annotations?.[HCI_ANNOTATIONS.MAINTENANCE_STATUS];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isMaintenanceModeError() {
|
|
||||||
return this.maintenanceModeConditionsEnabled && this.maintenanceModeCondition?.reason === MAINTENANCE_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isMigratable() {
|
get isMigratable() {
|
||||||
const states = ['in-progress', 'unavailable'];
|
const states = ['in-progress', 'unavailable'];
|
||||||
|
|
||||||
const engaged = this.maintenanceModeConditionsEnabled ? this.maintenanceModeCondition?.status === 'True' : !!this.legacyMaintenanceStatus;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
!engaged &&
|
!this.metadata?.annotations?.[HCI_ANNOTATIONS.MAINTENANCE_STATUS] &&
|
||||||
!this.isUnSchedulable &&
|
!this.isUnSchedulable &&
|
||||||
!states.includes(this.state)
|
!states.includes(this.state)
|
||||||
);
|
);
|
||||||
@@ -411,26 +362,17 @@ export default class HciNode extends HarvesterResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isEnteringMaintenance() {
|
get isEnteringMaintenance() {
|
||||||
if (!this.maintenanceModeConditionsEnabled) {
|
|
||||||
return this.legacyMaintenanceStatus === LEGACY_MAINTENANCE_RUNNING;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cond = this.maintenanceModeCondition;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
cond?.status === 'True' &&
|
this.metadata?.annotations?.[HCI_ANNOTATIONS.MAINTENANCE_STATUS] ===
|
||||||
MAINTENANCE_ENTERING_REASONS.includes(cond?.reason)
|
'running'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isMaintenance() {
|
get isMaintenance() {
|
||||||
if (!this.maintenanceModeConditionsEnabled) {
|
return (
|
||||||
return this.legacyMaintenanceStatus === LEGACY_MAINTENANCE_COMPLETED;
|
this.metadata?.annotations?.[HCI_ANNOTATIONS.MAINTENANCE_STATUS] ===
|
||||||
}
|
'completed'
|
||||||
|
);
|
||||||
const cond = this.maintenanceModeCondition;
|
|
||||||
|
|
||||||
return cond?.status === 'True' && cond?.reason === MAINTENANCE_COMPLETED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get cpuPinningFeatureEnabled() {
|
get cpuPinningFeatureEnabled() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester",
|
"name": "harvester",
|
||||||
"description": "Rancher UI Extension for Harvester",
|
"description": "Rancher UI Extension for Harvester",
|
||||||
"version": "1.10.0-dev",
|
"version": "1.9.1-rc3",
|
||||||
"private": false,
|
"private": false,
|
||||||
"rancher": {
|
"rancher": {
|
||||||
"annotations": {
|
"annotations": {
|
||||||
|
|||||||
@@ -11,12 +11,3 @@ export function parseVolumeClaimTemplates(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EMPTY_IMAGE = 'EMPTY_IMAGE';
|
export const EMPTY_IMAGE = 'EMPTY_IMAGE';
|
||||||
|
|
||||||
/**
|
|
||||||
* Disks and interfaces that take part in the boot sequence, sorted by boot order.
|
|
||||||
*/
|
|
||||||
export function getBootDevices(devices = {}) {
|
|
||||||
return [...(devices.disks || []), ...(devices.interfaces || [])]
|
|
||||||
.filter((device) => !!device.bootOrder)
|
|
||||||
.sort((a, b) => a.bootOrder - b.bootOrder);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user