mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-07-02 14:52:21 +00:00
Compare commits
12 Commits
v1.9.0-dev
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5090d8ecad | ||
|
|
b9334bafb7 | ||
|
|
9a675da756 | ||
|
|
8c0c36e022 | ||
|
|
140a85f6a8 | ||
|
|
e62ae7e1d8 | ||
|
|
d03cff645b | ||
|
|
34dfe4027e | ||
|
|
135d520b8d | ||
|
|
62a19ee3fb | ||
|
|
64f0f5fb87 | ||
|
|
93a692ff0c |
15
.github/renovate.json
vendored
15
.github/renovate.json
vendored
@ -14,10 +14,17 @@
|
|||||||
"semanticCommits": "enabled",
|
"semanticCommits": "enabled",
|
||||||
"semanticCommitType": "deps",
|
"semanticCommitType": "deps",
|
||||||
"prHourlyLimit": 12,
|
"prHourlyLimit": 12,
|
||||||
|
"digest": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
"timezone": "Asia/Taipei",
|
"timezone": "Asia/Taipei",
|
||||||
"schedule": ["after 10am on sunday"],
|
"schedule": ["after 10am on sunday"],
|
||||||
"postUpdateOptions": ["yarnDedupeFewer"],
|
"postUpdateOptions": ["yarnDedupeFewer"],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"matchUpdateTypes": ["digest"],
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"matchUpdateTypes": ["major"],
|
"matchUpdateTypes": ["major"],
|
||||||
"enabled": false
|
"enabled": false
|
||||||
@ -45,14 +52,6 @@
|
|||||||
"groupName": "patch dependencies",
|
"groupName": "patch dependencies",
|
||||||
"labels": ["patch-update"],
|
"labels": ["patch-update"],
|
||||||
"reviewers": ["a110605", "houhoucoop"]
|
"reviewers": ["a110605", "houhoucoop"]
|
||||||
},
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": ["digest", "pinDigest"],
|
|
||||||
"automerge": false,
|
|
||||||
"groupName": "digest dependencies",
|
|
||||||
"labels": ["digest-update"],
|
|
||||||
"schedule": ["on the first day of the month"],
|
|
||||||
"reviewers": ["a110605", "houhoucoop"]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
20
README.md
20
README.md
@ -5,6 +5,19 @@ The Harvester UI Extension is a Rancher extension that provides the user interfa
|
|||||||
> **Note:**
|
> **Note:**
|
||||||
> This extension is available starting from **Rancher 2.10.0**. Ensure your Rancher version is **2.10.0 or later** to access Harvester integration.
|
> This extension is available starting from **Rancher 2.10.0**. Ensure your Rancher version is **2.10.0 or later** to access Harvester integration.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Development Setup](#development-setup)
|
||||||
|
- [Commit Message Guidelines](#commit-message-guidelines)
|
||||||
|
- [Branch Structure](#branch-structure)
|
||||||
|
- [Testing Guidelines](#testing-guidelines)
|
||||||
|
- [Release](#release)
|
||||||
|
- [Contributing](#contributing)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io).
|
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io).
|
||||||
@ -157,6 +170,13 @@ To test the standalone UI, configure Harvester to load the UI from an external s
|
|||||||
2. Set **ui-source** to `External`
|
2. Set **ui-source** to `External`
|
||||||
3. Set **ui-index** to the desired URL
|
3. Set **ui-index** to the desired URL
|
||||||
|
|
||||||
|
|
||||||
|
## Release
|
||||||
|
|
||||||
|
The Harvester UI Extension follows the [Harvester](https://github.com/harvester/harvester) release cycle. After RC1 is cut for a new Harvester version, we usually create and work from the corresponding release branch (for example, `release-harvester-v1.8`). The remaining RC builds and the final official release are published from that branch.
|
||||||
|
|
||||||
|
After Harvester releases a new version, update the Harvester entry in rancher/ui-plugin-charts [manifest.json](https://github.com/rancher/ui-plugin-charts/blob/aafd215debbc6cb3100e7ba4b0a542c932397acd/manifest.json#L133-L151). This ensures air-gapped users can pull the new Harvester UI Extension image.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you want to contribute, start by reading this document, then visit our [Getting Started guide](https://extensions.rancher.io/extensions/next/extensions-getting-started) to learn how to develop and submit changes.
|
If you want to contribute, start by reading this document, then visit our [Getting Started guide](https://extensions.rancher.io/extensions/next/extensions-getting-started) to learn how to develop and submit changes.
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
"yaml": "^2.5.1"
|
"yaml": "^2.5.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/node": "25.9.2",
|
"@types/node": "25.9.4",
|
||||||
"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",
|
||||||
@ -34,8 +34,8 @@
|
|||||||
"node-forge": "1.4.0",
|
"node-forge": "1.4.0",
|
||||||
"nth-check": "2.1.1",
|
"nth-check": "2.1.1",
|
||||||
"qs": "6.15.2",
|
"qs": "6.15.2",
|
||||||
"roarr": "7.21.5",
|
"roarr": "7.21.6",
|
||||||
"semver": "7.8.2",
|
"semver": "7.8.5",
|
||||||
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -72,7 +72,8 @@ const FEATURE_FLAGS = {
|
|||||||
'v1.9.0': [
|
'v1.9.0': [
|
||||||
'supportFilesystem',
|
'supportFilesystem',
|
||||||
'disableResourcePooling',
|
'disableResourcePooling',
|
||||||
'expandOnlineEncryptedVolume'
|
'expandOnlineEncryptedVolume',
|
||||||
|
'longhornV2HugepageSettings'
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,14 @@ import { registerAddonSideNav } from '../utils/dynamic-nav';
|
|||||||
const TEMPLATE = HCI.VM_VERSION;
|
const TEMPLATE = HCI.VM_VERSION;
|
||||||
const MONITORING_GROUP = 'Monitoring & Logging::Monitoring';
|
const MONITORING_GROUP = 'Monitoring & Logging::Monitoring';
|
||||||
const LOGGING_GROUP = 'Monitoring & Logging::Logging';
|
const LOGGING_GROUP = 'Monitoring & Logging::Logging';
|
||||||
|
const OVERLAY_NETWORKS_GROUP = 'Overlay Networks';
|
||||||
|
const UNDERLAY_NETWORKS_GROUP = 'Underlay Networks';
|
||||||
|
const NAT_INTERNET_GROUP = `${ OVERLAY_NETWORKS_GROUP }::NAT & Internet`;
|
||||||
|
const GATEWAYS_GROUP = `${ NAT_INTERNET_GROUP }::Gateways`;
|
||||||
|
const EXTERNAL_IPS_GROUP = `${ NAT_INTERNET_GROUP }::External IPs`;
|
||||||
|
const RULES_GROUP = `${ NAT_INTERNET_GROUP }::Rules`;
|
||||||
|
const SOURCE_RULES_GROUP = `${ RULES_GROUP }::Source Rules`;
|
||||||
|
const DESTINATION_RULES_GROUP = `${ RULES_GROUP }::Destination Rules`;
|
||||||
|
|
||||||
export const PRODUCT_NAME = 'harvester';
|
export const PRODUCT_NAME = 'harvester';
|
||||||
|
|
||||||
@ -582,14 +590,52 @@ export function init($plugin, store) {
|
|||||||
HCI.CLUSTER_NETWORK,
|
HCI.CLUSTER_NETWORK,
|
||||||
HCI.NETWORK_ATTACHMENT,
|
HCI.NETWORK_ATTACHMENT,
|
||||||
HCI.HOST_NETWORK_CONFIG,
|
HCI.HOST_NETWORK_CONFIG,
|
||||||
HCI.VPC,
|
|
||||||
NETWORK_POLICY,
|
|
||||||
HCI.LB,
|
HCI.LB,
|
||||||
HCI.IP_POOL,
|
HCI.IP_POOL,
|
||||||
],
|
],
|
||||||
'networks'
|
'networks'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.VPC],
|
||||||
|
OVERLAY_NETWORKS_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[NETWORK_POLICY],
|
||||||
|
OVERLAY_NETWORKS_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.VPC_NAT_GATEWAY],
|
||||||
|
GATEWAYS_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.IPTABLES_EIP],
|
||||||
|
EXTERNAL_IPS_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.IPTABLES_SNAT_RULE],
|
||||||
|
SOURCE_RULES_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.IPTABLES_DNAT_RULE],
|
||||||
|
DESTINATION_RULES_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.PROVIDER_NETWORK],
|
||||||
|
UNDERLAY_NETWORKS_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
|
basicType(
|
||||||
|
[HCI.VLAN],
|
||||||
|
UNDERLAY_NETWORKS_GROUP
|
||||||
|
);
|
||||||
|
|
||||||
basicType(
|
basicType(
|
||||||
[
|
[
|
||||||
HCI.SCHEDULE_VM_BACKUP,
|
HCI.SCHEDULE_VM_BACKUP,
|
||||||
@ -601,7 +647,11 @@ export function init($plugin, store) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
weightGroup('networks', 494, true);
|
weightGroup('networks', 494, true);
|
||||||
weightGroup('backupAndSnapshot', 493, true);
|
weightGroup('Overlay Networks', 493, true);
|
||||||
|
weightGroup('NAT & Internet', 492, true);
|
||||||
|
weightGroup('Rules', 491, true);
|
||||||
|
weightGroup('Underlay Networks', 490, true);
|
||||||
|
weightGroup('backupAndSnapshot', 489, true);
|
||||||
|
|
||||||
basicType(
|
basicType(
|
||||||
[
|
[
|
||||||
@ -680,7 +730,7 @@ export function init($plugin, store) {
|
|||||||
name: HCI.CLUSTER_NETWORK,
|
name: HCI.CLUSTER_NETWORK,
|
||||||
ifHaveType: HCI.CLUSTER_NETWORK,
|
ifHaveType: HCI.CLUSTER_NETWORK,
|
||||||
namespaced: false,
|
namespaced: false,
|
||||||
weight: 189,
|
weight: 484,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: HCI.CLUSTER_NETWORK }
|
params: { resource: HCI.CLUSTER_NETWORK }
|
||||||
@ -702,7 +752,7 @@ export function init($plugin, store) {
|
|||||||
labelKey: 'harvester.network.label',
|
labelKey: 'harvester.network.label',
|
||||||
name: HCI.NETWORK_ATTACHMENT,
|
name: HCI.NETWORK_ATTACHMENT,
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
weight: 188,
|
weight: 485,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: HCI.NETWORK_ATTACHMENT }
|
params: { resource: HCI.NETWORK_ATTACHMENT }
|
||||||
@ -716,7 +766,7 @@ export function init($plugin, store) {
|
|||||||
labelKey: 'harvester.vpc.label',
|
labelKey: 'harvester.vpc.label',
|
||||||
name: HCI.VPC,
|
name: HCI.VPC,
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
weight: 187,
|
weight: 195,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: HCI.VPC }
|
params: { resource: HCI.VPC }
|
||||||
@ -725,13 +775,73 @@ export function init($plugin, store) {
|
|||||||
ifHaveType: HCI.VPC,
|
ifHaveType: HCI.VPC,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
configureType(HCI.VPC_NAT_GATEWAY, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
labelKey: 'harvester.natGateway.label',
|
||||||
|
name: HCI.VPC_NAT_GATEWAY,
|
||||||
|
namespaced: false,
|
||||||
|
weight: 193,
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: { resource: HCI.VPC_NAT_GATEWAY }
|
||||||
|
},
|
||||||
|
exact: false,
|
||||||
|
ifHaveType: HCI.VPC_NAT_GATEWAY,
|
||||||
|
});
|
||||||
|
|
||||||
|
configureType(HCI.IPTABLES_EIP, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
labelKey: 'harvester.externalIP.label',
|
||||||
|
name: HCI.IPTABLES_EIP,
|
||||||
|
namespaced: false,
|
||||||
|
weight: 192,
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: { resource: HCI.IPTABLES_EIP }
|
||||||
|
},
|
||||||
|
exact: false,
|
||||||
|
ifHaveType: HCI.IPTABLES_EIP,
|
||||||
|
});
|
||||||
|
|
||||||
|
configureType(HCI.IPTABLES_SNAT_RULE, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
labelKey: 'harvester.snat.label',
|
||||||
|
name: HCI.IPTABLES_SNAT_RULE,
|
||||||
|
namespaced: false,
|
||||||
|
weight: 191,
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: { resource: HCI.IPTABLES_SNAT_RULE }
|
||||||
|
},
|
||||||
|
exact: false,
|
||||||
|
ifHaveType: HCI.IPTABLES_SNAT_RULE,
|
||||||
|
});
|
||||||
|
|
||||||
|
configureType(HCI.IPTABLES_DNAT_RULE, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
labelKey: 'harvester.dnat.label',
|
||||||
|
name: HCI.IPTABLES_DNAT_RULE,
|
||||||
|
namespaced: false,
|
||||||
|
weight: 190,
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: { resource: HCI.IPTABLES_DNAT_RULE }
|
||||||
|
},
|
||||||
|
exact: false,
|
||||||
|
ifHaveType: HCI.IPTABLES_DNAT_RULE,
|
||||||
|
});
|
||||||
|
|
||||||
configureType(NETWORK_POLICY, { hiddenNamespaceGroupButton: true, canYaml: false });
|
configureType(NETWORK_POLICY, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
virtualType({
|
virtualType({
|
||||||
labelKey: 'harvester.networkPolicy.label',
|
labelKey: 'harvester.networkPolicy.label',
|
||||||
name: NETWORK_POLICY,
|
name: NETWORK_POLICY,
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
weight: 186,
|
weight: 194,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: NETWORK_POLICY }
|
params: { resource: NETWORK_POLICY }
|
||||||
@ -740,6 +850,53 @@ export function init($plugin, store) {
|
|||||||
ifHaveType: NETWORK_POLICY,
|
ifHaveType: NETWORK_POLICY,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
configureType(HCI.PROVIDER_NETWORK, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
labelKey: 'harvester.providerNetwork.label',
|
||||||
|
name: HCI.PROVIDER_NETWORK,
|
||||||
|
namespaced: false,
|
||||||
|
weight: 189,
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: { resource: HCI.PROVIDER_NETWORK }
|
||||||
|
},
|
||||||
|
exact: false,
|
||||||
|
ifHaveType: HCI.PROVIDER_NETWORK,
|
||||||
|
});
|
||||||
|
|
||||||
|
configureType(HCI.VLAN, { hiddenNamespaceGroupButton: true, canYaml: false });
|
||||||
|
|
||||||
|
headers(HCI.VLAN, [
|
||||||
|
STATE,
|
||||||
|
NAME_COL,
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
label: 'ID',
|
||||||
|
value: 'spec.id',
|
||||||
|
sort: 'spec.id'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'provider',
|
||||||
|
labelKey: 'harvester.subnet.provider.label',
|
||||||
|
value: 'spec.provider',
|
||||||
|
sort: 'spec.provider'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
virtualType({
|
||||||
|
labelKey: 'harvester.vlanNetwork.label',
|
||||||
|
name: HCI.VLAN,
|
||||||
|
namespaced: false,
|
||||||
|
weight: 188,
|
||||||
|
route: {
|
||||||
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
|
params: { resource: HCI.VLAN }
|
||||||
|
},
|
||||||
|
exact: false,
|
||||||
|
ifHaveType: HCI.VLAN,
|
||||||
|
});
|
||||||
|
|
||||||
configureType(HCI.SNAPSHOT, {
|
configureType(HCI.SNAPSHOT, {
|
||||||
isCreatable: false,
|
isCreatable: false,
|
||||||
location: {
|
location: {
|
||||||
@ -1094,7 +1251,7 @@ export function init($plugin, store) {
|
|||||||
labelKey: 'harvester.loadBalancer.label',
|
labelKey: 'harvester.loadBalancer.label',
|
||||||
name: HCI.LB,
|
name: HCI.LB,
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
weight: 185,
|
weight: 483,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: HCI.LB }
|
params: { resource: HCI.LB }
|
||||||
@ -1133,7 +1290,7 @@ export function init($plugin, store) {
|
|||||||
labelKey: 'harvester.ipPool.label',
|
labelKey: 'harvester.ipPool.label',
|
||||||
name: HCI.IP_POOL,
|
name: HCI.IP_POOL,
|
||||||
namespaced: false,
|
namespaced: false,
|
||||||
weight: 184,
|
weight: 482,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: HCI.IP_POOL }
|
params: { resource: HCI.IP_POOL }
|
||||||
@ -1154,7 +1311,7 @@ export function init($plugin, store) {
|
|||||||
labelKey: 'harvester.hostNetworkConfig.label',
|
labelKey: 'harvester.hostNetworkConfig.label',
|
||||||
name: HCI.HOST_NETWORK_CONFIG,
|
name: HCI.HOST_NETWORK_CONFIG,
|
||||||
namespaced: false,
|
namespaced: false,
|
||||||
weight: 183,
|
weight: 481,
|
||||||
route: {
|
route: {
|
||||||
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
name: `${ PRODUCT_NAME }-c-cluster-resource`,
|
||||||
params: { resource: HCI.HOST_NETWORK_CONFIG }
|
params: { resource: HCI.HOST_NETWORK_CONFIG }
|
||||||
|
|||||||
@ -82,4 +82,5 @@ export const HCI = {
|
|||||||
MAC_ADDRESS: 'harvesterhci.io/mac-address',
|
MAC_ADDRESS: 'harvesterhci.io/mac-address',
|
||||||
NODE_UPGRADE_PAUSE_MAP: 'harvesterhci.io/node-upgrade-pause-map',
|
NODE_UPGRADE_PAUSE_MAP: 'harvesterhci.io/node-upgrade-pause-map',
|
||||||
CDI_POPULATOR_KIND: 'cdi.kubevirt.io/storage.populator.kind',
|
CDI_POPULATOR_KIND: 'cdi.kubevirt.io/storage.populator.kind',
|
||||||
|
CNI_NETWORKS: 'k8s.v1.cni.cncf.io/networks',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,48 +1,50 @@
|
|||||||
export const HCI_SETTING = {
|
export const HCI_SETTING = {
|
||||||
BACKUP_TARGET: 'backup-target',
|
BACKUP_TARGET: 'backup-target',
|
||||||
CONTAINERD_REGISTRY: 'containerd-registry',
|
CONTAINERD_REGISTRY: 'containerd-registry',
|
||||||
LOG_LEVEL: 'log-level',
|
LOG_LEVEL: 'log-level',
|
||||||
SERVER_VERSION: 'server-version',
|
SERVER_VERSION: 'server-version',
|
||||||
UI_INDEX: 'ui-index',
|
UI_INDEX: 'ui-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',
|
||||||
UI_SOURCE: 'ui-source',
|
UI_SOURCE: 'ui-source',
|
||||||
UI_PL: 'ui-pl',
|
UI_PL: 'ui-pl',
|
||||||
HTTP_PROXY: 'http-proxy',
|
HTTP_PROXY: 'http-proxy',
|
||||||
ADDITIONAL_CA: 'additional-ca',
|
ADDITIONAL_CA: 'additional-ca',
|
||||||
OVERCOMMIT_CONFIG: 'overcommit-config',
|
OVERCOMMIT_CONFIG: 'overcommit-config',
|
||||||
CLUSTER_REGISTRATION_URL: 'cluster-registration-url',
|
CLUSTER_REGISTRATION_URL: 'cluster-registration-url',
|
||||||
DEFAULT_STORAGE_CLASS: 'default-storage-class',
|
DEFAULT_STORAGE_CLASS: 'default-storage-class',
|
||||||
SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout',
|
SUPPORT_BUNDLE_TIMEOUT: 'support-bundle-timeout',
|
||||||
SUPPORT_BUNDLE_EXPIRATION: 'support-bundle-expiration',
|
SUPPORT_BUNDLE_EXPIRATION: 'support-bundle-expiration',
|
||||||
SUPPORT_BUNDLE_FILE_NAME: 'support-bundle-file-name',
|
SUPPORT_BUNDLE_FILE_NAME: 'support-bundle-file-name',
|
||||||
SUPPORT_BUNDLE_IMAGE: 'support-bundle-image',
|
SUPPORT_BUNDLE_IMAGE: 'support-bundle-image',
|
||||||
SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT: 'support-bundle-node-collection-timeout',
|
SUPPORT_BUNDLE_NODE_COLLECTION_TIMEOUT: 'support-bundle-node-collection-timeout',
|
||||||
STORAGE_NETWORK: 'storage-network',
|
STORAGE_NETWORK: 'storage-network',
|
||||||
RWX_NETWORK: 'rwx-network',
|
RWX_NETWORK: 'rwx-network',
|
||||||
VM_FORCE_RESET_POLICY: 'vm-force-reset-policy',
|
VM_FORCE_RESET_POLICY: 'vm-force-reset-policy',
|
||||||
SSL_CERTIFICATES: 'ssl-certificates',
|
SSL_CERTIFICATES: 'ssl-certificates',
|
||||||
SSL_PARAMETERS: 'ssl-parameters',
|
SSL_PARAMETERS: 'ssl-parameters',
|
||||||
SUPPORT_BUNDLE_NAMESPACES: 'support-bundle-namespaces',
|
SUPPORT_BUNDLE_NAMESPACES: 'support-bundle-namespaces',
|
||||||
AUTO_DISK_PROVISION_PATHS: 'auto-disk-provision-paths',
|
AUTO_DISK_PROVISION_PATHS: 'auto-disk-provision-paths',
|
||||||
RELEASE_DOWNLOAD_URL: 'release-download-url',
|
RELEASE_DOWNLOAD_URL: 'release-download-url',
|
||||||
CCM_CSI_VERSION: 'harvester-csi-ccm-versions',
|
CCM_CSI_VERSION: 'harvester-csi-ccm-versions',
|
||||||
CSI_DRIVER_CONFIG: 'csi-driver-config',
|
CSI_DRIVER_CONFIG: 'csi-driver-config',
|
||||||
CSI_ONLINE_EXPAND_VALIDATION: 'csi-online-expand-validation',
|
CSI_ONLINE_EXPAND_VALIDATION: 'csi-online-expand-validation',
|
||||||
VM_TERMINATION_PERIOD: 'default-vm-termination-grace-period-seconds',
|
VM_TERMINATION_PERIOD: 'default-vm-termination-grace-period-seconds',
|
||||||
NTP_SERVERS: 'ntp-servers',
|
NTP_SERVERS: 'ntp-servers',
|
||||||
AUTO_ROTATE_RKE2_CERTS: 'auto-rotate-rke2-certs',
|
AUTO_ROTATE_RKE2_CERTS: 'auto-rotate-rke2-certs',
|
||||||
KUBECONFIG_DEFAULT_TOKEN_TTL_MINUTES: 'kubeconfig-default-token-ttl-minutes',
|
KUBECONFIG_DEFAULT_TOKEN_TTL_MINUTES: 'kubeconfig-default-token-ttl-minutes',
|
||||||
LONGHORN_V2_DATA_ENGINE_ENABLED: 'longhorn-v2-data-engine-enabled',
|
LONGHORN_V2_DATA_ENGINE_ENABLED: 'longhorn-v2-data-engine-enabled',
|
||||||
ADDITIONAL_GUEST_MEMORY_OVERHEAD_RATIO: 'additional-guest-memory-overhead-ratio',
|
LONGHORN_V2_DATA_ENGINE_HUGEPAGE_ENABLED: 'longhorn-v2-data-engine-hugepage-enabled',
|
||||||
UPGRADE_CONFIG: 'upgrade-config',
|
LONGHORN_V2_DATA_ENGINE_MEMORY_SIZE: 'longhorn-v2-data-engine-memory-size',
|
||||||
VM_MIGRATION_NETWORK: 'vm-migration-network',
|
ADDITIONAL_GUEST_MEMORY_OVERHEAD_RATIO: 'additional-guest-memory-overhead-ratio',
|
||||||
RANCHER_CLUSTER: 'rancher-cluster',
|
UPGRADE_CONFIG: 'upgrade-config',
|
||||||
MAX_HOTPLUG_RATIO: 'max-hotplug-ratio',
|
VM_MIGRATION_NETWORK: 'vm-migration-network',
|
||||||
KUBEVIRT_MIGRATION: 'kubevirt-migration',
|
RANCHER_CLUSTER: 'rancher-cluster',
|
||||||
INSTANCE_MANAGER_RESOURCES: 'instance-manager-resources',
|
MAX_HOTPLUG_RATIO: 'max-hotplug-ratio',
|
||||||
CLUSTER_POD_SECURITY_STANDARD: 'cluster-pod-security-standard'
|
KUBEVIRT_MIGRATION: 'kubevirt-migration',
|
||||||
|
INSTANCE_MANAGER_RESOURCES: 'instance-manager-resources',
|
||||||
|
CLUSTER_POD_SECURITY_STANDARD: 'cluster-pod-security-standard'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HCI_ALLOWED_SETTINGS = {
|
export const HCI_ALLOWED_SETTINGS = {
|
||||||
@ -112,6 +114,14 @@ export const HCI_ALLOWED_SETTINGS = {
|
|||||||
experimental: true,
|
experimental: true,
|
||||||
featureFlag: 'longhornV2LVMSupport'
|
featureFlag: 'longhornV2LVMSupport'
|
||||||
},
|
},
|
||||||
|
[HCI_SETTING.LONGHORN_V2_DATA_ENGINE_HUGEPAGE_ENABLED]: {
|
||||||
|
kind: 'boolean',
|
||||||
|
featureFlag: 'longhornV2HugepageSettings'
|
||||||
|
},
|
||||||
|
[HCI_SETTING.LONGHORN_V2_DATA_ENGINE_MEMORY_SIZE]: {
|
||||||
|
kind: 'number',
|
||||||
|
featureFlag: 'longhornV2HugepageSettings'
|
||||||
|
},
|
||||||
[HCI_SETTING.ADDITIONAL_GUEST_MEMORY_OVERHEAD_RATIO]: { kind: 'string', from: 'import' },
|
[HCI_SETTING.ADDITIONAL_GUEST_MEMORY_OVERHEAD_RATIO]: { kind: 'string', from: 'import' },
|
||||||
[HCI_SETTING.UPGRADE_CONFIG]: {
|
[HCI_SETTING.UPGRADE_CONFIG]: {
|
||||||
kind: 'json',
|
kind: 'json',
|
||||||
|
|||||||
@ -108,6 +108,7 @@ export default {
|
|||||||
<YamlEditor
|
<YamlEditor
|
||||||
ref="yamlUser"
|
ref="yamlUser"
|
||||||
v-model:value="config"
|
v-model:value="config"
|
||||||
|
:mode="mode"
|
||||||
class="yaml-editor"
|
class="yaml-editor"
|
||||||
:editor-mode="mode === 'view' ? 'VIEW_CODE' : 'EDIT_CODE'"
|
:editor-mode="mode === 'view' ? 'VIEW_CODE' : 'EDIT_CODE'"
|
||||||
@onChanges="update"
|
@onChanges="update"
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|||||||
import { HCI as HCI_LABELS_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
import { HCI as HCI_LABELS_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||||
import CreateEditView from '@shell/mixins/create-edit-view';
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { NAMESPACE, NODE } from '@shell/config/types';
|
||||||
import { HCI } from '../types';
|
import { HCI } from '../types';
|
||||||
import { NETWORK_TYPE, L2VLAN_MODE } from '../config/types';
|
import { NETWORK_TYPE, L2VLAN_MODE } from '../config/types';
|
||||||
import { removeObject } from '@shell/utils/array';
|
import { removeObject } from '@shell/utils/array';
|
||||||
@ -20,6 +21,7 @@ const { ACCESS, TRUNK } = L2VLAN_MODE;
|
|||||||
|
|
||||||
const AUTO = 'auto';
|
const AUTO = 'auto';
|
||||||
const MANUAL = 'manual';
|
const MANUAL = 'manual';
|
||||||
|
const KUBE_SYSTEM = 'kube-system';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['update:value'],
|
emits: ['update:value'],
|
||||||
@ -70,7 +72,12 @@ export default {
|
|||||||
async fetch() {
|
async fetch() {
|
||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
await allHash({ clusterNetworks: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER_NETWORK }) });
|
await allHash({
|
||||||
|
clusterNetworks: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.CLUSTER_NETWORK }),
|
||||||
|
namespaces: this.$store.dispatch(`${ inStore }/findAll`, { type: NAMESPACE }),
|
||||||
|
nodes: this.$store.dispatch(`${ inStore }/findAll`, { type: NODE }),
|
||||||
|
linkMonitors: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.LINK_MONITOR }),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
@ -199,6 +206,80 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.type === UNTAGGED;
|
return this.type === UNTAGGED;
|
||||||
|
},
|
||||||
|
|
||||||
|
showNicsTab() {
|
||||||
|
return this.isOverlayNetwork && this.value.metadata.namespace === KUBE_SYSTEM;
|
||||||
|
},
|
||||||
|
|
||||||
|
namespaceOptions() {
|
||||||
|
const ns = this.$store.getters['harvester/all'](NAMESPACE) || [];
|
||||||
|
|
||||||
|
// Allow users to select the "kube-system" namespace as the external subnet from Kube-OVN.
|
||||||
|
// This expects the provider network to be in the "kube-system" namespace for VPC NAT gateway functionality.
|
||||||
|
return ns
|
||||||
|
.filter((ns) => !ns.isSystem || ns.id === KUBE_SYSTEM)
|
||||||
|
.map((ns) => ({ name: ns.id }))
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
},
|
||||||
|
|
||||||
|
nodes() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const nodes = this.$store.getters[`${ inStore }/all`](NODE);
|
||||||
|
|
||||||
|
return nodes.filter((n) => n.isEtcd !== 'true');
|
||||||
|
},
|
||||||
|
|
||||||
|
nics() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const linkMonitor = this.$store.getters[`${ inStore }/byId`](HCI.LINK_MONITOR, 'nic') || {};
|
||||||
|
const linkStatus = linkMonitor?.status?.linkStatus || {};
|
||||||
|
const nodes = this.nodes.map((n) => n.id);
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
// Collect all nics from all nodes (for overlay, we select from all nodes)
|
||||||
|
Object.keys(linkStatus).map((nodeName) => {
|
||||||
|
if (nodes.includes(nodeName)) {
|
||||||
|
const nics = linkStatus[nodeName] || [];
|
||||||
|
|
||||||
|
nics.map((nic) => {
|
||||||
|
out.push({
|
||||||
|
...nic,
|
||||||
|
nodeName,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
nicOptions() {
|
||||||
|
const out = [];
|
||||||
|
const seen = new Set();
|
||||||
|
|
||||||
|
(this.nics || []).forEach((nic) => {
|
||||||
|
if (!seen.has(nic.name)) {
|
||||||
|
seen.add(nic.name);
|
||||||
|
out.push({
|
||||||
|
label: nic.name,
|
||||||
|
value: nic.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
},
|
||||||
|
|
||||||
|
master: {
|
||||||
|
get() {
|
||||||
|
return this.config?.master || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
set(value) {
|
||||||
|
this.config.master = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -214,6 +295,7 @@ export default {
|
|||||||
this.config.ipam = {};
|
this.config.ipam = {};
|
||||||
this.config.bridge = '';
|
this.config.bridge = '';
|
||||||
delete this.config.provider;
|
delete this.config.provider;
|
||||||
|
delete this.config.master;
|
||||||
delete this.config.server_socket;
|
delete this.config.server_socket;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -230,6 +312,13 @@ export default {
|
|||||||
this.config.vlan = '';
|
this.config.vlan = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'value.metadata.namespace'(newNamespace) {
|
||||||
|
// NIC selection is only valid for overlay in kube-system namespace.
|
||||||
|
if (newNamespace !== KUBE_SYSTEM) {
|
||||||
|
delete this.config.master;
|
||||||
|
this.value.spec.config = JSON.stringify({ ...this.config });
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -324,6 +413,10 @@ export default {
|
|||||||
delete this.config.promiscMode;
|
delete this.config.promiscMode;
|
||||||
delete this.config.vlan;
|
delete this.config.vlan;
|
||||||
delete this.config.ipam;
|
delete this.config.ipam;
|
||||||
|
|
||||||
|
if (this.value.metadata.namespace !== KUBE_SYSTEM) {
|
||||||
|
delete this.config.master;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isUntaggedNetwork) {
|
if (this.isUntaggedNetwork) {
|
||||||
@ -350,6 +443,7 @@ export default {
|
|||||||
ref="nd"
|
ref="nd"
|
||||||
:value="value"
|
:value="value"
|
||||||
:mode="mode"
|
:mode="mode"
|
||||||
|
:namespace-options="namespaceOptions"
|
||||||
@update:value="$emit('update:value', $event)"
|
@update:value="$emit('update:value', $event)"
|
||||||
/>
|
/>
|
||||||
<Tabbed
|
<Tabbed
|
||||||
@ -521,6 +615,25 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab
|
||||||
|
v-if="showNicsTab"
|
||||||
|
name="nics"
|
||||||
|
:label="t('harvester.network.tabs.nic')"
|
||||||
|
:weight="97"
|
||||||
|
class="bordered-table"
|
||||||
|
>
|
||||||
|
<div class="row mt-10">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="master"
|
||||||
|
:label="t('harvester.vlanConfig.uplink.nics.label')"
|
||||||
|
:placeholder="t('harvester.vlanConfig.uplink.nics.overlayWarning')"
|
||||||
|
:mode="mode"
|
||||||
|
:options="nicOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
</Tabbed>
|
</Tabbed>
|
||||||
</CruResource>
|
</CruResource>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
190
pkg/harvester/edit/kubeovn.io.iptablesdnatrule.vue
Normal file
190
pkg/harvester/edit/kubeovn.io.iptablesdnatrule.vue
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
<script>
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['update:value'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CruResource,
|
||||||
|
NameNsDescription,
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
LabeledInput,
|
||||||
|
LabeledSelect,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
eip: this.value?.spec?.eip || '',
|
||||||
|
externalPort: this.value?.spec?.externalPort || '',
|
||||||
|
internalIp: this.value?.spec?.internalIp || '',
|
||||||
|
internalPort: this.value?.spec?.internalPort || '',
|
||||||
|
protocol: this.value?.spec?.protocol || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
await allHash({ eips: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IPTABLES_EIP }) });
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
eipOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const eips = this.$store.getters[`${ inStore }/all`](HCI.IPTABLES_EIP) || [];
|
||||||
|
|
||||||
|
return eips.map((eip) => ({
|
||||||
|
label: eip.id,
|
||||||
|
value: eip.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
protocolOptions() {
|
||||||
|
return [
|
||||||
|
{ label: 'TCP', value: 'tcp' },
|
||||||
|
{ label: 'UDP', value: 'udp' },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateBeforeSave() {
|
||||||
|
if (!this.value.spec) {
|
||||||
|
this.value.spec = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value.spec.eip = this.eip;
|
||||||
|
this.value.spec.externalPort = this.externalPort;
|
||||||
|
this.value.spec.internalIp = this.internalIp;
|
||||||
|
this.value.spec.internalPort = this.internalPort;
|
||||||
|
this.value.spec.protocol = this.protocol;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
ref="nd"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:namespaced="false"
|
||||||
|
@update:value="$emit('update:value', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResourceTabs
|
||||||
|
class="mt-15"
|
||||||
|
:need-conditions="false"
|
||||||
|
:need-related="false"
|
||||||
|
:need-events="false"
|
||||||
|
:side-tabs="true"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="basic"
|
||||||
|
:label="t('generic.basic')"
|
||||||
|
:weight="99"
|
||||||
|
>
|
||||||
|
<div class="mt-20">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="eip"
|
||||||
|
class="mb-20"
|
||||||
|
:options="eipOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.dnat.eip.label')"
|
||||||
|
:placeholder="t('harvester.dnat.eip.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="externalPort"
|
||||||
|
class="mb-20"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.dnat.externalPort.label')"
|
||||||
|
:placeholder="t('harvester.dnat.externalPort.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="internalIp"
|
||||||
|
class="mb-20"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.dnat.internalIp.label')"
|
||||||
|
:placeholder="t('harvester.dnat.internalIp.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="internalPort"
|
||||||
|
class="mb-20"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.dnat.internalPort.label')"
|
||||||
|
:placeholder="t('harvester.dnat.internalPort.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="protocol"
|
||||||
|
class="mb-20"
|
||||||
|
:options="protocolOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.dnat.protocol.label')"
|
||||||
|
:placeholder="t('harvester.dnat.protocol.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
168
pkg/harvester/edit/kubeovn.io.iptableseip.vue
Normal file
168
pkg/harvester/edit/kubeovn.io.iptableseip.vue
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<script>
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['update:value'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CruResource,
|
||||||
|
NameNsDescription,
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
LabeledInput,
|
||||||
|
LabeledSelect,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
natGwDp: this.value?.spec?.natGwDp || '',
|
||||||
|
externalSubnet: this.value?.spec?.externalSubnet || '',
|
||||||
|
v4ip: this.value?.spec?.v4ip || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
await allHash({
|
||||||
|
natGateways: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VPC_NAT_GATEWAY }),
|
||||||
|
subnets: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SUBNET }),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
natGatewayOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const natGateways = this.$store.getters[`${ inStore }/all`](HCI.VPC_NAT_GATEWAY) || [];
|
||||||
|
|
||||||
|
return natGateways.map((gw) => ({
|
||||||
|
label: gw.id,
|
||||||
|
value: gw.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
subnetOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const subnets = this.$store.getters[`${ inStore }/all`](HCI.SUBNET) || [];
|
||||||
|
|
||||||
|
return subnets.map((subnet) => ({
|
||||||
|
label: subnet.id,
|
||||||
|
value: subnet.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateBeforeSave() {
|
||||||
|
if (!this.value.spec) {
|
||||||
|
this.value.spec = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value.spec.natGwDp = this.natGwDp;
|
||||||
|
this.value.spec.externalSubnet = this.externalSubnet;
|
||||||
|
this.value.spec.v4ip = this.v4ip;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
ref="nd"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:namespaced="false"
|
||||||
|
@update:value="$emit('update:value', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResourceTabs
|
||||||
|
class="mt-15"
|
||||||
|
:need-conditions="false"
|
||||||
|
:need-related="false"
|
||||||
|
:need-events="false"
|
||||||
|
:side-tabs="true"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="basic"
|
||||||
|
:label="t('generic.basic')"
|
||||||
|
:weight="99"
|
||||||
|
>
|
||||||
|
<div class="mt-20">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="natGwDp"
|
||||||
|
class="mb-20"
|
||||||
|
:options="natGatewayOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.externalIP.natGateway.label')"
|
||||||
|
:placeholder="t('harvester.externalIP.natGateway.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="externalSubnet"
|
||||||
|
class="mb-20"
|
||||||
|
:options="subnetOptions"
|
||||||
|
:label="t('harvester.externalIP.externalSubnet.label')"
|
||||||
|
:placeholder="t('harvester.externalIP.externalSubnet.placeholder')"
|
||||||
|
:mode="mode"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="v4ip"
|
||||||
|
class="mb-20"
|
||||||
|
:label="t('harvester.externalIP.v4ip.label')"
|
||||||
|
:placeholder="t('harvester.externalIP.v4ip.placeholder')"
|
||||||
|
:mode="mode"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
140
pkg/harvester/edit/kubeovn.io.iptablessnatrule.vue
Normal file
140
pkg/harvester/edit/kubeovn.io.iptablessnatrule.vue
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<script>
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['update:value'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CruResource,
|
||||||
|
NameNsDescription,
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
LabeledInput,
|
||||||
|
LabeledSelect,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
eip: this.value?.spec?.eip || '',
|
||||||
|
internalCIDR: this.value?.spec?.internalCIDR || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
await allHash({ eips: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IPTABLES_EIP }) });
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
eipOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const eips = this.$store.getters[`${ inStore }/all`](HCI.IPTABLES_EIP) || [];
|
||||||
|
|
||||||
|
return eips.map((eip) => ({
|
||||||
|
label: eip.id,
|
||||||
|
value: eip.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateBeforeSave() {
|
||||||
|
if (!this.value.spec) {
|
||||||
|
this.value.spec = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value.spec.eip = this.eip;
|
||||||
|
this.value.spec.internalCIDR = this.internalCIDR;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
ref="nd"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:namespaced="false"
|
||||||
|
@update:value="$emit('update:value', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResourceTabs
|
||||||
|
class="mt-15"
|
||||||
|
:need-conditions="false"
|
||||||
|
:need-related="false"
|
||||||
|
:need-events="false"
|
||||||
|
:side-tabs="true"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="basic"
|
||||||
|
:label="t('generic.basic')"
|
||||||
|
:weight="99"
|
||||||
|
>
|
||||||
|
<div class="mt-20">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="eip"
|
||||||
|
class="mb-20"
|
||||||
|
:options="eipOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.snat.eip.label')"
|
||||||
|
:placeholder="t('harvester.snat.eip.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="internalCIDR"
|
||||||
|
class="mb-20"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.snat.internalCIDR.label')"
|
||||||
|
:placeholder="t('harvester.snat.internalCIDR.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
384
pkg/harvester/edit/kubeovn.io.providernetwork.vue
Normal file
384
pkg/harvester/edit/kubeovn.io.providernetwork.vue
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
<script>
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import ArrayList from '@shell/components/form/ArrayList';
|
||||||
|
import InfoBox from '@shell/components/InfoBox';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import ArrayListSelect from '@shell/components/form/ArrayListSelect';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { NODE } from '@shell/config/types';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['update:value'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CruResource,
|
||||||
|
ArrayList,
|
||||||
|
InfoBox,
|
||||||
|
NameNsDescription,
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
LabeledSelect,
|
||||||
|
ArrayListSelect,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
defaultInterface: this.value?.spec?.defaultInterface || '',
|
||||||
|
excludedNodes: this.value?.spec?.excludeNodes || [],
|
||||||
|
customInterfaces: this.value?.spec?.customInterfaces || [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
await allHash({
|
||||||
|
nodes: this.$store.dispatch(`${ inStore }/findAll`, { type: NODE }),
|
||||||
|
linkMonitors: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.LINK_MONITOR }),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
nodes() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const nodes = this.$store.getters[`${ inStore }/all`](NODE);
|
||||||
|
|
||||||
|
return nodes.filter((n) => n.isEtcd !== 'true');
|
||||||
|
},
|
||||||
|
|
||||||
|
nics() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const linkMonitor = this.$store.getters[`${ inStore }/byId`](HCI.LINK_MONITOR, 'nic') || {};
|
||||||
|
const linkStatus = linkMonitor?.status?.linkStatus || {};
|
||||||
|
const nodes = this.nodes.map((n) => n.id);
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
// Collect all nics from all nodes
|
||||||
|
Object.keys(linkStatus).map((nodeName) => {
|
||||||
|
if (nodes.includes(nodeName)) {
|
||||||
|
const nics = linkStatus[nodeName] || [];
|
||||||
|
|
||||||
|
nics.map((nic) => {
|
||||||
|
out.push({
|
||||||
|
...nic,
|
||||||
|
nodeName,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
nicOptions() {
|
||||||
|
const out = [];
|
||||||
|
const seen = new Set();
|
||||||
|
|
||||||
|
(this.nics || []).forEach((nic) => {
|
||||||
|
if (!seen.has(nic.name)) {
|
||||||
|
seen.add(nic.name);
|
||||||
|
out.push({
|
||||||
|
label: nic.name,
|
||||||
|
value: nic.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
|
},
|
||||||
|
|
||||||
|
nodeOptions() {
|
||||||
|
return this.nodes.map((node) => ({
|
||||||
|
label: node.id,
|
||||||
|
value: node.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
removeCustomInterface(index) {
|
||||||
|
this.customInterfaces.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateBeforeSave() {
|
||||||
|
if (!this.value.spec) {
|
||||||
|
this.value.spec = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value.spec.defaultInterface = this.defaultInterface;
|
||||||
|
this.value.spec.excludeNodes = this.excludedNodes;
|
||||||
|
this.value.spec.customInterfaces = (this.customInterfaces || [])
|
||||||
|
.filter((item) => item?.interface || (item?.nodes || []).length)
|
||||||
|
.map((item) => ({
|
||||||
|
interface: item.interface || '',
|
||||||
|
nodes: (item.nodes || []).filter((node) => !!node),
|
||||||
|
}))
|
||||||
|
.filter((item) => item.interface && item.nodes.length > 0);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
ref="nd"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:namespaced="false"
|
||||||
|
@update:value="$emit('update:value', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResourceTabs
|
||||||
|
class="mt-15"
|
||||||
|
:need-conditions="false"
|
||||||
|
:need-related="false"
|
||||||
|
:need-events="false"
|
||||||
|
:side-tabs="true"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="Interfaces"
|
||||||
|
label="Interfaces"
|
||||||
|
:weight="99"
|
||||||
|
>
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="defaultInterface"
|
||||||
|
class="mb-20"
|
||||||
|
required
|
||||||
|
:options="nicOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.providerNetwork.defaultInterface.label')"
|
||||||
|
:placeholder="t('harvester.providerNetwork.defaultInterface.placeholder')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<hr class="section-divider" />
|
||||||
|
|
||||||
|
<ArrayList
|
||||||
|
v-model:value="customInterfaces"
|
||||||
|
class="mb-20 custom-interface-list"
|
||||||
|
:mode="mode"
|
||||||
|
:title="t('harvester.providerNetwork.customInterfaces.label')"
|
||||||
|
:protip="false"
|
||||||
|
:remove-allowed="false"
|
||||||
|
:initial-empty-row="true"
|
||||||
|
:default-add-value="{ interface: '', nodes: [] }"
|
||||||
|
>
|
||||||
|
<template #add="{ add }">
|
||||||
|
<div class="custom-interface-primary-add">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn role-primary"
|
||||||
|
:disabled="mode === 'view'"
|
||||||
|
@click="add"
|
||||||
|
>
|
||||||
|
{{ t('harvester.providerNetwork.customInterfaces.addLabel') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #column-headers>
|
||||||
|
<div class="row custom-interface-header">
|
||||||
|
<div class="col span-6">
|
||||||
|
{{ t('harvester.providerNetwork.customInterfaces.interface.label') }}
|
||||||
|
</div>
|
||||||
|
<div class="col span-6">
|
||||||
|
{{ t('harvester.providerNetwork.customInterfaces.nodes.label') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #columns="scope">
|
||||||
|
<InfoBox class="custom-interface-box">
|
||||||
|
<button
|
||||||
|
v-if="mode !== 'view'"
|
||||||
|
type="button"
|
||||||
|
class="role-link btn btn-sm remove"
|
||||||
|
@click="removeCustomInterface(scope.i)"
|
||||||
|
>
|
||||||
|
<i class="icon icon-x" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="custom-interface-content">
|
||||||
|
<div class="row custom-interface-row interface-row">
|
||||||
|
<div class="col span-12 interface-col">
|
||||||
|
<h3 class="mb-10">
|
||||||
|
{{ t('harvester.providerNetwork.customInterfaces.interface.label') }}
|
||||||
|
</h3>
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="scope.row.value.interface"
|
||||||
|
class="mb-20"
|
||||||
|
:label="''"
|
||||||
|
:options="nicOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:placeholder="t('harvester.providerNetwork.customInterfaces.interface.placeholder')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row custom-interface-row nodes-row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<ArrayListSelect
|
||||||
|
v-model:value="scope.row.value.nodes"
|
||||||
|
:options="nodeOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:disabled="mode === 'view'"
|
||||||
|
:enable-default-add-value="false"
|
||||||
|
:array-list-props="{
|
||||||
|
addLabel: t('harvester.providerNetwork.customInterfaces.nodes.addLabel'),
|
||||||
|
initialEmptyRow: true,
|
||||||
|
title: t('harvester.providerNetwork.customInterfaces.nodes.label'),
|
||||||
|
required: false,
|
||||||
|
protip: false,
|
||||||
|
}"
|
||||||
|
:select-props="{
|
||||||
|
placeholder: t('harvester.providerNetwork.customInterfaces.nodes.placeholder'),
|
||||||
|
disabled: mode === 'view',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #add="{ add }">
|
||||||
|
<div class="custom-interface-add">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn role-tertiary add"
|
||||||
|
:disabled="mode === 'view'"
|
||||||
|
@click="add"
|
||||||
|
>
|
||||||
|
{{ t('harvester.providerNetwork.customInterfaces.nodes.addLabel') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ArrayListSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</InfoBox>
|
||||||
|
</template>
|
||||||
|
</ArrayList>
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab
|
||||||
|
name="excludedNodes"
|
||||||
|
:label="t('harvester.providerNetwork.excludedNodes.label')"
|
||||||
|
:weight="98"
|
||||||
|
>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<ArrayListSelect
|
||||||
|
v-model:value="excludedNodes"
|
||||||
|
:options="nodeOptions"
|
||||||
|
:disabled="mode === 'view'"
|
||||||
|
:mode="mode"
|
||||||
|
:enable-default-add-value="false"
|
||||||
|
:array-list-props="{
|
||||||
|
addLabel: t('harvester.providerNetwork.excludedNodes.addLabel'),
|
||||||
|
initialEmptyRow: true,
|
||||||
|
required: false,
|
||||||
|
protip: false,
|
||||||
|
}"
|
||||||
|
:select-props="{
|
||||||
|
placeholder: t('harvester.providerNetwork.excludedNodes.placeholder'),
|
||||||
|
disabled: mode === 'view',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.section-divider {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
margin: 10px 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-interface-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-interface-row {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interface-row {
|
||||||
|
width: calc(100% - 90px);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodes-row {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-interface-add {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.nodes-row .array-list-select .box) {
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.nodes-row .array-list-select .box .remove) {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-interface-primary-add {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-interface-box {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.custom-interface-list .box) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -64,8 +64,9 @@ export default {
|
|||||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
const hash = {
|
const hash = {
|
||||||
vpc: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VPC }),
|
vpc: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VPC }),
|
||||||
nad: this.$store.dispatch(`${ inStore }/findAll`, { type: NETWORK_ATTACHMENT }),
|
nad: this.$store.dispatch(`${ inStore }/findAll`, { type: NETWORK_ATTACHMENT }),
|
||||||
|
vlans: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VLAN }),
|
||||||
};
|
};
|
||||||
|
|
||||||
await allHash(hash);
|
await allHash(hash);
|
||||||
@ -135,6 +136,16 @@ export default {
|
|||||||
natOutgoingDisabled() {
|
natOutgoingDisabled() {
|
||||||
// Disable the NAT Outgoing option when the subnet belongs to the ovn-cluster VPC and its name is join or ovn-default.
|
// Disable the NAT Outgoing option when the subnet belongs to the ovn-cluster VPC and its name is join or ovn-default.
|
||||||
return this.value?.spec?.vpc === 'ovn-cluster' && ['join', 'ovn-default'].includes(this.value?.metadata?.name);
|
return this.value?.spec?.vpc === 'ovn-cluster' && ['join', 'ovn-default'].includes(this.value?.metadata?.name);
|
||||||
|
},
|
||||||
|
|
||||||
|
vlanOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const vlans = this.$store.getters[`${ inStore }/all`](HCI.VLAN) || [];
|
||||||
|
|
||||||
|
return vlans.map((vlan) => ({
|
||||||
|
label: vlan.id,
|
||||||
|
value: vlan.id,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -270,6 +281,16 @@ export default {
|
|||||||
:mode="mode"
|
:mode="mode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col span-6">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="value.spec.vlan"
|
||||||
|
class="mb-20"
|
||||||
|
:options="vlanOptions"
|
||||||
|
:placeholder="t('harvester.subnet.vlan.placeholder')"
|
||||||
|
:label="t('harvester.subnet.vlan.label')"
|
||||||
|
:mode="mode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt-20">
|
<div class="row mt-20">
|
||||||
<div class="col span-6">
|
<div class="col span-6">
|
||||||
|
|||||||
146
pkg/harvester/edit/kubeovn.io.vlan.vue
Normal file
146
pkg/harvester/edit/kubeovn.io.vlan.vue
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<script>
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['update:value'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CruResource,
|
||||||
|
NameNsDescription,
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
LabeledInput,
|
||||||
|
LabeledSelect,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
vlanId: this.value?.spec?.id || '',
|
||||||
|
provider: this.value?.spec?.provider || '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
await allHash({ providerNetworks: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.PROVIDER_NETWORK }) });
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
providerNetworks() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const providerNetworks = this.$store.getters[`${ inStore }/all`](HCI.PROVIDER_NETWORK) || [];
|
||||||
|
|
||||||
|
return providerNetworks.map((pn) => ({
|
||||||
|
label: pn.id,
|
||||||
|
value: pn.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateBeforeSave() {
|
||||||
|
if (!this.value.spec) {
|
||||||
|
this.value.spec = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.vlanId !== '') {
|
||||||
|
this.value.spec.id = Number(this.vlanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value.spec.provider = this.provider;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
ref="nd"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:namespaced="false"
|
||||||
|
@update:value="$emit('update:value', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResourceTabs
|
||||||
|
class="mt-15"
|
||||||
|
:need-conditions="false"
|
||||||
|
:need-related="false"
|
||||||
|
:need-events="false"
|
||||||
|
:side-tabs="true"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="basic"
|
||||||
|
:label="t('generic.basic')"
|
||||||
|
:weight="99"
|
||||||
|
>
|
||||||
|
<div class="mt-20">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value.number="vlanId"
|
||||||
|
class="mb-20"
|
||||||
|
type="number"
|
||||||
|
:min="1"
|
||||||
|
:max="4094"
|
||||||
|
:label="t('harvester.vlan.id.label')"
|
||||||
|
:placeholder="t('harvester.vlan.id.placeholder')"
|
||||||
|
:mode="mode"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="provider"
|
||||||
|
class="mb-20"
|
||||||
|
:options="providerNetworks"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.vlan.provider.label')"
|
||||||
|
:placeholder="t('harvester.vlan.provider.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
246
pkg/harvester/edit/kubeovn.io.vpcnatgateway.vue
Normal file
246
pkg/harvester/edit/kubeovn.io.vpcnatgateway.vue
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
<script>
|
||||||
|
import CruResource from '@shell/components/CruResource';
|
||||||
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||||
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
||||||
|
import Tab from '@shell/components/Tabbed/Tab';
|
||||||
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
||||||
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||||
|
import ArrayListSelect from '@shell/components/form/ArrayListSelect';
|
||||||
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
||||||
|
import { allHash } from '@shell/utils/promise';
|
||||||
|
import { HCI as HCI_ANNOTATIONS } from '@pkg/config/labels-annotations';
|
||||||
|
import { HCI } from '../types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['update:value'],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
CruResource,
|
||||||
|
NameNsDescription,
|
||||||
|
ResourceTabs,
|
||||||
|
Tab,
|
||||||
|
LabeledInput,
|
||||||
|
LabeledSelect,
|
||||||
|
ArrayListSelect,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [CreateEditView],
|
||||||
|
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
const internalTenantNetwork = this.value?.metadata?.annotations?.[HCI_ANNOTATIONS.CNI_NETWORKS] || '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
internalTenantNetwork,
|
||||||
|
vpc: this.value?.spec?.vpc || '',
|
||||||
|
subnet: this.value?.spec?.subnet || '',
|
||||||
|
lanIp: this.value?.spec?.lanIp || '',
|
||||||
|
externalSubnets: this.value?.spec?.externalSubnets || [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
|
||||||
|
await allHash({
|
||||||
|
vpcs: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VPC }),
|
||||||
|
subnets: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.SUBNET }),
|
||||||
|
vmNetworks: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.NETWORK_ATTACHMENT }),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.registerBeforeHook) {
|
||||||
|
this.registerBeforeHook(this.updateBeforeSave);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
vpcOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const vpcs = this.$store.getters[`${ inStore }/all`](HCI.VPC) || [];
|
||||||
|
|
||||||
|
return vpcs.map((vpc) => ({
|
||||||
|
label: vpc.id,
|
||||||
|
value: vpc.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
subnetOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const subnets = this.$store.getters[`${ inStore }/all`](HCI.SUBNET) || [];
|
||||||
|
|
||||||
|
return subnets.map((subnet) => ({
|
||||||
|
label: subnet.id,
|
||||||
|
value: subnet.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
vmNetworkOptions() {
|
||||||
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||||
|
const vmNetworks = this.$store.getters[`${ inStore }/all`](HCI.NETWORK_ATTACHMENT) || [];
|
||||||
|
|
||||||
|
return vmNetworks.map((network) => ({
|
||||||
|
label: network.id,
|
||||||
|
value: network.id,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
updateBeforeSave() {
|
||||||
|
if (!this.value.spec) {
|
||||||
|
this.value.spec = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.value.metadata) {
|
||||||
|
this.value.metadata = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.value.metadata.annotations) {
|
||||||
|
this.value.metadata.annotations = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value.spec.vpc = this.vpc;
|
||||||
|
this.value.spec.subnet = this.subnet;
|
||||||
|
this.value.spec.lanIp = this.lanIp;
|
||||||
|
this.value.spec.externalSubnets = (this.externalSubnets || []).filter((subnet) => !!subnet);
|
||||||
|
|
||||||
|
if (this.internalTenantNetwork) {
|
||||||
|
this.value.metadata.annotations[HCI_ANNOTATIONS.CNI_NETWORKS] = this.internalTenantNetwork;
|
||||||
|
} else {
|
||||||
|
delete this.value.metadata.annotations[HCI_ANNOTATIONS.CNI_NETWORKS];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CruResource
|
||||||
|
:done-route="doneRoute"
|
||||||
|
:resource="value"
|
||||||
|
:mode="mode"
|
||||||
|
:errors="errors"
|
||||||
|
:apply-hooks="applyHooks"
|
||||||
|
@finish="save"
|
||||||
|
@error="e=>errors=e"
|
||||||
|
>
|
||||||
|
<NameNsDescription
|
||||||
|
ref="nd"
|
||||||
|
:value="value"
|
||||||
|
:mode="mode"
|
||||||
|
:namespaced="false"
|
||||||
|
@update:value="$emit('update:value', $event)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ResourceTabs
|
||||||
|
class="mt-15"
|
||||||
|
:need-conditions="false"
|
||||||
|
:need-related="false"
|
||||||
|
:need-events="false"
|
||||||
|
:side-tabs="true"
|
||||||
|
:mode="mode"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
name="basic"
|
||||||
|
:label="t('generic.basic')"
|
||||||
|
:weight="99"
|
||||||
|
>
|
||||||
|
<div class="mt-20">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="internalTenantNetwork"
|
||||||
|
class="mb-20"
|
||||||
|
required
|
||||||
|
:options="vmNetworkOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.natGateway.internalTenantNetwork.label')"
|
||||||
|
:placeholder="t('harvester.natGateway.internalTenantNetwork.placeholder')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="vpc"
|
||||||
|
class="mb-20"
|
||||||
|
:options="vpcOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.natGateway.vpc.label')"
|
||||||
|
:placeholder="t('harvester.natGateway.vpc.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledSelect
|
||||||
|
v-model:value="subnet"
|
||||||
|
class="mb-20"
|
||||||
|
:options="subnetOptions"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.natGateway.subnet.label')"
|
||||||
|
:placeholder="t('harvester.natGateway.subnet.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<LabeledInput
|
||||||
|
v-model:value="lanIp"
|
||||||
|
class="mb-20"
|
||||||
|
:mode="mode"
|
||||||
|
:label="t('harvester.natGateway.lanIp.label')"
|
||||||
|
:placeholder="t('harvester.natGateway.lanIp.placeholder')"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab
|
||||||
|
name="externalSubnets"
|
||||||
|
:label="t('harvester.natGateway.externalSubnets.label')"
|
||||||
|
:weight="98"
|
||||||
|
>
|
||||||
|
<div class="mt-20">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
<ArrayListSelect
|
||||||
|
v-model:value="externalSubnets"
|
||||||
|
:mode="mode"
|
||||||
|
:disabled="mode === 'view'"
|
||||||
|
required
|
||||||
|
:options="subnetOptions"
|
||||||
|
:enable-default-add-value="false"
|
||||||
|
:array-list-props="{
|
||||||
|
addLabel: t('harvester.natGateway.externalSubnets.addLabel'),
|
||||||
|
title: t('harvester.natGateway.subnet.label'),
|
||||||
|
initialEmptyRow: true,
|
||||||
|
required: true,
|
||||||
|
protip: false,
|
||||||
|
}"
|
||||||
|
:select-props="{
|
||||||
|
placeholder: t('harvester.natGateway.externalSubnets.placeholder'),
|
||||||
|
disabled: mode === 'view',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
</ResourceTabs>
|
||||||
|
</CruResource>
|
||||||
|
</template>
|
||||||
@ -143,6 +143,7 @@ export default {
|
|||||||
<YamlEditor
|
<YamlEditor
|
||||||
ref="yaml"
|
ref="yaml"
|
||||||
v-model:value="yamlScript"
|
v-model:value="yamlScript"
|
||||||
|
:mode="mode"
|
||||||
class="yaml-editor"
|
class="yaml-editor"
|
||||||
:editor-mode="editorMode"
|
:editor-mode="editorMode"
|
||||||
/>
|
/>
|
||||||
|
|||||||
24
pkg/harvester/formatters/HarvesterVlan.vue
Normal file
24
pkg/harvester/formatters/HarvesterVlan.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HarvesterVlanFormatter',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span v-if="value">
|
||||||
|
{{ value }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-muted"
|
||||||
|
>
|
||||||
|
—
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
@ -348,6 +348,9 @@ harvester:
|
|||||||
vmImportSourceOClusterStatus: Cluster Status
|
vmImportSourceOClusterStatus: Cluster Status
|
||||||
vmImportSourceOVAUrl: URL
|
vmImportSourceOVAUrl: URL
|
||||||
vmImportSourceOVAStatus: Status
|
vmImportSourceOVAStatus: Status
|
||||||
|
v4ip: V4 IP
|
||||||
|
v6ip: V6 IP
|
||||||
|
eipName: EIP Name
|
||||||
tab:
|
tab:
|
||||||
volume: Volumes
|
volume: Volumes
|
||||||
network: Networks
|
network: Networks
|
||||||
@ -781,7 +784,7 @@ harvester:
|
|||||||
addPort: Add Port
|
addPort: Add Port
|
||||||
cloudConfig:
|
cloudConfig:
|
||||||
title: Cloud Configuration
|
title: Cloud Configuration
|
||||||
createTemplateTitle: 'Create {name}.'
|
createTemplateTitle: 'Create {name}'
|
||||||
createNew: Create new...
|
createNew: Create new...
|
||||||
cloudInit:
|
cloudInit:
|
||||||
label: Cloud Init
|
label: Cloud Init
|
||||||
@ -1155,6 +1158,9 @@ harvester:
|
|||||||
gateway:
|
gateway:
|
||||||
label: Gateway IP
|
label: Gateway IP
|
||||||
placeholder: e.g. 172.20.0.1
|
placeholder: e.g. 172.20.0.1
|
||||||
|
vlan:
|
||||||
|
label: VLAN
|
||||||
|
placeholder: Select a VLAN
|
||||||
dhcp:
|
dhcp:
|
||||||
label: Dynamic Host Configuration Protocol (DHCP)
|
label: Dynamic Host Configuration Protocol (DHCP)
|
||||||
v4Options: DHCPV4Options
|
v4Options: DHCPV4Options
|
||||||
@ -1240,14 +1246,97 @@ harvester:
|
|||||||
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.
|
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.
|
||||||
|
natGateway:
|
||||||
|
label: Gateways
|
||||||
|
internalTenantNetwork:
|
||||||
|
label: Internal Tenant Network
|
||||||
|
placeholder: Select a Virtual Machine Network
|
||||||
|
vpc:
|
||||||
|
label: VPC
|
||||||
|
placeholder: Select a VPC
|
||||||
|
subnet:
|
||||||
|
label: Subnet
|
||||||
|
placeholder: Select a subnet
|
||||||
|
lanIp:
|
||||||
|
label: LAN IP
|
||||||
|
placeholder: Enter LAN IP
|
||||||
|
externalSubnets:
|
||||||
|
label: External Subnets
|
||||||
|
addLabel: Add
|
||||||
|
placeholder: Select a subnet
|
||||||
|
externalIP:
|
||||||
|
label: External IPs
|
||||||
|
natGateway:
|
||||||
|
label: VpcNatGateway
|
||||||
|
placeholder: Select a VpcNatGateway
|
||||||
|
externalSubnet:
|
||||||
|
label: External Subnet
|
||||||
|
placeholder: Select an external subnet
|
||||||
|
v4ip:
|
||||||
|
label: V4 IP
|
||||||
|
placeholder: public ip from external subnet
|
||||||
|
snat:
|
||||||
|
label: Source Rules
|
||||||
|
eip:
|
||||||
|
label: EIP
|
||||||
|
placeholder: Select an external IP
|
||||||
|
internalCIDR:
|
||||||
|
label: Internal CIDR
|
||||||
|
placeholder: internal subnet CIDR
|
||||||
|
dnat:
|
||||||
|
label: Destination Rules
|
||||||
|
eip:
|
||||||
|
label: EIP
|
||||||
|
placeholder: Select an external IP
|
||||||
|
externalPort:
|
||||||
|
label: External Port
|
||||||
|
placeholder: port number
|
||||||
|
internalIp:
|
||||||
|
label: Internal IP
|
||||||
|
placeholder: internal IP address
|
||||||
|
internalPort:
|
||||||
|
label: Internal Port
|
||||||
|
placeholder: port number
|
||||||
|
protocol:
|
||||||
|
label: Protocol
|
||||||
|
placeholder: Select protocol (tcp or udp)
|
||||||
|
providerNetwork:
|
||||||
|
label: Provider Networks
|
||||||
|
defaultInterface:
|
||||||
|
label: Default Interface
|
||||||
|
placeholder: Select the interface the same as master interface of external overlay network
|
||||||
|
customInterfaces:
|
||||||
|
label: Custom Interfaces
|
||||||
|
addLabel: Add Custom Interface
|
||||||
|
interface:
|
||||||
|
label: Network Interface
|
||||||
|
placeholder: e.g. eth2
|
||||||
|
nodes:
|
||||||
|
label: Nodes
|
||||||
|
addLabel: Add Node
|
||||||
|
placeholder: Select a node
|
||||||
|
excludedNodes:
|
||||||
|
label: Excluded Nodes
|
||||||
|
addLabel: Add Excluded Node
|
||||||
|
placeholder: Select node to exclude from this provider network
|
||||||
|
vlanNetwork:
|
||||||
|
label: VLANs
|
||||||
|
vlan:
|
||||||
|
id:
|
||||||
|
label: VLAN ID
|
||||||
|
placeholder: "e.g. 1-4094"
|
||||||
|
provider:
|
||||||
|
label: Provider Network
|
||||||
|
placeholder: Select a provider network
|
||||||
networkPolicy:
|
networkPolicy:
|
||||||
label: Network Policies
|
label: 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.
|
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:
|
||||||
basics: Basics
|
basics: Basics
|
||||||
layer3Network: Route
|
layer3Network: Route
|
||||||
|
nic: Network Interface Card
|
||||||
clusterNetwork:
|
clusterNetwork:
|
||||||
label: Cluster Network
|
label: Cluster Network
|
||||||
create: Create a new cluster network
|
create: Create a new cluster network
|
||||||
@ -1634,8 +1723,9 @@ harvester:
|
|||||||
schedulingRules: Select node(s) matching rules
|
schedulingRules: Select node(s) matching rules
|
||||||
uplink:
|
uplink:
|
||||||
nics:
|
nics:
|
||||||
label: NICs
|
label: NIC
|
||||||
addLabel: Add NIC
|
addLabel: Add NIC
|
||||||
|
overlayWarning: The NIC selected here must match the NIC provided in the provider network.
|
||||||
placeholder: Select a NIC that is available on all the selected nodes
|
placeholder: Select a NIC that is available on all the selected nodes
|
||||||
validate:
|
validate:
|
||||||
available: NIC "{nic}" is not available on the selected nodes
|
available: NIC "{nic}" is not available on the selected nodes
|
||||||
@ -2090,6 +2180,8 @@ advancedSettings:
|
|||||||
'harv-auto-rotate-rke2-certs': The certificate rotation mechanism relies on Rancher. Harvester will automatically update certificates generation to trigger rotation.
|
'harv-auto-rotate-rke2-certs': The certificate rotation mechanism relies on Rancher. Harvester will automatically update certificates generation to trigger rotation.
|
||||||
'harv-kubeconfig-default-token-ttl-minutes': 'TTL (in minutes) applied on Harvester administration kubeconfig files. Default is 0, which means to never expire.'
|
'harv-kubeconfig-default-token-ttl-minutes': 'TTL (in minutes) applied on Harvester administration kubeconfig files. Default is 0, which means to never expire.'
|
||||||
'harv-longhorn-v2-data-engine-enabled': 'Enable the Longhorn V2 data engine. Default is false. <ul><li>Changing this setting will restart RKE2 on all nodes. This will not affect running VM workloads.</li><li>If you see "not enough hugepages-2Mi capacity" errors when enabling this setting, wait a minute for the error to clear. If the error remains, reboot the affected node.</li></ul>'
|
'harv-longhorn-v2-data-engine-enabled': 'Enable the Longhorn V2 data engine. Default is false. <ul><li>Changing this setting will restart RKE2 on all nodes. This will not affect running VM workloads.</li><li>If you see "not enough hugepages-2Mi capacity" errors when enabling this setting, wait a minute for the error to clear. If the error remains, reboot the affected node.</li></ul>'
|
||||||
|
'harv-longhorn-v2-data-engine-hugepage-enabled': 'Enable hugepages when using the Longhorn V2 data engine. Default is true. Disabling hugepages reduces memory pressure on low-spec nodes and increases deployment flexibility. However, performance may be lower compared to running with hugepages.'
|
||||||
|
'harv-longhorn-v2-data-engine-memory-size': 'Configure the amount of memory allocated to the SPDK target daemon when using the Longhorn V2 data engine. Default is 2048 MiB.'
|
||||||
'harv-additional-guest-memory-overhead-ratio': 'The ratio for kubevirt to adjust the VM overhead memory. The value could be zero, empty value or floating number between 1.0 and 10.0, default to 1.5.'
|
'harv-additional-guest-memory-overhead-ratio': 'The ratio for kubevirt to adjust the VM overhead memory. The value could be zero, empty value or floating number between 1.0 and 10.0, default to 1.5.'
|
||||||
'harv-upgrade-config': 'Configure image preloading and VM restore options for upgrades. See related fields in <a href="{url}" target="_blank" rel="noopener">settings/upgrade-config</a>'
|
'harv-upgrade-config': 'Configure image preloading and VM restore options for upgrades. See related fields in <a href="{url}" target="_blank" rel="noopener">settings/upgrade-config</a>'
|
||||||
'harv-vm-migration-network': 'Segregated network for VM migration traffic.'
|
'harv-vm-migration-network': 'Segregated network for VM migration traffic.'
|
||||||
@ -2160,6 +2252,36 @@ typeLabel:
|
|||||||
one { Virtual Private Cloud }
|
one { Virtual Private Cloud }
|
||||||
other { Virtual Private Clouds }
|
other { Virtual Private Clouds }
|
||||||
}
|
}
|
||||||
|
kubeovn.io.vlan: |-
|
||||||
|
{count, plural,
|
||||||
|
one { VLAN Network }
|
||||||
|
other { VLAN Networks }
|
||||||
|
}
|
||||||
|
kubeovn.io.providernetwork: |-
|
||||||
|
{count, plural,
|
||||||
|
one { Provider Network }
|
||||||
|
other { Provider Networks }
|
||||||
|
}
|
||||||
|
kubeovn.io.vpcnatgateway: |-
|
||||||
|
{count, plural,
|
||||||
|
one { NAT Gateway }
|
||||||
|
other { NAT Gateways }
|
||||||
|
}
|
||||||
|
kubeovn.io.iptablessnatrule: |-
|
||||||
|
{count, plural,
|
||||||
|
one { Source Rule }
|
||||||
|
other { Source Rules }
|
||||||
|
}
|
||||||
|
kubeovn.io.iptablesdnatrule: |-
|
||||||
|
{count, plural,
|
||||||
|
one { Destination Rule }
|
||||||
|
other { Destination Rules }
|
||||||
|
}
|
||||||
|
kubeovn.io.iptableseip: |-
|
||||||
|
{count, plural,
|
||||||
|
one { External IP }
|
||||||
|
other { External IPs }
|
||||||
|
}
|
||||||
networking.k8s.io.networkpolicy: |-
|
networking.k8s.io.networkpolicy: |-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
one { Network Policy }
|
one { Network Policy }
|
||||||
|
|||||||
@ -166,6 +166,7 @@ export default {
|
|||||||
:schema="schema"
|
:schema="schema"
|
||||||
:groupable="true"
|
:groupable="true"
|
||||||
:rows="filterRows"
|
:rows="filterRows"
|
||||||
|
:ignore-filter="true"
|
||||||
key-field="_key"
|
key-field="_key"
|
||||||
>
|
>
|
||||||
<template #cell:state="{row}">
|
<template #cell:state="{row}">
|
||||||
|
|||||||
@ -67,6 +67,13 @@ export default {
|
|||||||
NAMESPACE,
|
NAMESPACE,
|
||||||
CIDR_BLOCK,
|
CIDR_BLOCK,
|
||||||
PROTOCOL,
|
PROTOCOL,
|
||||||
|
{
|
||||||
|
name: 'vlan',
|
||||||
|
labelKey: 'harvester.subnet.vlan.label',
|
||||||
|
value: 'spec.vlan',
|
||||||
|
sort: 'spec.vlan',
|
||||||
|
formatter: 'HarvesterVlan',
|
||||||
|
},
|
||||||
PROVIDER,
|
PROVIDER,
|
||||||
AGE
|
AGE
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1742,7 +1742,7 @@ export default {
|
|||||||
|
|
||||||
const oldImageId = old[0]?.image;
|
const oldImageId = old[0]?.image;
|
||||||
|
|
||||||
if (this.isCreate && oldImageId === imageId && imageId) {
|
if (this.isCreate && oldImageId !== imageId && imageId && osType) {
|
||||||
this.osType = osType;
|
this.osType = osType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import { ClusterNotFoundError } from '@shell/utils/error';
|
|||||||
import { SETTING } from '@shell/config/settings';
|
import { SETTING } from '@shell/config/settings';
|
||||||
import { COUNT, NAMESPACE, MANAGEMENT } from '@shell/config/types';
|
import { COUNT, NAMESPACE, MANAGEMENT } from '@shell/config/types';
|
||||||
import { allHash } from '@shell/utils/promise';
|
import { allHash } from '@shell/utils/promise';
|
||||||
import { DEV } from '@shell/store/prefs';
|
import { DEV, NAMESPACE_FILTERS } from '@shell/store/prefs';
|
||||||
|
import { createNamespaceFilterKeyWithId } from '@shell/utils/namespace-filter';
|
||||||
import { HCI } from '../../types';
|
import { HCI } from '../../types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -121,8 +122,11 @@ export default {
|
|||||||
|
|
||||||
await dispatch('cleanNamespaces', null, { root: true });
|
await dispatch('cleanNamespaces', null, { root: true });
|
||||||
|
|
||||||
|
const namespaceFilterKey = createNamespaceFilterKeyWithId(id, 'harvester');
|
||||||
|
const savedFilters = rootGetters['prefs/get'](NAMESPACE_FILTERS)?.[namespaceFilterKey];
|
||||||
|
|
||||||
commit('updateNamespaces', {
|
commit('updateNamespaces', {
|
||||||
filters: [],
|
filters: savedFilters || [],
|
||||||
all: getters.filterNamespace(),
|
all: getters.filterNamespace(),
|
||||||
getters
|
getters
|
||||||
}, { root: true });
|
}, { root: true });
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
// To find the CRD name, you can run `kubectl api-resources` and look for the `NAME` column.
|
||||||
|
// The CRD name is usually in the format of `<plural>.<group>`, where `<plural>` is the plural form of the resource and `<group>` is the API group it belongs to.
|
||||||
|
// e.g
|
||||||
|
// 1. `virtualmachines.kubevirt.io` -> kubevirt.io.virtualmachine, the CRD name for the `VirtualMachine` resource in the `kubevirt.io` API group
|
||||||
|
// 2. `vpc-nat-gateways.kubeovn.io` -> kubeovn.io.vpcnatgateway, the CRD name for the `VpcNatGateway` resource in the `kubeovn.io` API group.
|
||||||
|
|
||||||
export const HCI = {
|
export const HCI = {
|
||||||
VM: 'kubevirt.io.virtualmachine',
|
VM: 'kubevirt.io.virtualmachine',
|
||||||
VMI: 'kubevirt.io.virtualmachineinstance',
|
VMI: 'kubevirt.io.virtualmachineinstance',
|
||||||
@ -20,6 +26,12 @@ export const HCI = {
|
|||||||
SUBNET: 'kubeovn.io.subnet',
|
SUBNET: 'kubeovn.io.subnet',
|
||||||
VPC: 'kubeovn.io.vpc',
|
VPC: 'kubeovn.io.vpc',
|
||||||
IP: 'kubeovn.io.ip',
|
IP: 'kubeovn.io.ip',
|
||||||
|
VLAN: 'kubeovn.io.vlan',
|
||||||
|
IPTABLES_EIP: 'kubeovn.io.iptableseip',
|
||||||
|
IPTABLES_SNAT_RULE: 'kubeovn.io.iptablessnatrule',
|
||||||
|
IPTABLES_DNAT_RULE: 'kubeovn.io.iptablesdnatrule',
|
||||||
|
PROVIDER_NETWORK: 'kubeovn.io.providernetwork',
|
||||||
|
VPC_NAT_GATEWAY: 'kubeovn.io.vpcnatgateway',
|
||||||
VM_IMAGE_DOWNLOADER: 'harvesterhci.io.virtualmachineimagedownloader',
|
VM_IMAGE_DOWNLOADER: 'harvesterhci.io.virtualmachineimagedownloader',
|
||||||
SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle',
|
SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle',
|
||||||
NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',
|
NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',
|
||||||
|
|||||||
24
yarn.lock
24
yarn.lock
@ -2846,10 +2846,10 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/node@*", "@types/node@25.3.3", "@types/node@25.9.2":
|
"@types/node@*", "@types/node@25.3.3", "@types/node@25.9.4":
|
||||||
version "25.9.2"
|
version "25.9.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.2.tgz#fc8958e757994b71fee516f9634bdb03d1b19e9f"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.4.tgz#18b63c47f88c1fbbed9d55ea2b66ffd494a47001"
|
||||||
integrity sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==
|
integrity sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types ">=7.24.0 <7.24.7"
|
undici-types ">=7.24.0 <7.24.7"
|
||||||
|
|
||||||
@ -10696,10 +10696,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.3:
|
|||||||
hash-base "^3.1.2"
|
hash-base "^3.1.2"
|
||||||
inherits "^2.0.4"
|
inherits "^2.0.4"
|
||||||
|
|
||||||
roarr@7.21.5:
|
roarr@7.21.6:
|
||||||
version "7.21.5"
|
version "7.21.6"
|
||||||
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.21.5.tgz#19270853b48515af1542c3d2a878bd1f276c2237"
|
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.21.6.tgz#91c7f0c4b83de49a76b801e73782086baf4a7c8d"
|
||||||
integrity sha512-nvelZ4llbfodVanR/gG17H8jpnqgyPX01c4ekQYfoghjEKvAXn7aPPToVG8ngyxf4qtvTC1O5AxQe5PysnF4xg==
|
integrity sha512-bE9gaDGzWu4BxkyxUEVHfnHruvF4gX9NAZBz86EVJoun2J18exVR/3Wwk0hhu8MWXtabkTVveNcOk1lzHaZbVw==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-printf "^1.6.9"
|
fast-printf "^1.6.9"
|
||||||
safe-stable-stringify "^2.4.3"
|
safe-stable-stringify "^2.4.3"
|
||||||
@ -10847,10 +10847,10 @@ semver-compare@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||||
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@7.8.2, semver@^5.5.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@~7.0.0:
|
"semver@2 || 3 || 4 || 5", semver@7.8.5, semver@^5.5.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@~7.0.0:
|
||||||
version "7.8.2"
|
version "7.8.5"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.2.tgz#194bd65723a28cf82542d2bf176b91c26b343be1"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69"
|
||||||
integrity sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==
|
integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==
|
||||||
|
|
||||||
send@0.17.1:
|
send@0.17.1:
|
||||||
version "0.17.1"
|
version "0.17.1"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user