diff --git a/pkg/harvester/config/harvester-cluster.js b/pkg/harvester/config/harvester-cluster.js index c97b2364..b09fa0f8 100644 --- a/pkg/harvester/config/harvester-cluster.js +++ b/pkg/harvester/config/harvester-cluster.js @@ -54,6 +54,14 @@ import { registerAddonSideNav } from '../utils/dynamic-nav'; const TEMPLATE = HCI.VM_VERSION; const MONITORING_GROUP = 'Monitoring & Logging::Monitoring'; 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'; @@ -582,14 +590,52 @@ export function init($plugin, store) { HCI.CLUSTER_NETWORK, HCI.NETWORK_ATTACHMENT, HCI.HOST_NETWORK_CONFIG, - HCI.VPC, - NETWORK_POLICY, HCI.LB, HCI.IP_POOL, ], '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( [ HCI.SCHEDULE_VM_BACKUP, @@ -601,7 +647,11 @@ export function init($plugin, store) { ); 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( [ @@ -680,7 +730,7 @@ export function init($plugin, store) { name: HCI.CLUSTER_NETWORK, ifHaveType: HCI.CLUSTER_NETWORK, namespaced: false, - weight: 189, + weight: 484, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: HCI.CLUSTER_NETWORK } @@ -702,7 +752,7 @@ export function init($plugin, store) { labelKey: 'harvester.network.label', name: HCI.NETWORK_ATTACHMENT, namespaced: true, - weight: 188, + weight: 485, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: HCI.NETWORK_ATTACHMENT } @@ -716,7 +766,7 @@ export function init($plugin, store) { labelKey: 'harvester.vpc.label', name: HCI.VPC, namespaced: true, - weight: 187, + weight: 195, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: HCI.VPC } @@ -725,13 +775,73 @@ export function init($plugin, store) { 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 }); virtualType({ labelKey: 'harvester.networkPolicy.label', name: NETWORK_POLICY, namespaced: true, - weight: 186, + weight: 194, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: NETWORK_POLICY } @@ -740,6 +850,53 @@ export function init($plugin, store) { 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, { isCreatable: false, location: { @@ -1094,7 +1251,7 @@ export function init($plugin, store) { labelKey: 'harvester.loadBalancer.label', name: HCI.LB, namespaced: true, - weight: 185, + weight: 483, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: HCI.LB } @@ -1133,7 +1290,7 @@ export function init($plugin, store) { labelKey: 'harvester.ipPool.label', name: HCI.IP_POOL, namespaced: false, - weight: 184, + weight: 482, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: HCI.IP_POOL } @@ -1154,7 +1311,7 @@ export function init($plugin, store) { labelKey: 'harvester.hostNetworkConfig.label', name: HCI.HOST_NETWORK_CONFIG, namespaced: false, - weight: 183, + weight: 481, route: { name: `${ PRODUCT_NAME }-c-cluster-resource`, params: { resource: HCI.HOST_NETWORK_CONFIG } diff --git a/pkg/harvester/config/labels-annotations.js b/pkg/harvester/config/labels-annotations.js index df87e361..3d24ea15 100644 --- a/pkg/harvester/config/labels-annotations.js +++ b/pkg/harvester/config/labels-annotations.js @@ -82,4 +82,5 @@ export const HCI = { MAC_ADDRESS: 'harvesterhci.io/mac-address', NODE_UPGRADE_PAUSE_MAP: 'harvesterhci.io/node-upgrade-pause-map', CDI_POPULATOR_KIND: 'cdi.kubevirt.io/storage.populator.kind', + CNI_NETWORKS: 'k8s.v1.cni.cncf.io/networks', }; diff --git a/pkg/harvester/edit/kubeovn.io.iptablesdnatrule.vue b/pkg/harvester/edit/kubeovn.io.iptablesdnatrule.vue new file mode 100644 index 00000000..160ea986 --- /dev/null +++ b/pkg/harvester/edit/kubeovn.io.iptablesdnatrule.vue @@ -0,0 +1,190 @@ + + + diff --git a/pkg/harvester/edit/kubeovn.io.iptableseip.vue b/pkg/harvester/edit/kubeovn.io.iptableseip.vue new file mode 100644 index 00000000..0e3a2e8a --- /dev/null +++ b/pkg/harvester/edit/kubeovn.io.iptableseip.vue @@ -0,0 +1,168 @@ + + + diff --git a/pkg/harvester/edit/kubeovn.io.iptablessnatrule.vue b/pkg/harvester/edit/kubeovn.io.iptablessnatrule.vue new file mode 100644 index 00000000..d47b458a --- /dev/null +++ b/pkg/harvester/edit/kubeovn.io.iptablessnatrule.vue @@ -0,0 +1,140 @@ + + + diff --git a/pkg/harvester/edit/kubeovn.io.providernetwork.vue b/pkg/harvester/edit/kubeovn.io.providernetwork.vue new file mode 100644 index 00000000..1a9d629a --- /dev/null +++ b/pkg/harvester/edit/kubeovn.io.providernetwork.vue @@ -0,0 +1,384 @@ + + + + + diff --git a/pkg/harvester/edit/kubeovn.io.subnet/index.vue b/pkg/harvester/edit/kubeovn.io.subnet/index.vue index 3ad6246c..fcd99300 100644 --- a/pkg/harvester/edit/kubeovn.io.subnet/index.vue +++ b/pkg/harvester/edit/kubeovn.io.subnet/index.vue @@ -64,8 +64,9 @@ export default { const inStore = this.$store.getters['currentProduct'].inStore; const hash = { - vpc: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VPC }), - nad: this.$store.dispatch(`${ inStore }/findAll`, { type: NETWORK_ATTACHMENT }), + vpc: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VPC }), + nad: this.$store.dispatch(`${ inStore }/findAll`, { type: NETWORK_ATTACHMENT }), + vlans: this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VLAN }), }; await allHash(hash); @@ -135,6 +136,16 @@ export default { natOutgoingDisabled() { // 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); + }, + + 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" /> +
+ +
diff --git a/pkg/harvester/edit/kubeovn.io.vlan.vue b/pkg/harvester/edit/kubeovn.io.vlan.vue new file mode 100644 index 00000000..9e891f2f --- /dev/null +++ b/pkg/harvester/edit/kubeovn.io.vlan.vue @@ -0,0 +1,146 @@ + + + diff --git a/pkg/harvester/edit/kubeovn.io.vpcnatgateway.vue b/pkg/harvester/edit/kubeovn.io.vpcnatgateway.vue new file mode 100644 index 00000000..9326b060 --- /dev/null +++ b/pkg/harvester/edit/kubeovn.io.vpcnatgateway.vue @@ -0,0 +1,246 @@ + + + diff --git a/pkg/harvester/formatters/HarvesterVlan.vue b/pkg/harvester/formatters/HarvesterVlan.vue new file mode 100644 index 00000000..f390f9f4 --- /dev/null +++ b/pkg/harvester/formatters/HarvesterVlan.vue @@ -0,0 +1,24 @@ + + + diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index 828457f4..98f5a358 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -348,6 +348,9 @@ harvester: vmImportSourceOClusterStatus: Cluster Status vmImportSourceOVAUrl: URL vmImportSourceOVAStatus: Status + v4ip: V4 IP + v6ip: V6 IP + eipName: EIP Name tab: volume: Volumes network: Networks @@ -1155,6 +1158,9 @@ harvester: gateway: label: Gateway IP placeholder: e.g. 172.20.0.1 + vlan: + label: VLAN + placeholder: Select a VLAN dhcp: label: Dynamic Host Configuration Protocol (DHCP) v4Options: DHCPV4Options @@ -1240,8 +1246,90 @@ harvester: remoteVpc: label: Remote VPC infoBanner: The static route destination CIDR must cover all subnets CIDR from remote VPC Peer. Read VPC Peering Configuration Examples 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: - label: Network Policies + label: Policies banner: The network policies must be used for VMs attached to overlay networks. Please read the harvester document how the network policy works. network: label: Virtual Machine Networks @@ -2164,6 +2252,36 @@ typeLabel: one { Virtual Private Cloud } 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: |- {count, plural, one { Network Policy } diff --git a/pkg/harvester/list/kubeovn.io.vpc.vue b/pkg/harvester/list/kubeovn.io.vpc.vue index 363f4fd0..254200c4 100644 --- a/pkg/harvester/list/kubeovn.io.vpc.vue +++ b/pkg/harvester/list/kubeovn.io.vpc.vue @@ -67,6 +67,13 @@ export default { NAMESPACE, CIDR_BLOCK, PROTOCOL, + { + name: 'vlan', + labelKey: 'harvester.subnet.vlan.label', + value: 'spec.vlan', + sort: 'spec.vlan', + formatter: 'HarvesterVlan', + }, PROVIDER, AGE ]; diff --git a/pkg/harvester/types.ts b/pkg/harvester/types.ts index ddb929c3..caaa8adf 100644 --- a/pkg/harvester/types.ts +++ b/pkg/harvester/types.ts @@ -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 `.`, where `` is the plural form of the resource and `` 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 = { VM: 'kubevirt.io.virtualmachine', VMI: 'kubevirt.io.virtualmachineinstance', @@ -20,6 +26,12 @@ export const HCI = { SUBNET: 'kubeovn.io.subnet', VPC: 'kubeovn.io.vpc', 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', SUPPORT_BUNDLE: 'harvesterhci.io.supportbundle', NETWORK_ATTACHMENT: 'harvesterhci.io.networkattachmentdefinition',