mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-07 06:36:09 +08:00
Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f08e632aad | ||
|
|
df1d7001bc | ||
|
|
41f78c83a7 | ||
|
|
f1a53311ce | ||
|
|
e0c6e905a9 | ||
|
|
1852e434f1 | ||
|
|
77c085d3a7 | ||
|
|
9dbe7d9015 | ||
|
|
3f19494d98 | ||
|
|
296601d38c | ||
|
|
5d9ba584b8 | ||
|
|
eab4ba3d0f | ||
|
|
4e5320dc72 | ||
|
|
ba7092f400 | ||
|
|
eec7c3573d | ||
|
|
f0014cc627 | ||
|
|
9aa311a7e3 | ||
|
|
013d9f1d7e | ||
|
|
8c044ba44c | ||
|
|
7b8592e07d | ||
|
|
53d2e67b4f | ||
|
|
1d146a40bd | ||
|
|
11d6943596 | ||
|
|
7b754457ec | ||
|
|
2cb838eded | ||
|
|
bdcb34ab14 | ||
|
|
8daf44b3f6 | ||
|
|
8044e74102 | ||
|
|
264d65cc6d | ||
|
|
342a82c8e0 | ||
|
|
e7cd5cc4b0 | ||
|
|
cf405be71e | ||
|
|
911d30d7ba | ||
|
|
1b4bfa0962 | ||
|
|
d0f6f18a8a |
@@ -14,4 +14,3 @@ dist-pkg
|
|||||||
pkg/harvester/index.ts
|
pkg/harvester/index.ts
|
||||||
pkg/harvester/store/*
|
pkg/harvester/store/*
|
||||||
scripts/pkgfile.js
|
scripts/pkgfile.js
|
||||||
tmp
|
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ numberOfReviewers: 0
|
|||||||
reviewers:
|
reviewers:
|
||||||
- a110605
|
- a110605
|
||||||
- houhoucoop
|
- houhoucoop
|
||||||
|
- NickChungSUSE
|
||||||
+31
-12
@@ -1,14 +1,33 @@
|
|||||||
pull_request_rules:
|
pull_request_rules:
|
||||||
- name: Ask to resolve conflict
|
- name: Automatically open backport PR to release-harvester-v1.5
|
||||||
conditions:
|
conditions:
|
||||||
- conflict
|
- base=main
|
||||||
actions:
|
- label="require backport 1.5"
|
||||||
comment:
|
actions:
|
||||||
message: This pull request is now in conflict. Could you fix it @{{author}}? 🙏
|
backport:
|
||||||
|
branches:
|
||||||
|
- "release-harvester-v1.5"
|
||||||
|
assignees:
|
||||||
|
- "{{ author }}"
|
||||||
|
labels:
|
||||||
|
- "v1.5 backport PR"
|
||||||
|
|
||||||
commands_restrictions:
|
- name: Automatically open backport PR to release-harvester-v1.0
|
||||||
backport:
|
conditions:
|
||||||
conditions:
|
- base=main
|
||||||
- or:
|
- label="require backport 1.0"
|
||||||
- sender-permission>=write
|
actions:
|
||||||
- sender=github-actions[bot]
|
backport:
|
||||||
|
branches:
|
||||||
|
- "release-harvester-v1.0"
|
||||||
|
assignees:
|
||||||
|
- "{{ author }}"
|
||||||
|
labels:
|
||||||
|
- "v1.0 backport PR"
|
||||||
|
|
||||||
|
- name: Ask to resolve conflict
|
||||||
|
conditions:
|
||||||
|
- conflict
|
||||||
|
actions:
|
||||||
|
comment:
|
||||||
|
message: This pull request is now in conflict. Could you fix it @{{author}}? 🙏
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# .github/release.yml
|
|
||||||
changelog:
|
|
||||||
categories:
|
|
||||||
- title: Features
|
|
||||||
labels:
|
|
||||||
- feat
|
|
||||||
- title: Bug Fixes
|
|
||||||
labels:
|
|
||||||
- fix
|
|
||||||
- title: Dependencies
|
|
||||||
labels:
|
|
||||||
- deps
|
|
||||||
- title: Performance
|
|
||||||
labels:
|
|
||||||
- perf
|
|
||||||
- title: Documentation
|
|
||||||
labels:
|
|
||||||
- docs
|
|
||||||
- title: Style
|
|
||||||
labels:
|
|
||||||
- style
|
|
||||||
- title: Build & Continuous Integration
|
|
||||||
labels:
|
|
||||||
- build
|
|
||||||
- ci
|
|
||||||
- title: Security
|
|
||||||
labels:
|
|
||||||
- security
|
|
||||||
- title: Tests
|
|
||||||
labels:
|
|
||||||
- test
|
|
||||||
- title: Refactoring
|
|
||||||
labels:
|
|
||||||
- refactor
|
|
||||||
- title: Chores
|
|
||||||
labels:
|
|
||||||
- chore
|
|
||||||
- title: Others
|
|
||||||
labels:
|
|
||||||
- other
|
|
||||||
@@ -1,17 +1,15 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["config:recommended"],
|
||||||
"config:recommended",
|
|
||||||
":semanticCommitTypeAll(deps)",
|
|
||||||
":semanticCommitScopeDisabled"
|
|
||||||
],
|
|
||||||
"baseBranches": [
|
"baseBranches": [
|
||||||
|
"master",
|
||||||
"main",
|
"main",
|
||||||
"/^release-harvester-v\\d+\\.\\d+$/"
|
"/^release-harvester-v\\d+\\.\\d+$/"
|
||||||
],
|
],
|
||||||
"automergeMajor": false,
|
"automergeMajor": false,
|
||||||
"semanticCommits": "enabled",
|
"semanticCommits": "enabled",
|
||||||
"semanticCommitType": "deps",
|
"semanticCommitType": "chore",
|
||||||
|
"semanticCommitScope": "deps",
|
||||||
"prHourlyLimit": 12,
|
"prHourlyLimit": 12,
|
||||||
"timezone": "Asia/Taipei",
|
"timezone": "Asia/Taipei",
|
||||||
"schedule": ["after 10am on sunday"],
|
"schedule": ["after 10am on sunday"],
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
name: "[PR Management] Request Backport via Mergify"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [closed]
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
comment-backport:
|
|
||||||
if: github.event.pull_request.merged == true
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.base_ref }}
|
|
||||||
|
|
||||||
- name: Post Mergify backport command
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
pr_number=${{ github.event.pull_request.number }}
|
|
||||||
repo="${{ github.repository }}"
|
|
||||||
|
|
||||||
labels_json='${{ toJson(github.event.pull_request.labels.*.name) }}'
|
|
||||||
labels=$(echo "$labels_json" | jq -r '.[] // empty')
|
|
||||||
|
|
||||||
echo "Labels on PR: $labels"
|
|
||||||
|
|
||||||
matches=$(echo "$labels" | grep -oE '^require backport/v[0-9]+\.[0-9]+$' || true)
|
|
||||||
|
|
||||||
if [[ -z "$matches" ]]; then
|
|
||||||
echo "No back‑port labels found — skipping."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
branches=$(echo "$matches" \
|
|
||||||
| sed -E 's/^require backport\/v/release-harvester-v/' \
|
|
||||||
| sort -u | tr '\n' ' ')
|
|
||||||
branches=${branches%% }
|
|
||||||
|
|
||||||
cmd="@Mergifyio backport $branches"
|
|
||||||
echo "Posting Mergify command: $cmd"
|
|
||||||
gh pr comment "$pr_number" --repo "$repo" --body "$cmd"
|
|
||||||
@@ -132,7 +132,7 @@ jobs:
|
|||||||
git push origin ${{ needs.setup-target-branch.outputs.target_branch }}
|
git push origin ${{ needs.setup-target-branch.outputs.target_branch }}
|
||||||
|
|
||||||
- name: Run Helm chart releaser
|
- name: Run Helm chart releaser
|
||||||
uses: helm/chart-releaser-action@v1.7.0
|
uses: helm/chart-releaser-action@v1.4.1
|
||||||
with:
|
with:
|
||||||
charts_dir: ./charts
|
charts_dir: ./charts
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
name: "[PR Management] Add PR Label"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, reopened, edited]
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- 'release-harvester-v*'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auto-assign-pr-label:
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.base_ref }}
|
|
||||||
- name: Setup Nodejs and yarn install
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
- name: Set PR label
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
PR_LABEL=$(node ./scripts/extract-release-label.mjs "${{ github.event.pull_request.title }}")
|
|
||||||
echo "PR_LABEL = $PR_LABEL"
|
|
||||||
gh pr edit ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --add-label "$PR_LABEL"
|
|
||||||
@@ -7,7 +7,6 @@ on:
|
|||||||
- main
|
- main
|
||||||
- 'release-*'
|
- 'release-*'
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, reopened, edited, synchronize]
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- 'release-*'
|
- 'release-*'
|
||||||
@@ -23,11 +22,6 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: ./.github/actions/lint
|
uses: ./.github/actions/lint
|
||||||
|
|
||||||
- name: Validate PR title lint
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
shell: bash
|
|
||||||
run: echo "${{ github.event.pull_request.title }}" | npx commitlint
|
|
||||||
|
|
||||||
- name: Validate commit messages
|
- name: Validate commit messages
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -50,7 +44,6 @@ jobs:
|
|||||||
|
|
||||||
echo "FROM=$FROM"
|
echo "FROM=$FROM"
|
||||||
echo "TO=$TO"
|
echo "TO=$TO"
|
||||||
|
|
||||||
npx commitlint --from "$FROM" --to "$TO" --verbose
|
npx commitlint --from "$FROM" --to "$TO" --verbose
|
||||||
env:
|
env:
|
||||||
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ To validate changes in a release series, switch to the appropriate `vX.Y-head` b
|
|||||||
- Examples:
|
- Examples:
|
||||||
- Test `1.0.x` series → `v1.0-head`
|
- Test `1.0.x` series → `v1.0-head`
|
||||||
- Test `1.5.x` series → `v1.5-head`
|
- Test `1.5.x` series → `v1.5-head`
|
||||||
|
- Test `1.6.x` series → `v1.6-head`
|
||||||
|
|
||||||
**Steps:**
|
**Steps:**
|
||||||
1. Navigate to **Rancher UI** → **Local** → **App** → **Repositories**
|
1. Navigate to **Rancher UI** → **Local** → **App** → **Repositories**
|
||||||
|
|||||||
+10
-11
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester-ui-extension",
|
"name": "harvester-ui-extension",
|
||||||
"version": "1.7.0-dev",
|
"version": "1.6.0-rc4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-class-static-block": "7.28.3",
|
"@babel/plugin-transform-class-static-block": "7.26.0",
|
||||||
"@rancher/shell": "3.0.5-rc.8",
|
"@rancher/shell": "3.0.5-rc.7",
|
||||||
"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",
|
||||||
@@ -16,21 +16,21 @@
|
|||||||
"yaml": "^2.5.1"
|
"yaml": "^2.5.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/node": "~20.19.0",
|
"@types/node": "~20.10.0",
|
||||||
"cronstrue": "2.59.0",
|
"cronstrue": "2.50.0",
|
||||||
"d3-color": "3.1.0",
|
"d3-color": "3.1.0",
|
||||||
"ejs": "3.1.10",
|
"ejs": "3.1.10",
|
||||||
"follow-redirects": "1.15.11",
|
"follow-redirects": "1.15.6",
|
||||||
"glob": "7.2.3",
|
"glob": "7.2.3",
|
||||||
"glob-parent": "6.0.2",
|
"glob-parent": "6.0.2",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"@types/lodash": "4.17.20",
|
"@types/lodash": "4.17.5",
|
||||||
"merge": "2.1.1",
|
"merge": "2.1.1",
|
||||||
"node-forge": "1.3.1",
|
"node-forge": "1.3.1",
|
||||||
"nth-check": "2.1.1",
|
"nth-check": "2.1.1",
|
||||||
"qs": "6.14.0",
|
"qs": "6.11.1",
|
||||||
"roarr": "7.21.1",
|
"roarr": "7.0.4",
|
||||||
"semver": "7.7.2",
|
"semver": "7.5.4",
|
||||||
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/load": "^19.8.1",
|
|
||||||
"@commitlint/cli": "^19.8.1",
|
"@commitlint/cli": "^19.8.1",
|
||||||
"@commitlint/config-conventional": "^19.8.1",
|
"@commitlint/config-conventional": "^19.8.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const CATEGORY = {
|
|||||||
ui: [
|
ui: [
|
||||||
'branding',
|
'branding',
|
||||||
'ui-source',
|
'ui-source',
|
||||||
|
'ui-plugin-index',
|
||||||
'ui-index',
|
'ui-index',
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -257,12 +257,8 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
reconnect() {
|
|
||||||
this.$refs.novncConsole.reconnect();
|
|
||||||
},
|
|
||||||
|
|
||||||
softReboot() {
|
softReboot() {
|
||||||
this.vmResource.doSoftReboot();
|
this.vmResource.softrebootVM();
|
||||||
},
|
},
|
||||||
|
|
||||||
showKeysModal() {
|
showKeysModal() {
|
||||||
@@ -310,13 +306,6 @@ export default {
|
|||||||
{{ t("harvester.action.softreboot") }}
|
{{ t("harvester.action.softreboot") }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm bg-primary"
|
|
||||||
@click="reconnect"
|
|
||||||
>
|
|
||||||
{{ t("harvester.action.reconnect") }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<v-dropdown
|
<v-dropdown
|
||||||
v-if="!hideCustomKeysBar"
|
v-if="!hideCustomKeysBar"
|
||||||
ref="customKeyPopover"
|
ref="customKeyPopover"
|
||||||
@@ -383,12 +372,3 @@ export default {
|
|||||||
background: rgb(40, 40, 40);
|
background: rgb(40, 40, 40);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.vm-console .v-popper__arrow-container {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.vm-console .v-popper__popper{
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
// suffix of doc link, see utils/feature-flags.js how to get complete doc link
|
// suffix of doc link, see utils/feature-flags.js how to get complete doc link
|
||||||
export const DOC = {
|
export const DOC = {
|
||||||
CONSOLE_URL: `/host/#remote-console`,
|
CONSOLE_URL: `/host/#remote-console`,
|
||||||
RANCHER_INTEGRATION_URL: `/rancher/rancher-integration`,
|
RANCHER_INTEGRATION_URL: `/rancher/rancher-integration`,
|
||||||
KSMTUNED_MODE: `/host/#ksmtuned-mode`,
|
KSMTUNED_MODE: `/host/#ksmtuned-mode`,
|
||||||
UPGRADE_URL: `/upgrade/index`,
|
UPGRADE_URL: `/upgrade/index`,
|
||||||
UPGRADE_CONFIG_URL: `/advanced/index#upgrade-config`,
|
UPGRADE_CONFIG_URL: `/advanced/index#upgrade-config`,
|
||||||
STORAGE_NETWORK_EXAMPLE: `/advanced/storagenetwork#configuration-example`,
|
STORAGE_NETWORK_EXAMPLE: `/advanced/storagenetwork#configuration-example`,
|
||||||
SUPPORT_BUNDLE_NAMESPACES: `/advanced/index/#support-bundle-namespaces`,
|
SUPPORT_BUNDLE_NAMESPACES: `/advanced/index/#support-bundle-namespaces`,
|
||||||
VPC_CONFIGURATION_EXAMPLES: `/networking/kubeovn-vpc#vpc-peering-configuration-examples`,
|
|
||||||
NETWORK_POLICY: `/networking/kubeovn-vm-isolation/#network-policies`,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const FEATURE_FLAGS = {
|
|||||||
],
|
],
|
||||||
'v1.5.1': [],
|
'v1.5.1': [],
|
||||||
'v1.6.0': [
|
'v1.6.0': [
|
||||||
|
'vmMachineTypes',
|
||||||
'customSupportBundle',
|
'customSupportBundle',
|
||||||
'csiOnlineExpandValidation',
|
'csiOnlineExpandValidation',
|
||||||
'vmNetworkMigration',
|
'vmNetworkMigration',
|
||||||
@@ -47,10 +48,6 @@ const FEATURE_FLAGS = {
|
|||||||
'cpuMemoryHotplug',
|
'cpuMemoryHotplug',
|
||||||
'cdiSettings',
|
'cdiSettings',
|
||||||
'vmCloneRunStrategy',
|
'vmCloneRunStrategy',
|
||||||
],
|
|
||||||
'v1.6.1': [],
|
|
||||||
'v1.7.0': [
|
|
||||||
'vmMachineTypeAuto'
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
LOGGING,
|
LOGGING,
|
||||||
STORAGE_CLASS,
|
STORAGE_CLASS,
|
||||||
SECRET,
|
SECRET,
|
||||||
NETWORK_POLICY
|
|
||||||
} from '@shell/config/types';
|
} from '@shell/config/types';
|
||||||
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
import { HCI, VOLUME_SNAPSHOT } from '../types';
|
||||||
import {
|
import {
|
||||||
@@ -426,7 +425,6 @@ export function init($plugin, store) {
|
|||||||
HCI.CLUSTER_NETWORK,
|
HCI.CLUSTER_NETWORK,
|
||||||
HCI.NETWORK_ATTACHMENT,
|
HCI.NETWORK_ATTACHMENT,
|
||||||
HCI.VPC,
|
HCI.VPC,
|
||||||
NETWORK_POLICY,
|
|
||||||
HCI.LB,
|
HCI.LB,
|
||||||
HCI.IP_POOL,
|
HCI.IP_POOL,
|
||||||
],
|
],
|
||||||
@@ -568,21 +566,6 @@ export function init($plugin, store) {
|
|||||||
ifHaveType: HCI.VPC,
|
ifHaveType: HCI.VPC,
|
||||||
});
|
});
|
||||||
|
|
||||||
configureType(NETWORK_POLICY, { hiddenNamespaceGroupButton: true, canYaml: false });
|
|
||||||
|
|
||||||
virtualType({
|
|
||||||
labelKey: 'harvester.networkPolicy.label',
|
|
||||||
name: NETWORK_POLICY,
|
|
||||||
namespaced: true,
|
|
||||||
weight: 186,
|
|
||||||
route: {
|
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
|
||||||
params: { resource: NETWORK_POLICY }
|
|
||||||
},
|
|
||||||
exact: false,
|
|
||||||
ifHaveType: NETWORK_POLICY,
|
|
||||||
});
|
|
||||||
|
|
||||||
configureType(HCI.SNAPSHOT, {
|
configureType(HCI.SNAPSHOT, {
|
||||||
isCreatable: false,
|
isCreatable: false,
|
||||||
location: {
|
location: {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { HCI } from '../types';
|
|
||||||
|
|
||||||
// TODO: delete this not used variable
|
// TODO: delete this not used variable
|
||||||
export const MemoryUnit = [{
|
export const MemoryUnit = [{
|
||||||
label: 'Mi',
|
label: 'Mi',
|
||||||
@@ -83,14 +81,3 @@ export const CSI_SECRETS = {
|
|||||||
CSI_NODE_STAGE_SECRET_NAME: 'csi.storage.k8s.io/node-stage-secret-name',
|
CSI_NODE_STAGE_SECRET_NAME: 'csi.storage.k8s.io/node-stage-secret-name',
|
||||||
CSI_NODE_STAGE_SECRET_NAMESPACE: 'csi.storage.k8s.io/node-stage-secret-namespace',
|
CSI_NODE_STAGE_SECRET_NAMESPACE: 'csi.storage.k8s.io/node-stage-secret-namespace',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Some harvester CRD type is not equal to model file name, define the mapping here
|
|
||||||
export const HARVESTER_CRD_MAP = {
|
|
||||||
node: HCI.HOST,
|
|
||||||
configmap: HCI.CLOUD_TEMPLATE,
|
|
||||||
persistentvolumeclaim: HCI.VOLUME,
|
|
||||||
'snapshot.storage.k8s.io.volumesnapshot': HCI.SNAPSHOT,
|
|
||||||
// specific groupable table detail page
|
|
||||||
'network.harvesterhci.io.vlanconfig': HCI.CLUSTER_NETWORK,
|
|
||||||
'kubeovn.io.subnet': HCI.VPC
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const HCI_SETTING = {
|
|||||||
LOG_LEVEL: 'log-level',
|
LOG_LEVEL: 'log-level',
|
||||||
SERVER_VERSION: 'server-version',
|
SERVER_VERSION: 'server-version',
|
||||||
UI_INDEX: 'ui-index',
|
UI_INDEX: 'ui-index',
|
||||||
|
UI_PLUGIN_INDEX: 'ui-plugin-index',
|
||||||
UPGRADE_CHECKER_ENABLED: 'upgrade-checker-enabled',
|
UPGRADE_CHECKER_ENABLED: 'upgrade-checker-enabled',
|
||||||
UPGRADE_CHECKER_URL: 'upgrade-checker-url',
|
UPGRADE_CHECKER_URL: 'upgrade-checker-url',
|
||||||
VLAN: 'vlan',
|
VLAN: 'vlan',
|
||||||
@@ -83,6 +84,7 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
[HCI_SETTING.SUPPORT_BUNDLE_NAMESPACES]: { from: 'import', canReset: true },
|
[HCI_SETTING.SUPPORT_BUNDLE_NAMESPACES]: { from: 'import', canReset: true },
|
||||||
[HCI_SETTING.AUTO_DISK_PROVISION_PATHS]: { canReset: true },
|
[HCI_SETTING.AUTO_DISK_PROVISION_PATHS]: { canReset: true },
|
||||||
[HCI_SETTING.RELEASE_DOWNLOAD_URL]: { kind: 'url' },
|
[HCI_SETTING.RELEASE_DOWNLOAD_URL]: { kind: 'url' },
|
||||||
|
[HCI_SETTING.UI_PLUGIN_INDEX]: { kind: 'url' },
|
||||||
[HCI_SETTING.CONTAINERD_REGISTRY]: {
|
[HCI_SETTING.CONTAINERD_REGISTRY]: {
|
||||||
kind: 'json', from: 'import', canReset: true
|
kind: 'json', from: 'import', canReset: true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import { Checkbox } from '@components/Form/Checkbox';
|
|||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import { DOC } from '../../config/doc-links';
|
import { DOC } from '../../config/doc-links';
|
||||||
import { docLink } from '../../utils/feature-flags';
|
import { docLink } from '../../utils/feature-flags';
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
import { clone } from '@shell/utils/object';
|
|
||||||
|
|
||||||
export const ksmtunedMode = [{
|
export const ksmtunedMode = [{
|
||||||
value: 'standard',
|
value: 'standard',
|
||||||
@@ -66,13 +64,11 @@ export default {
|
|||||||
|
|
||||||
this.enableMergeAcrossNodes = !!this.ksmtuned.spec?.mergeAcrossNodes;
|
this.enableMergeAcrossNodes = !!this.ksmtuned.spec?.mergeAcrossNodes;
|
||||||
this.spec = this.ksmtuned.spec;
|
this.spec = this.ksmtuned.spec;
|
||||||
this.originSpec = clone(this.ksmtuned.spec);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ksmtuned: {},
|
ksmtuned: {},
|
||||||
originSpec: {},
|
|
||||||
spec: {},
|
spec: {},
|
||||||
thresCoef: 30,
|
thresCoef: 30,
|
||||||
ksmtunedMode,
|
ksmtunedMode,
|
||||||
@@ -103,11 +99,6 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async saveKsmtuned() {
|
async saveKsmtuned() {
|
||||||
// no ksmtuned spec change, skip save
|
|
||||||
if (isEqual(this.spec, this.originSpec)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.spec.mergeAcrossNodes = this.enableMergeAcrossNodes ? 1 : 0;
|
this.spec.mergeAcrossNodes = this.enableMergeAcrossNodes ? 1 : 0;
|
||||||
this.ksmtuned['spec'] = this.spec;
|
this.ksmtuned['spec'] = this.spec;
|
||||||
|
|
||||||
|
|||||||
@@ -527,6 +527,7 @@ export default {
|
|||||||
id: this.longhornNode.id,
|
id: this.longhornNode.id,
|
||||||
opt: { force: true },
|
opt: { force: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, '5000'));
|
await new Promise((resolve) => setTimeout(resolve, '5000'));
|
||||||
await retrySave();
|
await retrySave();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { HCI } from '../types';
|
|||||||
import { sortBy } from '@shell/utils/sort';
|
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';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateVMSchedule',
|
name: 'CreateVMSchedule',
|
||||||
@@ -94,7 +93,7 @@ export default {
|
|||||||
return this.settings.find( (O) => O.id === 'backup-target');
|
return this.settings.find( (O) => O.id === 'backup-target');
|
||||||
},
|
},
|
||||||
isEmptyValue() {
|
isEmptyValue() {
|
||||||
return isBackupTargetSettingEmpty(this.backupTargetResource);
|
return this.getBackupTargetValueIsEmpty(this.backupTargetResource);
|
||||||
},
|
},
|
||||||
canUpdate() {
|
canUpdate() {
|
||||||
return this?.backupTargetResource?.canUpdate;
|
return this?.backupTargetResource?.canUpdate;
|
||||||
@@ -110,7 +109,7 @@ export default {
|
|||||||
!!this.value.spec.maxFailure;
|
!!this.value.spec.maxFailure;
|
||||||
},
|
},
|
||||||
isBackupTargetUnAvailable() {
|
isBackupTargetUnAvailable() {
|
||||||
return this.value.spec.vmbackup.type === BACKUP_TYPE.BACKUP && isBackupTargetSettingUnavailable(this.backupTargetResource);
|
return this.value.spec.vmbackup.type === BACKUP_TYPE.BACKUP && (this.errorMessage || this.isEmptyValue) && this.canUpdate;
|
||||||
},
|
},
|
||||||
vmOptions() {
|
vmOptions() {
|
||||||
const nsVmList = this.$store.getters['harvester/all'](HCI.VM).filter((vm) => vm.metadata.namespace === this.value.metadata.namespace);
|
const nsVmList = this.$store.getters['harvester/all'](HCI.VM).filter((vm) => vm.metadata.namespace === this.value.metadata.namespace);
|
||||||
@@ -167,6 +166,20 @@ export default {
|
|||||||
onTypeChange(newType) {
|
onTypeChange(newType) {
|
||||||
this.value.metadata.name = `svm${ newType }-${ this.value.spec.vmbackup.source.name }`;
|
this.value.metadata.name = `svm${ newType }-${ this.value.spec.vmbackup.source.name }`;
|
||||||
},
|
},
|
||||||
|
getBackupTargetValueIsEmpty(resource) {
|
||||||
|
let out = true;
|
||||||
|
|
||||||
|
if (resource?.value) {
|
||||||
|
try {
|
||||||
|
const valueJson = JSON.parse(resource?.value);
|
||||||
|
|
||||||
|
out = !valueJson.type;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
validateFailure(count) {
|
validateFailure(count) {
|
||||||
if (this.value.spec.retain && count > this.value.spec.retain) {
|
if (this.value.spec.retain && count > this.value.spec.retain) {
|
||||||
this.value.spec['maxFailure'] = this.value.spec.retain;
|
this.value.spec['maxFailure'] = this.value.spec.retain;
|
||||||
|
|||||||
@@ -44,12 +44,6 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await allHash(hash);
|
await allHash(hash);
|
||||||
|
|
||||||
if (this.mode === _CREATE ) {
|
|
||||||
this.setDefaultVolumeSnapshotClass();
|
|
||||||
} else {
|
|
||||||
this.initCDISettingsFromAnnotations();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -65,6 +59,14 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.mode === _CREATE ) {
|
||||||
|
this.setDefaultVolumeSnapshotClass();
|
||||||
|
} else {
|
||||||
|
this.initCDISettingsFromAnnotations();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
inStore() {
|
inStore() {
|
||||||
return this.$store.getters['currentProduct'].inStore;
|
return this.$store.getters['currentProduct'].inStore;
|
||||||
@@ -146,7 +148,7 @@ export default {
|
|||||||
setDefaultVolumeSnapshotClass() {
|
setDefaultVolumeSnapshotClass() {
|
||||||
try {
|
try {
|
||||||
const setting = this.$store.getters[`${ this.inStore }/byId`](HCI.SETTING, HCI_SETTING.CSI_DRIVER_CONFIG);
|
const setting = this.$store.getters[`${ this.inStore }/byId`](HCI.SETTING, HCI_SETTING.CSI_DRIVER_CONFIG);
|
||||||
const config = JSON.parse(setting?.value || setting?.default || '{}');
|
const config = JSON.parse(setting?.value || '{}');
|
||||||
const defaultClass = config?.[this.provisioner]?.volumeSnapshotClassName || null;
|
const defaultClass = config?.[this.provisioner]?.volumeSnapshotClassName || null;
|
||||||
|
|
||||||
const allClasses = this.$store.getters[`${ this.inStore }/all`](VOLUME_SNAPSHOT_CLASS) || [];
|
const allClasses = this.$store.getters[`${ this.inStore }/all`](VOLUME_SNAPSHOT_CLASS) || [];
|
||||||
|
|||||||
@@ -297,26 +297,18 @@ export default {
|
|||||||
|
|
||||||
if (Object.keys(volumeModeAccessModes).length > 0) {
|
if (Object.keys(volumeModeAccessModes).length > 0) {
|
||||||
annotations[HCI_ANNOTATIONS.VOLUME_MODE_ACCESS_MODES] = JSON.stringify(volumeModeAccessModes);
|
annotations[HCI_ANNOTATIONS.VOLUME_MODE_ACCESS_MODES] = JSON.stringify(volumeModeAccessModes);
|
||||||
} else {
|
|
||||||
delete annotations[HCI_ANNOTATIONS.VOLUME_MODE_ACCESS_MODES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.cdiSettings.volumeSnapshotClass) {
|
if (this.cdiSettings.volumeSnapshotClass) {
|
||||||
annotations[HCI_ANNOTATIONS.VOLUME_SNAPSHOT_CLASS] = this.cdiSettings.volumeSnapshotClass;
|
annotations[HCI_ANNOTATIONS.VOLUME_SNAPSHOT_CLASS] = this.cdiSettings.volumeSnapshotClass;
|
||||||
} else {
|
|
||||||
delete annotations[HCI_ANNOTATIONS.VOLUME_SNAPSHOT_CLASS];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.cdiSettings.cloneStrategy) {
|
if (this.cdiSettings.cloneStrategy) {
|
||||||
annotations[HCI_ANNOTATIONS.CLONE_STRATEGY] = this.cdiSettings.cloneStrategy;
|
annotations[HCI_ANNOTATIONS.CLONE_STRATEGY] = this.cdiSettings.cloneStrategy;
|
||||||
} else {
|
|
||||||
delete annotations[HCI_ANNOTATIONS.CLONE_STRATEGY];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.cdiSettings.filesystemOverhead) {
|
if (this.cdiSettings.filesystemOverhead) {
|
||||||
annotations[HCI_ANNOTATIONS.FILESYSTEM_OVERHEAD] = this.cdiSettings.filesystemOverhead;
|
annotations[HCI_ANNOTATIONS.FILESYSTEM_OVERHEAD] = this.cdiSettings.filesystemOverhead;
|
||||||
} else {
|
|
||||||
delete annotations[HCI_ANNOTATIONS.FILESYSTEM_OVERHEAD];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.value.metadata.annotations = annotations;
|
this.value.metadata.annotations = annotations;
|
||||||
|
|||||||
@@ -257,7 +257,6 @@ export default {
|
|||||||
:label="t('harvester.vmTemplate.tabs.basics')"
|
:label="t('harvester.vmTemplate.tabs.basics')"
|
||||||
>
|
>
|
||||||
<CpuMemory
|
<CpuMemory
|
||||||
class="mb-20"
|
|
||||||
:cpu="cpu"
|
:cpu="cpu"
|
||||||
:memory="memory"
|
:memory="memory"
|
||||||
:max-cpu="maxCpu"
|
:max-cpu="maxCpu"
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
<script>
|
|
||||||
import debounce from 'lodash/debounce';
|
|
||||||
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
|
||||||
import { removeAt } from '@shell/utils/array';
|
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import InfoBox from '@shell/components/InfoBox';
|
|
||||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
||||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'AccessControlList',
|
|
||||||
|
|
||||||
emits: ['update:value'],
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Banner, InfoBox, LabeledSelect, LabeledInput
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Array,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
mode: {
|
|
||||||
type: String,
|
|
||||||
default: _EDIT,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
const rows = (this.value || []).map((row) => {
|
|
||||||
return {
|
|
||||||
action: row.action || '',
|
|
||||||
direction: row.direction || '',
|
|
||||||
priority: row.priority || 0,
|
|
||||||
match: row.match || '',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return { rows };
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isView() {
|
|
||||||
return this.mode === _VIEW;
|
|
||||||
},
|
|
||||||
|
|
||||||
actionOptions() {
|
|
||||||
return [
|
|
||||||
{ label: 'Allow', value: 'allow' },
|
|
||||||
{ label: 'Drop', value: 'drop' },
|
|
||||||
{ label: 'Pass', value: 'pass' },
|
|
||||||
{ label: 'Reject', value: 'reject' },
|
|
||||||
{ label: 'Allow-related', value: 'allow-related' },
|
|
||||||
{ label: 'Allow-stateless', value: 'allow-stateless' },
|
|
||||||
];
|
|
||||||
},
|
|
||||||
directionOptions() {
|
|
||||||
return [
|
|
||||||
{ label: 'To-lport', value: 'to-lport' },
|
|
||||||
{ label: 'From-lport', value: 'from-lport' },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.queueUpdate = debounce(this.update, 100);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
add() {
|
|
||||||
this.rows.push({
|
|
||||||
action: '',
|
|
||||||
direction: '',
|
|
||||||
priority: this.rows.length,
|
|
||||||
match: '',
|
|
||||||
});
|
|
||||||
this.queueUpdate();
|
|
||||||
},
|
|
||||||
|
|
||||||
removeRule(idx) {
|
|
||||||
removeAt(this.rows, idx);
|
|
||||||
this.queueUpdate();
|
|
||||||
},
|
|
||||||
|
|
||||||
update() {
|
|
||||||
if (this.isView) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('update:value', this.rows);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<Banner
|
|
||||||
v-if="rows.length > 0"
|
|
||||||
color="info"
|
|
||||||
>
|
|
||||||
<t
|
|
||||||
k="harvester.subnet.acl.banner"
|
|
||||||
:raw="true"
|
|
||||||
/>
|
|
||||||
</Banner>
|
|
||||||
<div
|
|
||||||
v-for="(row, idx) in rows"
|
|
||||||
:key="idx"
|
|
||||||
>
|
|
||||||
<InfoBox class="box">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="role-link btn btn-sm removeBtn"
|
|
||||||
:disabled="isView"
|
|
||||||
@click="removeRule(idx)"
|
|
||||||
>
|
|
||||||
<i class="icon icon-x" />
|
|
||||||
</button>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col span-4">
|
|
||||||
<LabeledSelect
|
|
||||||
v-model:value="row.action"
|
|
||||||
:mode="mode"
|
|
||||||
class="mt-5 ml-5"
|
|
||||||
:options="actionOptions"
|
|
||||||
:required="true"
|
|
||||||
:label="t('harvester.subnet.acl.action.label')"
|
|
||||||
:placeholder="t('harvester.subnet.acl.action.placeholder')"
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col span-4">
|
|
||||||
<LabeledSelect
|
|
||||||
v-model:value="row.direction"
|
|
||||||
:mode="mode"
|
|
||||||
:options="directionOptions"
|
|
||||||
class="mt-5"
|
|
||||||
:required="true"
|
|
||||||
:label="t('harvester.subnet.acl.direction.label')"
|
|
||||||
:placeholder="t('harvester.subnet.acl.direction.placeholder')"
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col span-3">
|
|
||||||
<LabeledInput
|
|
||||||
v-model:value.number="row.priority"
|
|
||||||
type="number"
|
|
||||||
class="mb-20 mt-5"
|
|
||||||
:max="32767"
|
|
||||||
:min="0"
|
|
||||||
:mode="mode"
|
|
||||||
required
|
|
||||||
label-key="harvester.subnet.acl.priority.label"
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col span-11">
|
|
||||||
<LabeledInput
|
|
||||||
v-model:value="row.match"
|
|
||||||
class="mb-5 ml-5"
|
|
||||||
:mode="mode"
|
|
||||||
required
|
|
||||||
:placeholder="t('harvester.subnet.acl.match.placeholder')"
|
|
||||||
label-key="harvester.subnet.acl.match.label"
|
|
||||||
@update:value="update"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</InfoBox>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn role-tertiary add"
|
|
||||||
:disabled="isView"
|
|
||||||
@click="add()"
|
|
||||||
>
|
|
||||||
<t k="harvester.subnet.acl.addRule" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.box {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.removeBtn {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -14,8 +14,6 @@ import ArrayList from '@shell/components/form/ArrayList';
|
|||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import ResourceTabs from '@shell/components/form/ResourceTabs/index';
|
import ResourceTabs from '@shell/components/form/ResourceTabs/index';
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import AccessControlList from './AccessControlList';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'EditSubnet',
|
name: 'EditSubnet',
|
||||||
@@ -23,7 +21,6 @@ export default {
|
|||||||
emits: ['update:value'],
|
emits: ['update:value'],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Banner,
|
|
||||||
CruResource,
|
CruResource,
|
||||||
LabeledInput,
|
LabeledInput,
|
||||||
LabeledSelect,
|
LabeledSelect,
|
||||||
@@ -33,7 +30,6 @@ export default {
|
|||||||
ArrayList,
|
ArrayList,
|
||||||
ResourceTabs,
|
ResourceTabs,
|
||||||
Loading,
|
Loading,
|
||||||
AccessControlList
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [CreateEditView],
|
mixins: [CreateEditView],
|
||||||
@@ -42,9 +38,7 @@ export default {
|
|||||||
|
|
||||||
created() {
|
created() {
|
||||||
const vpc = this.$route.query.vpc || '';
|
const vpc = this.$route.query.vpc || '';
|
||||||
const enableDHCP = this.value?.spec?.enableDHCP || false;
|
|
||||||
|
|
||||||
set(this.value.spec, 'enableDHCP', enableDHCP);
|
|
||||||
set(this.value, 'spec', this.value.spec || {
|
set(this.value, 'spec', this.value.spec || {
|
||||||
cidrBlock: '',
|
cidrBlock: '',
|
||||||
protocol: NETWORK_PROTOCOL.IPv4,
|
protocol: NETWORK_PROTOCOL.IPv4,
|
||||||
@@ -52,9 +46,7 @@ export default {
|
|||||||
vpc,
|
vpc,
|
||||||
gatewayIP: '',
|
gatewayIP: '',
|
||||||
excludeIps: [],
|
excludeIps: [],
|
||||||
private: false,
|
private: false
|
||||||
enableDHCP,
|
|
||||||
acls: []
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -89,6 +81,7 @@ export default {
|
|||||||
protocolOptions() {
|
protocolOptions() {
|
||||||
return Object.values(NETWORK_PROTOCOL);
|
return Object.values(NETWORK_PROTOCOL);
|
||||||
},
|
},
|
||||||
|
|
||||||
provider: {
|
provider: {
|
||||||
get() {
|
get() {
|
||||||
const raw = this.value.spec.provider;
|
const raw = this.value.spec.provider;
|
||||||
@@ -132,21 +125,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
|
||||||
'value.spec.enableDHCP': {
|
|
||||||
handler(newValue) {
|
|
||||||
if (newValue === false) {
|
|
||||||
this.value.spec.dhcpV4Options = '';
|
|
||||||
this.value.spec.dhcpV6Options = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async saveSubnet(buttonCb) {
|
async saveSubnet(buttonCb) {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
const name = this.value?.metadata?.name;
|
const name = this.value?.metadata?.name;
|
||||||
const hasEmptyAcls = this.value?.spec?.acls?.some((acl) => !acl.match || !acl.action || acl.priority === undefined || acl.priority === null);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
@@ -157,8 +139,6 @@ export default {
|
|||||||
errors.push(this.t('validation.required', { key: this.t('harvester.subnet.provider.label') }, true));
|
errors.push(this.t('validation.required', { key: this.t('harvester.subnet.provider.label') }, true));
|
||||||
} else if (this.value.spec.excludeIps.includes('')) {
|
} else if (this.value.spec.excludeIps.includes('')) {
|
||||||
errors.push(this.t('harvester.validation.subnet.excludeIps'));
|
errors.push(this.t('harvester.validation.subnet.excludeIps'));
|
||||||
} else if (hasEmptyAcls) {
|
|
||||||
errors.push(this.t('harvester.validation.subnet.aclEmptyError'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
@@ -265,45 +245,6 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-20">
|
|
||||||
<div class="col span-6">
|
|
||||||
<RadioGroup
|
|
||||||
v-model:value="value.spec.enableDHCP"
|
|
||||||
name="enabled"
|
|
||||||
:options="[true, false]"
|
|
||||||
:label="t('harvester.subnet.dhcp.label')"
|
|
||||||
:labels="[t('generic.enabled'), t('generic.disabled')]"
|
|
||||||
:mode="mode"
|
|
||||||
:tooltip="t('harvester.subnet.dhcp.tooltip')"
|
|
||||||
/>
|
|
||||||
<LabeledInput
|
|
||||||
v-if="value.spec.enableDHCP && value.spec.protocol === 'IPv4'"
|
|
||||||
v-model:value="value.spec.dhcpV4Options"
|
|
||||||
class="mb-20 mt-20"
|
|
||||||
:placeholder="t('harvester.subnet.dhcp.placeholder')"
|
|
||||||
:label="t('harvester.subnet.dhcp.v4Options')"
|
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
<LabeledInput
|
|
||||||
v-if="value.spec.enableDHCP && value.spec.protocol === 'IPv6'"
|
|
||||||
v-model:value="value.spec.dhcpV6Options"
|
|
||||||
:placeholder="t('harvester.subnet.dhcp.placeholder')"
|
|
||||||
class="mb-20 mt-20"
|
|
||||||
:label="t('harvester.subnet.dhcp.v6Options')"
|
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
<Banner
|
|
||||||
v-if="value.spec.enableDHCP"
|
|
||||||
color="info"
|
|
||||||
class="dhcpOption-banner"
|
|
||||||
>
|
|
||||||
<t
|
|
||||||
k="harvester.subnet.dhcp.dhcpOptionBanner"
|
|
||||||
:raw="true"
|
|
||||||
/>
|
|
||||||
</Banner>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mt-20">
|
<div class="row mt-20">
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
@@ -377,23 +318,6 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
</ArrayList>
|
</ArrayList>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
|
||||||
name="ACL"
|
|
||||||
:label="t('harvester.subnet.acl.label')"
|
|
||||||
:weight="-2"
|
|
||||||
class="bordered-table"
|
|
||||||
>
|
|
||||||
<AccessControlList
|
|
||||||
v-model:value="value.spec.acls"
|
|
||||||
:mode="mode"
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
</ResourceTabs>
|
</ResourceTabs>
|
||||||
</CruResource>
|
</CruResource>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.dhcpOption-banner {
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -2,17 +2,12 @@
|
|||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
import { _EDIT, _VIEW } from '@shell/config/query-params';
|
||||||
import { removeAt } from '@shell/utils/array';
|
import { removeAt } from '@shell/utils/array';
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import { DOC } from '../../config/doc-links';
|
|
||||||
import { docLink } from '../../utils/feature-flags';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'StaticRoutes',
|
name: 'StaticRoutes',
|
||||||
|
|
||||||
emits: ['update:value'],
|
emits: ['update:value'],
|
||||||
|
|
||||||
components: { Banner },
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -50,12 +45,6 @@ export default {
|
|||||||
nextHopIPTooltip() {
|
nextHopIPTooltip() {
|
||||||
return this.t('harvester.vpc.staticRoutes.nextHopIP.tooltip');
|
return this.t('harvester.vpc.staticRoutes.nextHopIP.tooltip');
|
||||||
},
|
},
|
||||||
|
|
||||||
vpcPeeringExamplesLink() {
|
|
||||||
const version = this.$store.getters['harvester-common/getServerVersion']();
|
|
||||||
|
|
||||||
return docLink(DOC.VPC_CONFIGURATION_EXAMPLES, version);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
@@ -89,13 +78,6 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Banner color="info">
|
|
||||||
<t
|
|
||||||
k="harvester.vpc.vpcPeerings.infoBanner"
|
|
||||||
:raw="true"
|
|
||||||
:url="vpcPeeringExamplesLink"
|
|
||||||
/>
|
|
||||||
</Banner>
|
|
||||||
<div
|
<div
|
||||||
v-if="rows.length"
|
v-if="rows.length"
|
||||||
class="static-route-row"
|
class="static-route-row"
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="mt-20">
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-model:value="checkedSsh"
|
v-model:value="checkedSsh"
|
||||||
:label="t('harvester.virtualMachine.input.sshKey')"
|
:label="t('harvester.virtualMachine.input.sshKey')"
|
||||||
|
|||||||
@@ -338,15 +338,6 @@ export default {
|
|||||||
saveVM(buttonCb) {
|
saveVM(buttonCb) {
|
||||||
clear(this.errors);
|
clear(this.errors);
|
||||||
|
|
||||||
this.validateCPUMemory();
|
|
||||||
|
|
||||||
// block create VM flow if has validation errors
|
|
||||||
if (this.errors.length) {
|
|
||||||
buttonCb(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isSingle) {
|
if (this.isSingle) {
|
||||||
this.saveSingle(buttonCb);
|
this.saveSingle(buttonCb);
|
||||||
} else {
|
} else {
|
||||||
@@ -354,18 +345,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
validateCPUMemory() {
|
|
||||||
const { cpu, memory } = this;
|
|
||||||
|
|
||||||
if ((!cpu)) {
|
|
||||||
this.errors.push(this.t('validation.required', { key: this.t('harvester.virtualMachine.input.cpu') }, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!memory)) {
|
|
||||||
this.errors.push(this.t('validation.required', { key: this.t('harvester.virtualMachine.input.memory') }, true));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async saveSingle(buttonCb) {
|
async saveSingle(buttonCb) {
|
||||||
this.parseVM();
|
this.parseVM();
|
||||||
this.value.spec.template.spec.hostname = this.hostname ? this.hostname : this.value.metadata.name;
|
this.value.spec.template.spec.hostname = this.hostname ? this.hostname : this.value.metadata.name;
|
||||||
@@ -487,7 +466,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = this.getAccessCredentialsValidation();
|
const cpuMemoryErrors = this.getCPUMemoryValidation();
|
||||||
|
const accessCredentialsErrors = this.getAccessCredentialsValidation();
|
||||||
|
const errors = [...cpuMemoryErrors, ...accessCredentialsErrors];
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Promise.reject(errors);
|
return Promise.reject(errors);
|
||||||
@@ -624,7 +605,6 @@ export default {
|
|||||||
:label="t('harvester.virtualMachine.detail.tabs.basics')"
|
:label="t('harvester.virtualMachine.detail.tabs.basics')"
|
||||||
>
|
>
|
||||||
<CpuMemory
|
<CpuMemory
|
||||||
class="mb-20"
|
|
||||||
:cpu="cpu"
|
:cpu="cpu"
|
||||||
:max-cpu="maxCpu"
|
:max-cpu="maxCpu"
|
||||||
:memory="memory"
|
:memory="memory"
|
||||||
@@ -892,10 +872,7 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="col span-6">
|
||||||
v-if="!value.vmMachineTypeAutoFeatureEnabled"
|
|
||||||
class="col span-6"
|
|
||||||
>
|
|
||||||
<LabeledSelect
|
<LabeledSelect
|
||||||
v-model:value="machineType"
|
v-model:value="machineType"
|
||||||
label-key="harvester.virtualMachine.input.MachineType"
|
label-key="harvester.virtualMachine.input.MachineType"
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
<script>
|
|
||||||
import NetworkPolicy from '@shell/edit/networking.k8s.io.networkpolicy';
|
|
||||||
import { Banner } from '@components/Banner';
|
|
||||||
import { DOC } from '../config/doc-links';
|
|
||||||
import { docLink } from '../utils/feature-flags';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
NetworkPolicy,
|
|
||||||
Banner
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
networkPolicyDocLink() {
|
|
||||||
const version = this.$store.getters['harvester-common/getServerVersion']();
|
|
||||||
|
|
||||||
return docLink(DOC.NETWORK_POLICY, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="network-policy-edit">
|
|
||||||
<Banner color="info">
|
|
||||||
<t
|
|
||||||
k="harvester.networkPolicy.banner"
|
|
||||||
:url="networkPolicyDocLink"
|
|
||||||
:raw="true"
|
|
||||||
/>
|
|
||||||
</Banner>
|
|
||||||
<NetworkPolicy v-bind="$attrs" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.network-policy-edit {
|
|
||||||
flex: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -236,7 +236,6 @@ harvester:
|
|||||||
shutdown: Shut Down
|
shutdown: Shut Down
|
||||||
powerOn: Power On
|
powerOn: Power On
|
||||||
reboot: Reboot
|
reboot: Reboot
|
||||||
reconnect: Reconnect
|
|
||||||
forceStop: Force Stop
|
forceStop: Force Stop
|
||||||
formatters:
|
formatters:
|
||||||
hardwareResourceGauge:
|
hardwareResourceGauge:
|
||||||
@@ -404,7 +403,7 @@ harvester:
|
|||||||
sha512: 'Invalid SHA512 checksum.'
|
sha512: 'Invalid SHA512 checksum.'
|
||||||
subnet:
|
subnet:
|
||||||
excludeIps: 'Exclude IPs cannot be empty. Please remove or fill in the exclude IPs.'
|
excludeIps: 'Exclude IPs cannot be empty. Please remove or fill in the exclude IPs.'
|
||||||
aclEmptyError: The fields in subnet access control list rule can not be empty.
|
|
||||||
dashboard:
|
dashboard:
|
||||||
label: Dashboard
|
label: Dashboard
|
||||||
header: "Harvester Cluster: {cluster}"
|
header: "Harvester Cluster: {cluster}"
|
||||||
@@ -1045,13 +1044,6 @@ harvester:
|
|||||||
gateway:
|
gateway:
|
||||||
label: Gateway IP
|
label: Gateway IP
|
||||||
placeholder: e.g. 172.20.0.1
|
placeholder: e.g. 172.20.0.1
|
||||||
dhcp:
|
|
||||||
label: Dynamic Host Configuration Protocol (DHCP)
|
|
||||||
v4Options: DHCPV4Options
|
|
||||||
v6Options: DHCPV6Options
|
|
||||||
placeholder: key1=value1, key2=value2
|
|
||||||
dhcpOptionBanner: DHCP options is a key/value string concatenate with comma. For more detail, please refer to <a href="https://kubeovn.github.io/docs/v1.13.x/en/kubevirt/dhcp/" target="_blank">KubOVN document</a>
|
|
||||||
tooltip: Enable DHCP server for this subnet. When enabled, VMs can automatically obtain IP addresses from this subnet.
|
|
||||||
private:
|
private:
|
||||||
label: Private Subnet
|
label: Private Subnet
|
||||||
tooltip: Enable network isolation for this Subnet. When enabled, VMs can only communicate within this subnet, even if other subnets exist under the same VPC.
|
tooltip: Enable network isolation for this Subnet. When enabled, VMs can only communicate within this subnet, even if other subnets exist under the same VPC.
|
||||||
@@ -1062,23 +1054,6 @@ harvester:
|
|||||||
placeholder: e.g. 172.16.0.0/16
|
placeholder: e.g. 172.16.0.0/16
|
||||||
excludeIPs:
|
excludeIPs:
|
||||||
tooltip: The IP address list to reserve from automatic assignment. The gateway IP address is always excluded and will be automatically added to the list.
|
tooltip: The IP address list to reserve from automatic assignment. The gateway IP address is always excluded and will be automatically added to the list.
|
||||||
acl:
|
|
||||||
label: Access Control List
|
|
||||||
tooltip: The ACL to apply to this Subnet. Must be one of the ACLs in the same namespace.
|
|
||||||
action:
|
|
||||||
label: Action
|
|
||||||
placeholder: Please select an action
|
|
||||||
direction:
|
|
||||||
label: Direction
|
|
||||||
placeholder: Please select a direction
|
|
||||||
addRule: Add Rule
|
|
||||||
priority:
|
|
||||||
label: Priority
|
|
||||||
placeholder: Please select a priority
|
|
||||||
match:
|
|
||||||
label: Match
|
|
||||||
placeholder: e.g. ip4.dst == 10.10.0.2
|
|
||||||
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:
|
||||||
noAddonEnabled:
|
noAddonEnabled:
|
||||||
@@ -1105,10 +1080,6 @@ harvester:
|
|||||||
placeholder: e.g. 169.254.0.1/16
|
placeholder: e.g. 169.254.0.1/16
|
||||||
remoteVpc:
|
remoteVpc:
|
||||||
label: Remote VPC
|
label: Remote VPC
|
||||||
infoBanner: The static route destination CIDR must cover all subnets CIDR from remote VPC Peer. Read <a href="{url}" target="_blank">VPC Peering Configuration Examples</a> for more information.
|
|
||||||
networkPolicy:
|
|
||||||
label: Network Policies
|
|
||||||
banner: The network policies must be used for VMs attached to overlay networks. Please read the <a href="{url}" target="_blank">harvester document</a> how the network policy works.
|
|
||||||
network:
|
network:
|
||||||
label: Virtual Machine Networks
|
label: Virtual Machine Networks
|
||||||
tabs:
|
tabs:
|
||||||
@@ -1747,8 +1718,9 @@ advancedSettings:
|
|||||||
'harv-server-version': Harvester server version.
|
'harv-server-version': Harvester server version.
|
||||||
'harv-upgrade-checker-enabled': Specifies whether to enable Harvester upgrade check or not. Default is True.
|
'harv-upgrade-checker-enabled': Specifies whether to enable Harvester upgrade check or not. Default is True.
|
||||||
'harv-upgrade-checker-url': Default Harvester upgrade check url. Only used when the <code>upgrade-checker-enabled</code> is equal to True.
|
'harv-upgrade-checker-url': Default Harvester upgrade check url. Only used when the <code>upgrade-checker-enabled</code> is equal to True.
|
||||||
'harv-ui-source': Configure how to load the UI source. The `External` option is used to load the UI from a ui-index URL, while the `Bundled` option loads the UI from the local Harvester server.
|
'harv-ui-source': Configure how to load the UI source.
|
||||||
'harv-ui-index': 'HTML index location for the UI.'
|
'harv-ui-index': 'HTML index location for the UI.'
|
||||||
|
'harv-ui-plugin-index': 'JS index location for the Harvester plugin UI.'
|
||||||
'harv-cluster-registration-url': Registration URL for multi-cluster management.
|
'harv-cluster-registration-url': Registration URL for multi-cluster management.
|
||||||
'harv-http-proxy': 'HTTP proxy for Harvester to access external services.'
|
'harv-http-proxy': 'HTTP proxy for Harvester to access external services.'
|
||||||
'harv-additional-ca': 'Custom CA root certificates for TLS validation.'
|
'harv-additional-ca': 'Custom CA root certificates for TLS validation.'
|
||||||
@@ -1835,11 +1807,6 @@ typeLabel:
|
|||||||
one { Virtual Private Cloud }
|
one { Virtual Private Cloud }
|
||||||
other { Virtual Private Clouds }
|
other { Virtual Private Clouds }
|
||||||
}
|
}
|
||||||
networking.k8s.io.networkpolicy: |-
|
|
||||||
{count, plural,
|
|
||||||
one { Network Policy }
|
|
||||||
other { Network Policies }
|
|
||||||
}
|
|
||||||
harvesterhci.io.cloudtemplate: |-
|
harvesterhci.io.cloudtemplate: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Cloud Configuration Template }
|
one { Cloud Configuration Template }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Loading from '@shell/components/Loading';
|
|||||||
import MessageLink from '@shell/components/MessageLink';
|
import MessageLink from '@shell/components/MessageLink';
|
||||||
import Masthead from '@shell/components/ResourceList/Masthead';
|
import Masthead from '@shell/components/ResourceList/Masthead';
|
||||||
import ResourceTable from '@shell/components/ResourceTable';
|
import ResourceTable from '@shell/components/ResourceTable';
|
||||||
import { isBackupTargetSettingEmpty } from '../utils/setting';
|
|
||||||
import { STATE, AGE, NAME, NAMESPACE } from '@shell/config/table-headers';
|
import { STATE, AGE, NAME, NAMESPACE } from '@shell/config/table-headers';
|
||||||
import FilterVMSchedule from '../components/FilterVMSchedule';
|
import FilterVMSchedule from '../components/FilterVMSchedule';
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
@@ -39,7 +39,7 @@ export default {
|
|||||||
this.settings = hash.settings;
|
this.settings = hash.settings;
|
||||||
if (this.$store.getters[`${ inStore }/schemaFor`](HCI.SETTING)) {
|
if (this.$store.getters[`${ inStore }/schemaFor`](HCI.SETTING)) {
|
||||||
const backupTargetResource = hash.settings.find( (O) => O.id === 'backup-target');
|
const backupTargetResource = hash.settings.find( (O) => O.id === 'backup-target');
|
||||||
const isEmpty = isBackupTargetSettingEmpty(backupTargetResource);
|
const isEmpty = this.getBackupTargetValueIsEmpty(backupTargetResource);
|
||||||
|
|
||||||
if (backupTargetResource && !isEmpty) {
|
if (backupTargetResource && !isEmpty) {
|
||||||
this.testConnect();
|
this.testConnect();
|
||||||
@@ -78,6 +78,20 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getBackupTargetValueIsEmpty(resource) {
|
||||||
|
let out = true;
|
||||||
|
|
||||||
|
if (resource?.value) {
|
||||||
|
try {
|
||||||
|
const valueJson = JSON.parse(resource?.value);
|
||||||
|
|
||||||
|
out = !valueJson.type;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
getRow(row) {
|
getRow(row) {
|
||||||
return row.status && row.status.source;
|
return row.status && row.status.source;
|
||||||
},
|
},
|
||||||
@@ -169,9 +183,11 @@ export default {
|
|||||||
backupTargetResource() {
|
backupTargetResource() {
|
||||||
return this.settings.find((O) => O.id === 'backup-target');
|
return this.settings.find((O) => O.id === 'backup-target');
|
||||||
},
|
},
|
||||||
|
|
||||||
isEmptyValue() {
|
isEmptyValue() {
|
||||||
return isBackupTargetSettingEmpty(this.backupTargetResource);
|
return this.getBackupTargetValueIsEmpty(this.backupTargetResource);
|
||||||
},
|
},
|
||||||
|
|
||||||
canUpdate() {
|
canUpdate() {
|
||||||
return this?.backupTargetResource?.canUpdate;
|
return this?.backupTargetResource?.canUpdate;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default {
|
|||||||
async fetch() {
|
async fetch() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
const _hash = {
|
const _hash = {
|
||||||
vms: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VM, opt: { force: true } }),
|
vms: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VM }),
|
||||||
pod: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
|
pod: this.$store.dispatch(`${ inStore }/findAll`, { type: POD }),
|
||||||
pvcs: this.$store.dispatch(`${ inStore }/findAll`, { type: PVC }),
|
pvcs: this.$store.dispatch(`${ inStore }/findAll`, { type: PVC }),
|
||||||
pvs: this.$store.dispatch(`${ inStore }/findAll`, { type: PV }),
|
pvs: this.$store.dispatch(`${ inStore }/findAll`, { type: PV }),
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ export default {
|
|||||||
|
|
||||||
needNewSecret() {
|
needNewSecret() {
|
||||||
// When creating a template it is always necessary to create a new secret.
|
// When creating a template it is always necessary to create a new secret.
|
||||||
return this.isCreate ? true : this.showYaml ? false : this.resourceType === HCI.VM_VERSION;
|
return this.showYaml ? false : this.resourceType === HCI.VM_VERSION || this.isCreate;
|
||||||
},
|
},
|
||||||
|
|
||||||
defaultTerminationSetting() {
|
defaultTerminationSetting() {
|
||||||
@@ -299,11 +299,9 @@ export default {
|
|||||||
async created() {
|
async created() {
|
||||||
await this.$store.dispatch(`${ this.inStore }/findAll`, { type: SECRET });
|
await this.$store.dispatch(`${ this.inStore }/findAll`, { type: SECRET });
|
||||||
|
|
||||||
if (!this.value.vmMachineTypeAutoFeatureEnabled) {
|
if (this.value.vmMachineTypesFeatureEnabled) {
|
||||||
try {
|
try {
|
||||||
const url = this.$store.getters['harvester-common/getHarvesterClusterUrl'](
|
const url = this.$store.getters['harvester-common/getHarvesterClusterUrl']('v1/harvester/clusters/local?link=machineTypes');
|
||||||
'v1/harvester/clusters/local?link=machineTypes'
|
|
||||||
);
|
|
||||||
const machineTypes = await this.$store.dispatch('harvester/request', { url });
|
const machineTypes = await this.$store.dispatch('harvester/request', { url });
|
||||||
|
|
||||||
this.machineTypes = machineTypes;
|
this.machineTypes = machineTypes;
|
||||||
@@ -689,22 +687,21 @@ export default {
|
|||||||
set(this.spec.template.spec, 'domain.memory.maxGuest', this.maxMemory);
|
set(this.spec.template.spec, 'domain.memory.maxGuest', this.maxMemory);
|
||||||
set(this.spec.template.spec, 'domain.resources.limits.memory', this.maxMemory);
|
set(this.spec.template.spec, 'domain.resources.limits.memory', this.maxMemory);
|
||||||
} else {
|
} else {
|
||||||
this.spec.template.spec.domain.cpu.maxSockets = 1;
|
|
||||||
this.spec.template.spec.domain.cpu.sockets = 1;
|
this.spec.template.spec.domain.cpu.sockets = 1;
|
||||||
this.spec.template.spec.domain.cpu.cores = this.cpu;
|
this.spec.template.spec.domain.cpu.cores = this.cpu;
|
||||||
this.spec.template.spec.domain.resources.limits.cpu = this.cpu?.toString();
|
this.spec.template.spec.domain.resources.limits.cpu = this.cpu?.toString();
|
||||||
this.spec.template.spec.domain.resources.limits.memory = this.memory;
|
this.spec.template.spec.domain.resources.limits.memory = this.memory;
|
||||||
// clean below fields as we don't need them if not enable CPU and memory hotplug
|
// clean
|
||||||
if (this.spec?.template?.spec?.domain?.memory?.maxGuest) {
|
delete this.spec.template.spec.resources;
|
||||||
delete this.spec.template.spec.domain.memory.maxGuest;
|
delete this.spec.template.spec.domain.memory;
|
||||||
}
|
delete this.spec.template.spec.domain.cpu.maxSockets;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
parseDiskRows(disk) {
|
parseDiskRows(disk) {
|
||||||
const disks = [];
|
const disks = [];
|
||||||
const volumes = [];
|
const volumes = [];
|
||||||
const diskNameLabels = [];
|
const diskNameLables = [];
|
||||||
const volumeClaimTemplates = [];
|
const volumeClaimTemplates = [];
|
||||||
|
|
||||||
disk.forEach( (R, index) => {
|
disk.forEach( (R, index) => {
|
||||||
@@ -726,7 +723,7 @@ export default {
|
|||||||
|
|
||||||
disks.push(_disk);
|
disks.push(_disk);
|
||||||
volumes.push(_volume);
|
volumes.push(_volume);
|
||||||
diskNameLabels.push(dataVolumeName);
|
diskNameLables.push(dataVolumeName);
|
||||||
|
|
||||||
if (R.source !== SOURCE_TYPE.CONTAINER) {
|
if (R.source !== SOURCE_TYPE.CONTAINER) {
|
||||||
volumeClaimTemplates.push(_dataVolumeTemplate);
|
volumeClaimTemplates.push(_dataVolumeTemplate);
|
||||||
@@ -767,9 +764,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const specDisks = this.spec?.template?.spec?.domain?.devices?.disks;
|
|
||||||
const mergedDisks = this.mergeDeviceList(specDisks, disks);
|
|
||||||
|
|
||||||
let spec = {
|
let spec = {
|
||||||
...this.spec,
|
...this.spec,
|
||||||
runStrategy: this.runStrategy,
|
runStrategy: this.runStrategy,
|
||||||
@@ -792,7 +786,7 @@ export default {
|
|||||||
...this.spec.template?.spec?.domain,
|
...this.spec.template?.spec?.domain,
|
||||||
devices: {
|
devices: {
|
||||||
...this.spec.template?.spec?.domain?.devices,
|
...this.spec.template?.spec?.domain?.devices,
|
||||||
disks: mergedDisks,
|
disks,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
volumes,
|
volumes,
|
||||||
@@ -891,16 +885,13 @@ export default {
|
|||||||
interfaces.push(_interface);
|
interfaces.push(_interface);
|
||||||
});
|
});
|
||||||
|
|
||||||
const specInterfaces = this.spec?.template?.spec?.domain?.devices?.interfaces;
|
|
||||||
const mergedInterfaces = this.mergeInterfaceList(specInterfaces, interfaces);
|
|
||||||
|
|
||||||
const spec = {
|
const spec = {
|
||||||
...this.spec.template.spec,
|
...this.spec.template.spec,
|
||||||
domain: {
|
domain: {
|
||||||
...this.spec.template.spec.domain,
|
...this.spec.template.spec.domain,
|
||||||
devices: {
|
devices: {
|
||||||
...this.spec.template.spec.domain.devices,
|
...this.spec.template.spec.domain.devices,
|
||||||
interfaces: mergedInterfaces,
|
interfaces,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
networks
|
networks
|
||||||
@@ -1043,9 +1034,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
parseVolumeClaimTemplate(R, dataVolumeName) {
|
parseVolumeClaimTemplate(R, dataVolumeName) {
|
||||||
const sizeString = String(R.size);
|
if (!String(R.size).includes('Gi') && R.size) {
|
||||||
|
|
||||||
if (!(sizeString.includes('Gi') || sizeString.includes('Ti')) && R.size) {
|
|
||||||
R.size = `${ R.size }${ GIBIBYTE }`;
|
R.size = `${ R.size }${ GIBIBYTE }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1369,6 +1358,21 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCPUMemoryValidation() {
|
||||||
|
const errors = [];
|
||||||
|
const { cpu, memory } = this;
|
||||||
|
|
||||||
|
if ((!cpu)) {
|
||||||
|
errors.push(this.t('validation.required', { key: this.t('harvester.virtualMachine.input.cpu') }, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!memory)) {
|
||||||
|
errors.push(this.t('validation.required', { key: this.t('harvester.virtualMachine.input.memory') }, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
},
|
||||||
|
|
||||||
getAccessCredentialsValidation() {
|
getAccessCredentialsValidation() {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
@@ -1536,49 +1540,6 @@ export default {
|
|||||||
this.refreshYamlEditor();
|
this.refreshYamlEditor();
|
||||||
},
|
},
|
||||||
|
|
||||||
mergeInterfaceList(specInterfaceList, interfaceList) {
|
|
||||||
if (!specInterfaceList || specInterfaceList.length === 0) {
|
|
||||||
return interfaceList;
|
|
||||||
}
|
|
||||||
const specInterfaceMap = new Map(specInterfaceList.map((iface) => [iface.name, iface]));
|
|
||||||
|
|
||||||
return interfaceList.map((iface) => {
|
|
||||||
const specInterface = specInterfaceMap.get(iface.name);
|
|
||||||
|
|
||||||
if (specInterface) {
|
|
||||||
const merged = { ...specInterface, ...iface };
|
|
||||||
|
|
||||||
// currently we only have bridge and masquerade network type, they are mutually exclusive
|
|
||||||
if (iface['bridge']) {
|
|
||||||
delete merged['masquerade'];
|
|
||||||
} else {
|
|
||||||
delete merged['bridge'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iface;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
mergeDeviceList(specDeviceList, deviceList) {
|
|
||||||
if (!specDeviceList || specDeviceList.length === 0) {
|
|
||||||
return deviceList;
|
|
||||||
}
|
|
||||||
const specDeviceMap = new Map(specDeviceList.map((device) => [device.name, device]));
|
|
||||||
|
|
||||||
return deviceList.map((device) => {
|
|
||||||
const specDevice = specDeviceMap.get(device.name);
|
|
||||||
|
|
||||||
if (specDevice) {
|
|
||||||
return { ...specDevice, ...device };
|
|
||||||
}
|
|
||||||
|
|
||||||
return device;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
refreshYamlEditor() {
|
refreshYamlEditor() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.yamlEditor?.updateValue();
|
this.$refs.yamlEditor?.updateValue();
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import SteveModel from '@shell/plugins/steve/steve-class';
|
import SteveModel from '@shell/plugins/steve/steve-class';
|
||||||
import { VIEW_IN_API, DEV } from '@shell/store/prefs';
|
import { VIEW_IN_API, DEV } from '@shell/store/prefs';
|
||||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../config/harvester';
|
||||||
import { HARVESTER_CRD_MAP } from '../config/harvester-map';
|
|
||||||
|
|
||||||
export default class HarvesterResource extends SteveModel {
|
export default class HarvesterResource extends SteveModel {
|
||||||
get listLocation() {
|
get listLocation() {
|
||||||
const name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
const name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
||||||
const resource = HARVESTER_CRD_MAP[this.type] || this.type;
|
|
||||||
|
|
||||||
return this.$rootGetters['type-map/optionsFor'](this.type).customRoute || {
|
return this.$rootGetters['type-map/optionsFor'](this.type).customRoute || {
|
||||||
name,
|
name,
|
||||||
params: {
|
params: {
|
||||||
product: this.$rootGetters['productId'],
|
product: this.$rootGetters['productId'],
|
||||||
cluster: this.$rootGetters['clusterId'],
|
cluster: this.$rootGetters['clusterId'],
|
||||||
resource
|
resource: this.type,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// This file is Cloud Configuration Templates page model
|
|
||||||
import { clone } from '@shell/utils/object';
|
import { clone } from '@shell/utils/object';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
import HarvesterResource from '../harvester';
|
import HarvesterResource from '../harvester';
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default class HarvesterNetworkAttachmentDef extends NetworkAttachmentDef
|
|||||||
params: {
|
params: {
|
||||||
product: HARVESTER_PRODUCT,
|
product: HARVESTER_PRODUCT,
|
||||||
cluster: this.$rootGetters['clusterId'],
|
cluster: this.$rootGetters['clusterId'],
|
||||||
resource: HCI.NETWORK_ATTACHMENT,
|
resource: this.type,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,19 +9,6 @@ export default class HciClusterflow extends HarvesterFlow {
|
|||||||
return this.$rootGetters['harvester/all'](LOGGING.CLUSTER_OUTPUT) || [];
|
return this.$rootGetters['harvester/all'](LOGGING.CLUSTER_OUTPUT) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.CLUSTER_FLOW;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _detailLocation() {
|
get _detailLocation() {
|
||||||
const schema = this.$getters['schemaFor'](this.type);
|
const schema = this.$getters['schemaFor'](this.type);
|
||||||
|
|
||||||
|
|||||||
@@ -21,19 +21,6 @@ export default class HciClusteroutput extends LogOutput {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.CLUSTER_OUTPUT;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get detailLocation() {
|
get detailLocation() {
|
||||||
const detailLocation = clone(this._detailLocation);
|
const detailLocation = clone(this._detailLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -14,19 +14,6 @@ export default class HarvesterLogFlow extends Flow {
|
|||||||
return this.$rootGetters['harvester/all'](LOGGING.CLUSTER_OUTPUT) || [];
|
return this.$rootGetters['harvester/all'](LOGGING.CLUSTER_OUTPUT) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.FLOW;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _detailLocation() {
|
get _detailLocation() {
|
||||||
const schema = this.$getters['schemaFor'](this.type);
|
const schema = this.$getters['schemaFor'](this.type);
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,6 @@ export default class HarvesterLogOutput extends LogOutput {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.OUTPUT;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get detailLocation() {
|
get detailLocation() {
|
||||||
const detailLocation = clone(this._detailLocation);
|
const detailLocation = clone(this._detailLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -19,19 +19,6 @@ export default class HciAlertmanagerConfig extends SteveModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.ALERTMANAGERCONFIG;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get doneOverride() {
|
get doneOverride() {
|
||||||
const detailLocation = clone(this._detailLocation);
|
const detailLocation = clone(this._detailLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -5,25 +5,6 @@ import Secret from '@shell/models/secret';
|
|||||||
import { NAMESPACE } from '@shell/config/types';
|
import { NAMESPACE } from '@shell/config/types';
|
||||||
|
|
||||||
export default class HciSecret extends Secret {
|
export default class HciSecret extends Secret {
|
||||||
// prevent harvester secret detail page be overridden.
|
|
||||||
// See isFullPageOverride in https://github.com/rancher/dashboard/blob/master/shell/components/ResourceDetail/index.vue
|
|
||||||
get fullDetailPageOverride() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.SECRET;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get _detailLocation() {
|
get _detailLocation() {
|
||||||
const schema = this.$getters['schemaFor'](this.type);
|
const schema = this.$getters['schemaFor'](this.type);
|
||||||
|
|
||||||
|
|||||||
@@ -9,19 +9,6 @@ import { isInternalStorageClass } from '../../utils/storage-class';
|
|||||||
export const LVM_DRIVER = 'lvm.driver.harvesterhci.io';
|
export const LVM_DRIVER = 'lvm.driver.harvesterhci.io';
|
||||||
|
|
||||||
export default class HciStorageClass extends StorageClass {
|
export default class HciStorageClass extends StorageClass {
|
||||||
get listLocation() {
|
|
||||||
const listLocation = clone(super.listLocation);
|
|
||||||
|
|
||||||
listLocation.name = this.harvesterResourcesInExplorer ? 'c-cluster-product-resource' : `${ HARVESTER_PRODUCT }-c-cluster-resource`;
|
|
||||||
listLocation.params.resource = HCI.STORAGE;
|
|
||||||
|
|
||||||
return listLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
get harvesterResourcesInExplorer() {
|
|
||||||
return this.$rootGetters['productId'] !== HARVESTER_PRODUCT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get detailLocation() {
|
get detailLocation() {
|
||||||
const detailLocation = clone(this._detailLocation);
|
const detailLocation = clone(this._detailLocation);
|
||||||
|
|
||||||
@@ -107,14 +94,13 @@ export default class HciStorageClass extends StorageClass {
|
|||||||
get availableActions() {
|
get availableActions() {
|
||||||
let out = super.availableActions || [];
|
let out = super.availableActions || [];
|
||||||
|
|
||||||
if (this.isInternalStorageClass()) {
|
out = out.map((action) => {
|
||||||
out = out.filter((action) => {
|
if ((action.action === 'setDefault' || action.action === 'setAsDefault' || action.action === 'promptRemove') && this.isInternalStorageClass()) {
|
||||||
return !['setDefault', 'setAsDefault', 'promptRemove'].includes(action.action);
|
return { ...action, enabled: false };
|
||||||
});
|
}
|
||||||
}
|
|
||||||
if (out[0] && out[0].divider === true) {
|
return action;
|
||||||
out = out.slice(1);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,8 +156,4 @@ export default class HciSetting extends HarvesterResource {
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
get disableResourceDetailDrawer() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import SteveModel from '@shell/plugins/steve/steve-class';
|
|||||||
import { HCI } from '@shell/config/labels-annotations';
|
import { HCI } from '@shell/config/labels-annotations';
|
||||||
import { NETWORK_TYPE } from '../config/types';
|
import { NETWORK_TYPE } from '../config/types';
|
||||||
|
|
||||||
const { UNTAGGED, OVERLAY } = NETWORK_TYPE;
|
const { UNTAGGED } = NETWORK_TYPE;
|
||||||
|
|
||||||
export default class NetworkAttachmentDef extends SteveModel {
|
export default class NetworkAttachmentDef extends SteveModel {
|
||||||
applyDefaults() {
|
applyDefaults() {
|
||||||
@@ -45,7 +45,7 @@ export default class NetworkAttachmentDef extends SteveModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get vlanId() {
|
get vlanId() {
|
||||||
return this.vlanType === UNTAGGED || this.vlanType === OVERLAY ? 'N/A' : this.parseConfig.vlan;
|
return this.vlanType === UNTAGGED ? 'N/A' : this.parseConfig.vlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
get customValidationRules() {
|
get customValidationRules() {
|
||||||
@@ -68,7 +68,7 @@ export default class NetworkAttachmentDef extends SteveModel {
|
|||||||
const route = annotations[HCI.NETWORK_ROUTE];
|
const route = annotations[HCI.NETWORK_ROUTE];
|
||||||
let config = {};
|
let config = {};
|
||||||
|
|
||||||
if (this.vlanType === UNTAGGED || this.vlanType === OVERLAY) {
|
if (this.vlanType === UNTAGGED) {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { BACKUP_TYPE } from '../config/types';
|
|||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import HarvesterResource from './harvester';
|
import HarvesterResource from './harvester';
|
||||||
import { getVmCPUMemoryValues } from '../utils/cpuMemory';
|
import { getVmCPUMemoryValues } from '../utils/cpuMemory';
|
||||||
import { isBackupTargetSettingUnavailable } from '../utils/setting';
|
|
||||||
|
|
||||||
export const OFF = 'Off';
|
export const OFF = 'Off';
|
||||||
|
|
||||||
@@ -153,7 +152,7 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
action: 'backupVM',
|
action: 'backupVM',
|
||||||
enabled: !!this.actions?.backup && !this.isBackupTargetUnavailable,
|
enabled: !!this.actions?.backup,
|
||||||
icon: 'icon icon-backup',
|
icon: 'icon icon-backup',
|
||||||
label: this.t('harvester.action.backup')
|
label: this.t('harvester.action.backup')
|
||||||
},
|
},
|
||||||
@@ -328,10 +327,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
doSoftReboot() {
|
|
||||||
this.doActionGrowl('softreboot', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
softrebootVM(resources = this) {
|
softrebootVM(resources = this) {
|
||||||
this.$dispatch('promptModal', {
|
this.$dispatch('promptModal', {
|
||||||
resources,
|
resources,
|
||||||
@@ -1169,13 +1164,6 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return this.t('harvester.virtualMachine.hotplug.restartVMMessage');
|
return this.t('harvester.virtualMachine.hotplug.restartVMMessage');
|
||||||
}
|
}
|
||||||
|
|
||||||
const restoreConditions = this.restoreResource?.status?.conditions || [];
|
|
||||||
const restoreError = restoreConditions.find((s) => s.error === true && s.reason === 'Error' && s.type === 'Ready');
|
|
||||||
|
|
||||||
if (restoreError?.message) {
|
|
||||||
return restoreError.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ingoreVMMessage ? '' : super.stateDescription;
|
return this.ingoreVMMessage ? '' : super.stateDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1236,15 +1224,8 @@ export default class VirtVm extends HarvesterResource {
|
|||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
||||||
}
|
}
|
||||||
|
|
||||||
get vmMachineTypeAutoFeatureEnabled() {
|
get vmMachineTypesFeatureEnabled() {
|
||||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('vmMachineTypeAuto');
|
return this.$rootGetters['harvester-common/getFeatureEnabled']('vmMachineTypes');
|
||||||
}
|
|
||||||
|
|
||||||
get isBackupTargetUnavailable() {
|
|
||||||
const allSettings = this.$rootGetters['harvester/all'](HCI.SETTING) || [];
|
|
||||||
const backupTargetSetting = allSettings.find( (O) => O.id === 'backup-target');
|
|
||||||
|
|
||||||
return isBackupTargetSettingUnavailable(backupTargetSetting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setInstanceLabels(val) {
|
setInstanceLabels(val) {
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ export default class HciVlanConfig extends HarvesterResource {
|
|||||||
uplink: {
|
uplink: {
|
||||||
nics: [],
|
nics: [],
|
||||||
linkAttributes: {},
|
linkAttributes: {},
|
||||||
bondOptions: {
|
bondOptions: { mode: 'active-backup' },
|
||||||
mode: 'active-backup',
|
|
||||||
miimon: -1
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "harvester",
|
"name": "harvester",
|
||||||
"description": "Rancher UI Extension for Harvester",
|
"description": "Rancher UI Extension for Harvester",
|
||||||
"version": "1.7.0-dev",
|
"version": "1.6.0-rc4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"rancher": {
|
"rancher": {
|
||||||
"annotations": {
|
"annotations": {
|
||||||
|
|||||||
@@ -3,19 +3,17 @@ import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations
|
|||||||
export function getVmCPUMemoryValues(vm) {
|
export function getVmCPUMemoryValues(vm) {
|
||||||
if (!vm) {
|
if (!vm) {
|
||||||
return {
|
return {
|
||||||
cpu: null,
|
cpu: 0,
|
||||||
memory: null,
|
memory: null,
|
||||||
isHotplugEnabled: false
|
isHotplugEnabled: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const isHotplugEnabled = isCPUMemoryHotPlugEnabled(vm);
|
const isHotplugEnabled = isCPUMemoryHotPlugEnabled(vm);
|
||||||
const { sockets = 1, threads = 1, cores = null } = vm.spec.template.spec.domain.cpu || {};
|
|
||||||
const cpu = cores === null ? null : sockets * threads * cores;
|
|
||||||
|
|
||||||
if (isHotplugEnabled) {
|
if (isHotplugEnabled) {
|
||||||
return {
|
return {
|
||||||
cpu,
|
cpu: vm.spec.template.spec.domain.cpu.sockets,
|
||||||
memory: vm.spec.template.spec.domain?.memory?.guest || null,
|
memory: vm.spec.template.spec.domain?.memory?.guest || null,
|
||||||
maxCpu: vm.spec.template.spec.domain.cpu?.maxSockets || 0,
|
maxCpu: vm.spec.template.spec.domain.cpu?.maxSockets || 0,
|
||||||
maxMemory: vm.spec.template.spec.domain?.memory?.maxGuest || null,
|
maxMemory: vm.spec.template.spec.domain?.memory?.maxGuest || null,
|
||||||
@@ -23,7 +21,7 @@ export function getVmCPUMemoryValues(vm) {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
cpu,
|
cpu: vm.spec.template.spec.domain.cpu.cores,
|
||||||
memory: vm.spec.template.spec.domain.resources?.limits?.memory || null,
|
memory: vm.spec.template.spec.domain.resources?.limits?.memory || null,
|
||||||
isHotplugEnabled
|
isHotplugEnabled
|
||||||
};
|
};
|
||||||
@@ -31,5 +29,8 @@ export function getVmCPUMemoryValues(vm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isCPUMemoryHotPlugEnabled(vm) {
|
export function isCPUMemoryHotPlugEnabled(vm) {
|
||||||
return vm?.metadata?.annotations[HCI_ANNOTATIONS.VM_CPU_MEMORY_HOTPLUG] === 'true' || !!vm?.spec?.template?.spec?.domain?.memory?.maxGuest || false;
|
return vm?.metadata?.annotations[HCI_ANNOTATIONS.VM_CPU_MEMORY_HOTPLUG] === 'true' ||
|
||||||
|
!!vm?.spec?.template?.spec?.domain.cpu?.maxSockets ||
|
||||||
|
!!vm?.spec?.template?.spec?.domain?.memory?.maxGuest ||
|
||||||
|
false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const docLink = (suffix, version) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getVersion(v) {
|
export function getVersion(v) {
|
||||||
|
// e.g v1.4.0
|
||||||
if (process.env.VUE_APP_SERVER_VERSION) {
|
if (process.env.VUE_APP_SERVER_VERSION) {
|
||||||
return process.env.VUE_APP_SERVER_VERSION;
|
return process.env.VUE_APP_SERVER_VERSION;
|
||||||
}
|
}
|
||||||
@@ -17,21 +18,20 @@ export function getVersion(v) {
|
|||||||
return `v${ semver.major(v) }.${ semver.minor(v) }.${ semver.patch(v) }`;
|
return `v${ semver.major(v) }.${ semver.minor(v) }.${ semver.patch(v) }`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// fallback to the latest version
|
// fallback to the latest version
|
||||||
return getLatestVersion();
|
return latestVersion(Object.keys(RELEASE_FEATURES));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLatestVersion() {
|
function latestVersion(versions) {
|
||||||
const allVersions = Object.keys(RELEASE_FEATURES).filter(semver.valid).sort(semver.rcompare);
|
return versions.sort((a, b) => semver.compare(a, b)).pop();
|
||||||
|
|
||||||
return allVersions[0] || '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLatestCompatibleVersion(version) {
|
// v1.3.3 => latest v1.3.x, v1.4.2 => latest v1.4.x
|
||||||
const allVersions = Object.keys(RELEASE_FEATURES).filter(semver.valid);
|
function latestMinorVersion(v) {
|
||||||
const compatible = allVersions.filter((v) => semver.lte(v, version)).sort(semver.rcompare);
|
const minor = `v${ semver.major(v) }.${ semver.minor(v) }`;
|
||||||
|
const minorVersions = Object.keys(RELEASE_FEATURES).filter((version) => version.startsWith(minor));
|
||||||
|
|
||||||
return compatible[0] || getLatestVersion();
|
return latestVersion(minorVersions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const featureEnabled = (featureKey, serverVersion) => {
|
export const featureEnabled = (featureKey, serverVersion) => {
|
||||||
@@ -46,16 +46,12 @@ export const featureEnabled = (featureKey, serverVersion) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const compatibleVersion = getLatestCompatibleVersion(version);
|
let releasedFeatures = RELEASE_FEATURES[version];
|
||||||
const releasedFeatures = RELEASE_FEATURES?.[compatibleVersion];
|
|
||||||
|
|
||||||
if (!Array.isArray(releasedFeatures)) {
|
if (!releasedFeatures) {
|
||||||
// eslint-disable-next-line no-console
|
const fallback = latestMinorVersion(version);
|
||||||
console.error(
|
|
||||||
`Feature flags for version ${ version } are not defined. Please upgrade Harvester UI extension and check the support matrix.`
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
releasedFeatures = RELEASE_FEATURES[fallback];
|
||||||
}
|
}
|
||||||
|
|
||||||
return releasedFeatures.includes(featureKey);
|
return releasedFeatures.includes(featureKey);
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
export function isBackupTargetSettingEmpty(setting = {}) {
|
|
||||||
let isEmpty = true;
|
|
||||||
|
|
||||||
if (setting?.value) {
|
|
||||||
try {
|
|
||||||
const valueJson = JSON.parse(setting?.value);
|
|
||||||
|
|
||||||
isEmpty = !valueJson.type;
|
|
||||||
} catch (e) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error('Failed to parse backup target value:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isBackupTargetSettingUnavailable(setting = {}) {
|
|
||||||
const errorMessage = setting.errorMessage;
|
|
||||||
const isEmptyValue = isBackupTargetSettingEmpty(setting);
|
|
||||||
const canUpdate = setting.canUpdate;
|
|
||||||
|
|
||||||
return (errorMessage || isEmptyValue) && canUpdate;
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,7 @@ if [[ "$VERSION" != "$EXTENSION_VERSION" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${TAG_VERSION}" != "${PKG_VERSION}"* ]]; then
|
if [[ "${TAG_VERSION}" != "$PKG_VERSION" ]]; then
|
||||||
echo "Package version $PKG_VERSION in pkg/harvester/package.json does not match tag version ${TAG_VERSION}"
|
echo "Package version $PKG_VERSION in pkg/harvester/package.json does not match tag version ${TAG_VERSION}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
/* eslint-disable no-console */
|
|
||||||
import load from "@commitlint/load";
|
|
||||||
|
|
||||||
if (process.argv.length < 3) {
|
|
||||||
console.error('No PR title as first argument');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const prTitle = process.argv[2];
|
|
||||||
const prLabel = prTitle.split(':')[0].trim();
|
|
||||||
|
|
||||||
const config = await load({ extends: ["./commitlint.config.js"] });
|
|
||||||
const commitPrefix = config?.rules?.['type-enum']?.[2] || [];
|
|
||||||
const label = commitPrefix.includes(prLabel) ? prLabel : 'other';
|
|
||||||
|
|
||||||
console.log(label);
|
|
||||||
Reference in New Issue
Block a user