From 9a20ad5edd74ce2f90a6f08ff9eecfc30ea95b9d Mon Sep 17 00:00:00 2001 From: Marcelo Fukumoto Date: Fri, 17 Apr 2026 20:15:19 +0200 Subject: [PATCH] feat: forklift poc first commit Signed-off-by: Marcelo Fukumoto --- forklift | 264 +++++++++++++ pkg/harvester/config/harvester-cluster.js | 171 +++++++++ pkg/harvester/config/harvester-map.js | 3 + pkg/harvester/config/table-headers.js | 67 ++++ .../edit/forklift.konveyor.io.migration.vue | 119 ++++++ .../edit/forklift.konveyor.io.networkmap.vue | 199 ++++++++++ .../edit/forklift.konveyor.io.plan.vue | 303 +++++++++++++++ .../edit/forklift.konveyor.io.provider.vue | 346 ++++++++++++++++++ .../edit/forklift.konveyor.io.storagemap.vue | 208 +++++++++++ pkg/harvester/l10n/en-us.yaml | 88 +++++ .../list/forklift.konveyor.io.migration.vue | 60 +++ .../list/forklift.konveyor.io.networkmap.vue | 60 +++ .../list/forklift.konveyor.io.plan.vue | 60 +++ .../list/forklift.konveyor.io.provider.vue | 60 +++ .../list/forklift.konveyor.io.storagemap.vue | 60 +++ pkg/harvester/mixins/forklift-provider.js | 47 +++ pkg/harvester/types.ts | 147 ++++---- 17 files changed, 2191 insertions(+), 71 deletions(-) create mode 100644 forklift create mode 100644 pkg/harvester/edit/forklift.konveyor.io.migration.vue create mode 100644 pkg/harvester/edit/forklift.konveyor.io.networkmap.vue create mode 100644 pkg/harvester/edit/forklift.konveyor.io.plan.vue create mode 100644 pkg/harvester/edit/forklift.konveyor.io.provider.vue create mode 100644 pkg/harvester/edit/forklift.konveyor.io.storagemap.vue create mode 100644 pkg/harvester/list/forklift.konveyor.io.migration.vue create mode 100644 pkg/harvester/list/forklift.konveyor.io.networkmap.vue create mode 100644 pkg/harvester/list/forklift.konveyor.io.plan.vue create mode 100644 pkg/harvester/list/forklift.konveyor.io.provider.vue create mode 100644 pkg/harvester/list/forklift.konveyor.io.storagemap.vue create mode 100644 pkg/harvester/mixins/forklift-provider.js diff --git a/forklift b/forklift new file mode 100644 index 00000000..f7ca66d1 --- /dev/null +++ b/forklift @@ -0,0 +1,264 @@ +# Forklift Addon + +## Summary + +This enhancement details the changes needed to include [forklift](https://github.com/kubev2v/forklift) as a Harvester addon. + +The addon can be used as an alternate option for performing vm migrations from vmware. + +### Related Issues + +https://github.com/harvester/harvester/issues/9588 + +## Motivation +forklift is a tool similar to vm-import-controller which allows users to migrate VM based workloads from platforms such as vmware and openstack to Harvester. forklift has the added advantage of support [vsphere-xcopy](https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/7-0/vsphere-storage/storage-hardware-acceleration-in-vsphere/vsphere-hardware-acceleration-on-block-storage/managing-hardware-acceleration-filter-and-plug-ins/configure-xcopy-parameters.html) based plugins. When used in conjuction with supported storage arrays that are being shared across both vmware and Harvester installations, the plugin offloads the disk clone operation to the storage array. This allows the data to be populated in the kubernetes pvc directly from the vsphere datastore. This speeds up the data migration as the compute and network layer for both vsphere and Harvester components are skipped. + +### Goals + +- Addon to allow users to enable/disable the forklift addon on a Harvester cluster +- Allow migration of VM's from vsphere to Harvester using forklift + +### Non-goals [optional] + +- QA of individual vsphere-xcopy plugins + +## Proposal + +This is where we get down to the nitty gritty of what the proposal actually is. + +### User Stories + +#### Story 1: Migration VirtualMachines from vmware to Harvester +As a user I wish to migration my existing VM workloads from vmware to Harvester. + +### User Experience In Detail + +A user can trigger a migration of VM from vmware to Harvester by following the listed steps: + +##### create a provider +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: vmware-secret + namespace: default + labels: + createdForProviderType: vsphere + createdForResourceType: providers +type: Opaque +stringData: + user: "username" + password: "password" + insecureSkipVerify: "true" + url: "https://vcenter/sdk" +--- +apiVersion: forklift.konveyor.io/v1beta1 +kind: Provider +metadata: + name: vmware-source + namespace: default +spec: + type: vsphere + url: "https://mako-vcenter.devlab.pgu1.suse.com/sdk" + secret: + name: vmware-secret + namespace: default +--- +apiVersion: forklift.konveyor.io/v1beta1 +kind: Provider +metadata: + name: local + namespace: default +spec: + type: openshift + secret: {} +``` + +##### create a network map defining mapping of vmware networks to Harvester vm networks +```yaml +apiVersion: forklift.konveyor.io/v1beta1 +kind: NetworkMap +metadata: + name: vsphere-network-map + namespace: default +spec: + map: + - destination: + type: pod + source: + id: dvportgroup-13025 + provider: + destination: + apiVersion: forklift.konveyor.io/v1beta1 + kind: Provider + name: local + namespace: default + source: + apiVersion: forklift.konveyor.io/v1beta1 + kind: Provider + name: vmware-source + namespace: default +``` + +##### create a storage map defining mapping of vmware storage to Harvester storage class +```yaml +apiVersion: forklift.konveyor.io/v1beta1 +kind: StorageMap +metadata: + name: vsphere-storage-map + namespace: default +spec: + map: + - destination: + storageClass: longhorn-v2 + source: + id: datastore-2040 + provider: + destination: + apiVersion: forklift.konveyor.io/v1beta1 + kind: Provider + name: local + namespace: default + source: + apiVersion: forklift.konveyor.io/v1beta1 + kind: Provider + name: vmware-source + namespace: default +``` + +##### create a migration plan which details steps be carried out for a list of VMs in question +```yaml +apiVersion: forklift.konveyor.io/v1beta1 +kind: Plan +metadata: + annotations: + populatorLabels: 'True' + name: vmware-gm-ubuntu-test-migration + namespace: default +spec: + map: + network: + apiVersion: forklift.konveyor.io/v1beta1 + kind: NetworkMap + name: vsphere-network-map + namespace: default + storage: + apiVersion: forklift.konveyor.io/v1beta1 + kind: StorageMap + name: vsphere-storage-map + namespace: default + migrateSharedDisks: true + provider: + destination: + apiVersion: forklift.konveyor.io/v1beta1 + kind: Provider + name: local + namespace: default + source: + apiVersion: forklift.konveyor.io/v1beta1 + kind: Provider + name: vmware-source + namespace: default + targetNamespace: default + vms: + - id: vm-13007 + name: gm-ubuntu-test +``` + +##### trigger the vm migration +```yaml +kind: Migration +metadata: + name: vmware-gm-ubuntu-test-migration + namespace: default +spec: + plan: + name: vmware-gm-ubuntu-test-migration + namespace: default +``` + + +Once the migration is successful the VM's listed in the plan will have been migrated to the Harvester + +### API changes +There are no API changes in core Harvester itself. + +Enabling the Addon will create the following CRDs on the Harvester cluster: +* forkliftcontrollers.forklift.konveyor.io +* hooks.forklift.konveyor.io +* hosts.forklift.konveyor.io +* migrations.forklift.konveyor.io +* networkmaps.forklift.konveyor.io +* openstackvolumepopulators.forklift.konveyor.io +* ovirtvolumepopulators.forklift.konveyor.io +* plans.forklift.konveyor.io +* providers.forklift.konveyor.io +* storagemaps.forklift.konveyor.io +* vspherexcopyvolumepopulators.forklift.konveyor.io + +## Design + +### Implementation Overview +Implementation requires two main tasks: + +* repackaging of forklift components using SLE BCL base images +* packaging of forklift-operator as a helm chart. Upstream this is managed by [OLM](https://olm.operatorframework.io/) + +We are going to skip the introduction of OLM to Harvester as we have our own AppCo and Helm based component delivery mechanism. OLM ties will with `operator-sdk` which we do not leverage, as core Harvester capability leverages the rancher wrangler framework. + +The [repo](https://github.com/ibrokethecloud/forklift-packaging) already contains the tooling to repackage and publish all forklift components using SLE BCL or Opensuse images. + + +The forklift addon will be leveraging the forklift-operator, which will be packaged as a vanilla helm chart. This will allow users to enable / disable the addon as per their needs. + +Once the addon is enabled, the users will need to create a `ForkliftController` CR to trigger the installation of forklift components + +```yaml +apiVersion: forklift.konveyor.io/v1beta1 +kind: ForkliftController +metadata: + name: forklift-controller + namespace: forklift +spec: + feature_ui_plugin: "false" +``` + +The creation of the `ForkliftController` CR will trigger the following deployments + +``` +NAME READY UP-TO-DATE AVAILABLE AGE +forklift-api 1/1 1 1 10d +forklift-controller 1/1 1 1 10d +forklift-operator-forklift-operator-ansible 1/1 1 1 10d +forklift-validation 1/1 1 1 10d +forklift-volume-populator-controller 1/1 1 1 10d +``` + +#### harvester-webhook changes +Forklift controller runs the migration pod with the default seccomp profile. This stops the actual virt-v2v converstion from completing successfully. The Harvester webhook needs a change to patch the migration pod to provide it priveleged permissions to allow successful conversion. + +The harvester-webhook will also patch the migration pod's `ImagePullPolicy` from `Always` to `IfNotPresent` to ensure simpler operation in air-gapped environments. + + +### Test plan + +* Enable forklift addon +* Create a `ForkliftController` CR +* Once forklift components have been installed, trigger a VM migration from vmware to forklift by using the sample available earlier in the HEP +* The VM should migrate successfully + +### Upgrade strategy + +No specific upgrade enhancement is needed. The addon will be upgrade as part of the Harvester upgrade process. + +## Note [optional] + +* forklift-operator needs [cert-manager ](https://cert-manager.io/). Users will need to manually install cert-manager from `AppCo` or upstream to ensure the forklift components can be initialised successfully by the operator + +* forklift leverages [vddk images](https://docs.redhat.com/en/documentation/migration_toolkit_for_virtualization/2.0/html/installing_and_using_the_migration_toolkit_for_virtualization/installing-mtv_mtv#adding-vddk-to-mtv_mtv) which is a proprietary package. We cannot packaging this in our distribution, and users will need to manually build and host this image in their environment to leverage vddk based migration capabilities + +* QA has no way to validate vmware-xcopy based plugin capabilities. We may need to leverage vendor relationships to QA and validate the same + +* Forklift also introduces [OPA](https://v0-12-2--opa-docs.netlify.app/kubernetes-admission-control/) to run its validation layer. + +* A mutating webhook is needed in [harvester-webhook](https://github.com/harvester/harvester/pull/9935) to mutate virt-v2v pods being created by forklift to have privileged escalation. This is to ensure passt interface can initialise successfully inside the pod, to allow for subsequent vm conversion. \ No newline at end of file diff --git a/pkg/harvester/config/harvester-cluster.js b/pkg/harvester/config/harvester-cluster.js index b09fa0f8..0a2bc519 100644 --- a/pkg/harvester/config/harvester-cluster.js +++ b/pkg/harvester/config/harvester-cluster.js @@ -47,6 +47,13 @@ import { VM_IMPORT_SOURCE_O_STATUS, VM_IMPORT_SOURCE_OVA_URL, VM_IMPORT_SOURCE_OVA_STATUS, + FORKLIFT_PROVIDER_TYPE, + FORKLIFT_PROVIDER_URL, + FORKLIFT_MAP_SOURCE_PROVIDER, + FORKLIFT_MAP_DEST_PROVIDER, + FORKLIFT_PLAN_TARGET_NS, + FORKLIFT_PLAN_VM_COUNT, + FORKLIFT_MIGRATION_PLAN, } from './table-headers'; import { ADD_ONS } from './harvester-map'; import { registerAddonSideNav } from '../utils/dynamic-nav'; @@ -1319,4 +1326,168 @@ export function init($plugin, store) { exact: false, ifHaveType: HCI.HOST_NETWORK_CONFIG, }); + // =========================================================================== + // Forklift Addon UI Flow + // =========================================================================== + weightGroup('forklift', 0, false); + + // Provider + headers(HCI.FORKLIFT_PROVIDER, [ + STATE, + NAME_COL, + NAMESPACE_COL, + FORKLIFT_PROVIDER_TYPE, + FORKLIFT_PROVIDER_URL, + AGE + ]); + configureType(HCI.FORKLIFT_PROVIDER, { + resource: HCI.FORKLIFT_PROVIDER, + resourceDetail: HCI.FORKLIFT_PROVIDER, + resourceEdit: HCI.FORKLIFT_PROVIDER, + location: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_PROVIDER } + } + }); + virtualType({ + name: HCI.FORKLIFT_PROVIDER, + labelKey: 'harvester.addons.forklift.labels.provider', + group: 'forklift', + namespaced: true, + route: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_PROVIDER } + } + }); + + // NetworkMap + headers(HCI.FORKLIFT_NETWORK_MAP, [ + STATE, + NAME_COL, + NAMESPACE_COL, + FORKLIFT_MAP_SOURCE_PROVIDER, + FORKLIFT_MAP_DEST_PROVIDER, + AGE + ]); + configureType(HCI.FORKLIFT_NETWORK_MAP, { + resource: HCI.FORKLIFT_NETWORK_MAP, + resourceDetail: HCI.FORKLIFT_NETWORK_MAP, + resourceEdit: HCI.FORKLIFT_NETWORK_MAP, + location: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_NETWORK_MAP } + } + }); + virtualType({ + name: HCI.FORKLIFT_NETWORK_MAP, + labelKey: 'harvester.addons.forklift.labels.networkMap', + group: 'forklift', + namespaced: true, + route: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_NETWORK_MAP } + } + }); + + // StorageMap + headers(HCI.FORKLIFT_STORAGE_MAP, [ + STATE, + NAME_COL, + NAMESPACE_COL, + FORKLIFT_MAP_SOURCE_PROVIDER, + FORKLIFT_MAP_DEST_PROVIDER, + AGE + ]); + configureType(HCI.FORKLIFT_STORAGE_MAP, { + resource: HCI.FORKLIFT_STORAGE_MAP, + resourceDetail: HCI.FORKLIFT_STORAGE_MAP, + resourceEdit: HCI.FORKLIFT_STORAGE_MAP, + location: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_STORAGE_MAP } + } + }); + virtualType({ + name: HCI.FORKLIFT_STORAGE_MAP, + labelKey: 'harvester.addons.forklift.labels.storageMap', + group: 'forklift', + namespaced: true, + route: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_STORAGE_MAP } + } + }); + + // Plan + headers(HCI.FORKLIFT_PLAN, [ + STATE, + NAME_COL, + NAMESPACE_COL, + FORKLIFT_MAP_SOURCE_PROVIDER, + FORKLIFT_PLAN_TARGET_NS, + FORKLIFT_PLAN_VM_COUNT, + AGE + ]); + configureType(HCI.FORKLIFT_PLAN, { + resource: HCI.FORKLIFT_PLAN, + resourceDetail: HCI.FORKLIFT_PLAN, + resourceEdit: HCI.FORKLIFT_PLAN, + location: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_PLAN } + } + }); + virtualType({ + name: HCI.FORKLIFT_PLAN, + labelKey: 'harvester.addons.forklift.labels.plan', + group: 'forklift', + namespaced: true, + route: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_PLAN } + } + }); + + // Migration + headers(HCI.FORKLIFT_MIGRATION, [ + STATE, + NAME_COL, + NAMESPACE_COL, + FORKLIFT_MIGRATION_PLAN, + AGE + ]); + configureType(HCI.FORKLIFT_MIGRATION, { + resource: HCI.FORKLIFT_MIGRATION, + resourceDetail: HCI.FORKLIFT_MIGRATION, + resourceEdit: HCI.FORKLIFT_MIGRATION, + location: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_MIGRATION } + } + }); + virtualType({ + name: HCI.FORKLIFT_MIGRATION, + labelKey: 'harvester.addons.forklift.labels.migration', + group: 'forklift', + namespaced: true, + route: { + name: `${ PRODUCT_NAME }-c-cluster-resource`, + params: { resource: HCI.FORKLIFT_MIGRATION } + } + }); + + // Enable SideNav based on Forklift Addon Status + registerAddonSideNav(store, PRODUCT_NAME, { + addonName: ADD_ONS.FORKLIFT_OPERATOR, + resourceType: HCI.ADD_ONS, + navGroup: 'forklift', + types: [ + HCI.FORKLIFT_PROVIDER, + HCI.FORKLIFT_NETWORK_MAP, + HCI.FORKLIFT_STORAGE_MAP, + HCI.FORKLIFT_PLAN, + HCI.FORKLIFT_MIGRATION, + ] + }); + // =========================================================================== } diff --git a/pkg/harvester/config/harvester-map.js b/pkg/harvester/config/harvester-map.js index 95323b8c..acbe2fb0 100644 --- a/pkg/harvester/config/harvester-map.js +++ b/pkg/harvester/config/harvester-map.js @@ -79,6 +79,7 @@ export const ADD_ONS = { VM_IMPORT_CONTROLLER: 'vm-import-controller', LVM_DRIVER: 'lvm.driver.harvesterhci.io', KUBEOVN_OPERATOR: 'kubeovn-operator', + FORKLIFT_OPERATOR: 'forklift-operator', }; export const CSI_SECRETS = { @@ -92,6 +93,8 @@ export const CSI_SECRETS = { CSI_NODE_EXPAND_SECRET_NAMESPACE: 'csi.storage.k8s.io/node-expand-secret-namespace' }; +export const FORKLIFT_API_VERSION = 'forklift.konveyor.io/v1beta1'; + // Some harvester CRD type is not equal to model file name, define the mapping here export const HARVESTER_CRD_MAP = { node: HCI.HOST, diff --git a/pkg/harvester/config/table-headers.js b/pkg/harvester/config/table-headers.js index 92c1c3de..69d14b0b 100644 --- a/pkg/harvester/config/table-headers.js +++ b/pkg/harvester/config/table-headers.js @@ -230,3 +230,70 @@ export const VM_IMPORT_SOURCE_OVA_STATUS = { sort: 'status.status', align: 'left', }; + +// ======================================== +// Forklift table headers +// ======================================== + +// Provider type column in forklift.konveyor.io.provider list page +export const FORKLIFT_PROVIDER_TYPE = { + name: 'providerType', + labelKey: 'harvester.tableHeaders.forkliftProviderType', + value: 'spec.type', + sort: 'spec.type', + align: 'left', +}; + +// Provider URL column in forklift.konveyor.io.provider list page +export const FORKLIFT_PROVIDER_URL = { + name: 'providerUrl', + labelKey: 'harvester.tableHeaders.forkliftProviderUrl', + value: 'spec.url', + sort: 'spec.url', + align: 'left', +}; + +// Source provider column in forklift network/storage map list page +export const FORKLIFT_MAP_SOURCE_PROVIDER = { + name: 'sourceProvider', + labelKey: 'harvester.tableHeaders.forkliftMapSourceProvider', + value: 'spec.provider.source.name', + sort: 'spec.provider.source.name', + align: 'left', +}; + +// Destination provider column in forklift network/storage map list page +export const FORKLIFT_MAP_DEST_PROVIDER = { + name: 'destProvider', + labelKey: 'harvester.tableHeaders.forkliftMapDestProvider', + value: 'spec.provider.destination.name', + sort: 'spec.provider.destination.name', + align: 'left', +}; + +// Target namespace column in forklift.konveyor.io.plan list page +export const FORKLIFT_PLAN_TARGET_NS = { + name: 'targetNamespace', + labelKey: 'harvester.tableHeaders.forkliftPlanTargetNs', + value: 'spec.targetNamespace', + sort: 'spec.targetNamespace', + align: 'left', +}; + +// VM count column in forklift.konveyor.io.plan list page +export const FORKLIFT_PLAN_VM_COUNT = { + name: 'vmCount', + labelKey: 'harvester.tableHeaders.forkliftPlanVmCount', + value: 'spec.vms.length', + sort: 'spec.vms.length', + align: 'center', +}; + +// Plan reference column in forklift.konveyor.io.migration list page +export const FORKLIFT_MIGRATION_PLAN = { + name: 'plan', + labelKey: 'harvester.tableHeaders.forkliftMigrationPlan', + value: 'spec.plan.name', + sort: 'spec.plan.name', + align: 'left', +}; diff --git a/pkg/harvester/edit/forklift.konveyor.io.migration.vue b/pkg/harvester/edit/forklift.konveyor.io.migration.vue new file mode 100644 index 00000000..09a4a557 --- /dev/null +++ b/pkg/harvester/edit/forklift.konveyor.io.migration.vue @@ -0,0 +1,119 @@ + + + diff --git a/pkg/harvester/edit/forklift.konveyor.io.networkmap.vue b/pkg/harvester/edit/forklift.konveyor.io.networkmap.vue new file mode 100644 index 00000000..a4ed4d29 --- /dev/null +++ b/pkg/harvester/edit/forklift.konveyor.io.networkmap.vue @@ -0,0 +1,199 @@ + + + diff --git a/pkg/harvester/edit/forklift.konveyor.io.plan.vue b/pkg/harvester/edit/forklift.konveyor.io.plan.vue new file mode 100644 index 00000000..fcc63c55 --- /dev/null +++ b/pkg/harvester/edit/forklift.konveyor.io.plan.vue @@ -0,0 +1,303 @@ + + + diff --git a/pkg/harvester/edit/forklift.konveyor.io.provider.vue b/pkg/harvester/edit/forklift.konveyor.io.provider.vue new file mode 100644 index 00000000..0021f622 --- /dev/null +++ b/pkg/harvester/edit/forklift.konveyor.io.provider.vue @@ -0,0 +1,346 @@ + + + diff --git a/pkg/harvester/edit/forklift.konveyor.io.storagemap.vue b/pkg/harvester/edit/forklift.konveyor.io.storagemap.vue new file mode 100644 index 00000000..ee3fc6cd --- /dev/null +++ b/pkg/harvester/edit/forklift.konveyor.io.storagemap.vue @@ -0,0 +1,208 @@ + + + diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index a3cd87f5..daddeca4 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -22,6 +22,7 @@ nav: Logging: Logging 'Monitoring and Logging': Monitoring and Logging vmimport: Virtual Machine Imports + forklift: Forklift Migration resourceTable: groupBy: @@ -352,6 +353,13 @@ harvester: v4ip: V4 IP v6ip: V6 IP eipName: EIP Name + forkliftProviderType: Type + forkliftProviderUrl: URL + forkliftMapSourceProvider: Source Provider + forkliftMapDestProvider: Destination Provider + forkliftPlanTargetNs: Target Namespace + forkliftPlanVmCount: VMs + forkliftMigrationPlan: Plan tab: volume: Volumes network: Networks @@ -1799,6 +1807,61 @@ harvester: 'harvester-csi-driver-lvm': harvester-csi-driver-lvm is an add-on allowing users to create PVC through the LVM with local devices. 'descheduler': 'The virtual machine auto balance optimizes workload scheduling by evicting pods that are not optimally placed according to administrator-defined policies.' + forklift: + labels: + provider: Providers + networkMap: Network Maps + storageMap: Storage Maps + plan: Migration Plans + migration: Migrations + titles: + basic: Basic + auth: Authentication + providers: Providers + networkMappings: Network Mappings + storageMappings: Storage Mappings + mappings: Mappings + vms: Virtual Machines + fields: + providerType: Provider Type + providerTypeVsphere: vSphere + providerTypeOpenstack: OpenStack + providerTypeOpenshift: OpenShift / Harvester (Local) + url: URL + username: Username + password: Password + caCert: CA Certificate (PEM) + insecureSkipVerify: Skip TLS Certificate Verification + selectSecret: Select Secret + createSecret: Create New Credentials + useSecret: Use Existing Secret + sourceProvider: Source Provider + destProvider: Destination Provider + sourceNetworkId: Source Network ID + destNetworkType: Destination Network Type + destNetworkPod: Pod Network + destNetworkMultus: Multus + sourceDatastoreId: Source Datastore ID + destStorageClass: Destination Storage Class + networkMap: Network Map + storageMap: Storage Map + targetNamespace: Target Namespace + migrateSharedDisks: Migrate Shared Disks + vmId: VM ID + vmName: VM Name + plan: Migration Plan + placeholders: + url: "e.g. https://vcenter/sdk" + caCert: "-----BEGIN CERTIFICATE----- ..." + sourceNetworkId: "e.g. dvportgroup-13025" + sourceDatastoreId: "e.g. datastore-2040" + vmId: "e.g. vm-13007" + vmName: "e.g. my-vm" + actions: + addMapping: Add Mapping + addVm: Add VM + remove: Remove + vmImport: titles: basic: Basic @@ -2434,3 +2497,28 @@ typeLabel: one { Virtual Machine Import } other { Virtual Machine Imports } } + forklift.konveyor.io.provider: |- + {count, plural, + one { Provider } + other { Providers } + } + forklift.konveyor.io.networkmap: |- + {count, plural, + one { Network Map } + other { Network Maps } + } + forklift.konveyor.io.storagemap: |- + {count, plural, + one { Storage Map } + other { Storage Maps } + } + forklift.konveyor.io.plan: |- + {count, plural, + one { Migration Plan } + other { Migration Plans } + } + forklift.konveyor.io.migration: |- + {count, plural, + one { Migration } + other { Migrations } + } diff --git a/pkg/harvester/list/forklift.konveyor.io.migration.vue b/pkg/harvester/list/forklift.konveyor.io.migration.vue new file mode 100644 index 00000000..9b6902a7 --- /dev/null +++ b/pkg/harvester/list/forklift.konveyor.io.migration.vue @@ -0,0 +1,60 @@ + + + diff --git a/pkg/harvester/list/forklift.konveyor.io.networkmap.vue b/pkg/harvester/list/forklift.konveyor.io.networkmap.vue new file mode 100644 index 00000000..10347ff2 --- /dev/null +++ b/pkg/harvester/list/forklift.konveyor.io.networkmap.vue @@ -0,0 +1,60 @@ + + + diff --git a/pkg/harvester/list/forklift.konveyor.io.plan.vue b/pkg/harvester/list/forklift.konveyor.io.plan.vue new file mode 100644 index 00000000..84d83684 --- /dev/null +++ b/pkg/harvester/list/forklift.konveyor.io.plan.vue @@ -0,0 +1,60 @@ + + + diff --git a/pkg/harvester/list/forklift.konveyor.io.provider.vue b/pkg/harvester/list/forklift.konveyor.io.provider.vue new file mode 100644 index 00000000..d8b5e126 --- /dev/null +++ b/pkg/harvester/list/forklift.konveyor.io.provider.vue @@ -0,0 +1,60 @@ + + + diff --git a/pkg/harvester/list/forklift.konveyor.io.storagemap.vue b/pkg/harvester/list/forklift.konveyor.io.storagemap.vue new file mode 100644 index 00000000..8ddaf2aa --- /dev/null +++ b/pkg/harvester/list/forklift.konveyor.io.storagemap.vue @@ -0,0 +1,60 @@ + + + diff --git a/pkg/harvester/mixins/forklift-provider.js b/pkg/harvester/mixins/forklift-provider.js new file mode 100644 index 00000000..1414d488 --- /dev/null +++ b/pkg/harvester/mixins/forklift-provider.js @@ -0,0 +1,47 @@ +import { FORKLIFT_API_VERSION } from '../config/harvester-map'; + +/** + * Shared mixin for Forklift edit pages that need to select + * source and destination providers from available Provider CRs. + * + * Components using this mixin must load providers in their own fetch(): + * this.allProviders = await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.FORKLIFT_PROVIDER }); + */ +export default { + data() { + return { allProviders: [] }; + }, + + computed: { + providerOptions() { + return this.allProviders.map((p) => ({ + label: `${ p.metadata.name } (${ p.spec?.type || 'unknown' })`, + value: p.metadata.name, + })); + }, + }, + + methods: { + /** + * Build a Forklift object reference for a Provider. + */ + buildProviderRef(name) { + const provider = this.allProviders.find((p) => p.metadata.name === name); + + return { + apiVersion: FORKLIFT_API_VERSION, + kind: 'Provider', + name, + namespace: provider?.metadata?.namespace || this.value.metadata.namespace || 'default', + }; + }, + + updateSourceProvider(val) { + this.value.spec.provider.source = this.buildProviderRef(val); + }, + + updateDestProvider(val) { + this.value.spec.provider.destination = this.buildProviderRef(val); + }, + }, +}; diff --git a/pkg/harvester/types.ts b/pkg/harvester/types.ts index caaa8adf..5193beae 100644 --- a/pkg/harvester/types.ts +++ b/pkg/harvester/types.ts @@ -5,77 +5,82 @@ // 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', - VMIM: 'kubevirt.io.virtualmachineinstancemigration', - VM_TEMPLATE: 'harvesterhci.io.virtualmachinetemplate', - VM_VERSION: 'harvesterhci.io.virtualmachinetemplateversion', - IMAGE: 'harvesterhci.io.virtualmachineimage', - SSH: 'harvesterhci.io.keypair', - VOLUME: 'harvesterhci.io.volume', - USER: 'harvesterhci.io.user', - SETTING: 'harvesterhci.io.setting', - UPGRADE: 'harvesterhci.io.upgrade', - UPGRADE_LOG: 'harvesterhci.io.upgradelog', - SCHEDULE_VM_BACKUP: 'harvesterhci.io.schedulevmbackup', - BACKUP: 'harvesterhci.io.virtualmachinebackup', - RESTORE: 'harvesterhci.io.virtualmachinerestore', - NODE_NETWORK: 'network.harvesterhci.io.nodenetwork', - CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork', - HOST_NETWORK_CONFIG: 'network.harvesterhci.io.hostnetworkconfig', - 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', - CLUSTER: 'harvesterhci.io.management.cluster', - DASHBOARD: 'harvesterhci.io.dashboard', - BLOCK_DEVICE: 'harvesterhci.io.blockdevice', - CLOUD_TEMPLATE: 'harvesterhci.io.cloudtemplate', - HOST: 'harvesterhci.io.host', - VERSION: 'harvesterhci.io.version', - SNAPSHOT: 'harvesterhci.io.volumesnapshot', - VM_SNAPSHOT: 'harvesterhci.io.vmsnapshot', - ALERTMANAGERCONFIG: 'harvesterhci.io.monitoring.alertmanagerconfig', - CLUSTER_FLOW: 'harvesterhci.io.logging.clusterflow', - CLUSTER_OUTPUT: 'harvesterhci.io.logging.clusteroutput', - FLOW: 'harvesterhci.io.logging.flow', - OUTPUT: 'harvesterhci.io.logging.output', - STORAGE: 'harvesterhci.io.storage', - RESOURCE_QUOTA: 'harvesterhci.io.resourcequota', - KSTUNED: 'node.harvesterhci.io.ksmtuned', - HUGEPAGES: 'node.harvesterhci.io.hugepage', - PCI_DEVICE: 'devices.harvesterhci.io.pcidevice', - PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim', - SR_IOV: 'devices.harvesterhci.io.sriovnetworkdevice', - VGPU_DEVICE: 'devices.harvesterhci.io.vgpudevice', - SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice', - USB_DEVICE: 'devices.harvesterhci.io.usbdevice', - USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim', - MIG_CONFIGURATION: 'devices.harvesterhci.io.migconfiguration', - VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig', - VLAN_STATUS: 'network.harvesterhci.io.vlanstatus', - ADD_ONS: 'harvesterhci.io.addon', - LINK_MONITOR: 'network.harvesterhci.io.linkmonitor', - SECRET: 'harvesterhci.io.secret', - INVENTORY: 'metal.harvesterhci.io.inventory', - LB: 'loadbalancer.harvesterhci.io.loadbalancer', - IP_POOL: 'loadbalancer.harvesterhci.io.ippool', - HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig', - LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup', - VMIMPORT_SOURCE_V: 'migration.harvesterhci.io.vmwaresource', - VMIMPORT_SOURCE_O: 'migration.harvesterhci.io.openstacksource', - VMIMPORT_SOURCE_OVA: 'migration.harvesterhci.io.ovasource', - VMIMPORT: 'migration.harvesterhci.io.virtualmachineimport', - MIGRATION: 'migration.harvesterhci.io', - + VM: 'kubevirt.io.virtualmachine', + VMI: 'kubevirt.io.virtualmachineinstance', + VMIM: 'kubevirt.io.virtualmachineinstancemigration', + VM_TEMPLATE: 'harvesterhci.io.virtualmachinetemplate', + VM_VERSION: 'harvesterhci.io.virtualmachinetemplateversion', + IMAGE: 'harvesterhci.io.virtualmachineimage', + SSH: 'harvesterhci.io.keypair', + VOLUME: 'harvesterhci.io.volume', + USER: 'harvesterhci.io.user', + SETTING: 'harvesterhci.io.setting', + UPGRADE: 'harvesterhci.io.upgrade', + UPGRADE_LOG: 'harvesterhci.io.upgradelog', + SCHEDULE_VM_BACKUP: 'harvesterhci.io.schedulevmbackup', + BACKUP: 'harvesterhci.io.virtualmachinebackup', + RESTORE: 'harvesterhci.io.virtualmachinerestore', + NODE_NETWORK: 'network.harvesterhci.io.nodenetwork', + CLUSTER_NETWORK: 'network.harvesterhci.io.clusternetwork', + HOST_NETWORK_CONFIG: 'network.harvesterhci.io.hostnetworkconfig', + 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', + CLUSTER: 'harvesterhci.io.management.cluster', + DASHBOARD: 'harvesterhci.io.dashboard', + BLOCK_DEVICE: 'harvesterhci.io.blockdevice', + CLOUD_TEMPLATE: 'harvesterhci.io.cloudtemplate', + HOST: 'harvesterhci.io.host', + VERSION: 'harvesterhci.io.version', + SNAPSHOT: 'harvesterhci.io.volumesnapshot', + VM_SNAPSHOT: 'harvesterhci.io.vmsnapshot', + ALERTMANAGERCONFIG: 'harvesterhci.io.monitoring.alertmanagerconfig', + CLUSTER_FLOW: 'harvesterhci.io.logging.clusterflow', + CLUSTER_OUTPUT: 'harvesterhci.io.logging.clusteroutput', + FLOW: 'harvesterhci.io.logging.flow', + OUTPUT: 'harvesterhci.io.logging.output', + STORAGE: 'harvesterhci.io.storage', + RESOURCE_QUOTA: 'harvesterhci.io.resourcequota', + KSTUNED: 'node.harvesterhci.io.ksmtuned', + HUGEPAGES: 'node.harvesterhci.io.hugepage', + PCI_DEVICE: 'devices.harvesterhci.io.pcidevice', + PCI_CLAIM: 'devices.harvesterhci.io.pcideviceclaim', + SR_IOV: 'devices.harvesterhci.io.sriovnetworkdevice', + VGPU_DEVICE: 'devices.harvesterhci.io.vgpudevice', + SR_IOVGPU_DEVICE: 'devices.harvesterhci.io.sriovgpudevice', + USB_DEVICE: 'devices.harvesterhci.io.usbdevice', + USB_CLAIM: 'devices.harvesterhci.io.usbdeviceclaim', + MIG_CONFIGURATION: 'devices.harvesterhci.io.migconfiguration', + VLAN_CONFIG: 'network.harvesterhci.io.vlanconfig', + VLAN_STATUS: 'network.harvesterhci.io.vlanstatus', + ADD_ONS: 'harvesterhci.io.addon', + LINK_MONITOR: 'network.harvesterhci.io.linkmonitor', + SECRET: 'harvesterhci.io.secret', + INVENTORY: 'metal.harvesterhci.io.inventory', + LB: 'loadbalancer.harvesterhci.io.loadbalancer', + IP_POOL: 'loadbalancer.harvesterhci.io.ippool', + HARVESTER_CONFIG: 'rke-machine-config.cattle.io.harvesterconfig', + LVM_VOLUME_GROUP: 'harvesterhci.io.lvmvolumegroup', + VMIMPORT_SOURCE_V: 'migration.harvesterhci.io.vmwaresource', + VMIMPORT_SOURCE_O: 'migration.harvesterhci.io.openstacksource', + VMIMPORT_SOURCE_OVA: 'migration.harvesterhci.io.ovasource', + VMIMPORT: 'migration.harvesterhci.io.virtualmachineimport', + MIGRATION: 'migration.harvesterhci.io', + // Forklift CRDs + FORKLIFT_PROVIDER: 'forklift.konveyor.io.provider', + FORKLIFT_NETWORK_MAP: 'forklift.konveyor.io.networkmap', + FORKLIFT_STORAGE_MAP: 'forklift.konveyor.io.storagemap', + FORKLIFT_PLAN: 'forklift.konveyor.io.plan', + FORKLIFT_MIGRATION: 'forklift.konveyor.io.migration', }; export const VOLUME_SNAPSHOT = 'snapshot.storage.k8s.io.volumesnapshot';