You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2021/07/27 05:43:00 UTC

[cloudstack] branch main updated: ui: vmware vm import-unmanage (#5075)

This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 87ee866  ui: vmware vm import-unmanage (#5075)
87ee866 is described below

commit 87ee86679ecca8ef358ce4f9d00ba332afa99dc6
Author: Abhishek Kumar <ab...@gmail.com>
AuthorDate: Tue Jul 27 11:12:37 2021 +0530

    ui: vmware vm import-unmanage (#5075)
    
    Adds UI for importing and unmanaging VMs.
    A new navigation section - Tools has been added in the UI.
    
    Doc PR: apache/cloudstack-documentation#221
    
    Signed-off-by: Abhishek Kumar <ab...@gmail.com>
---
 .../api/response/UnmanagedInstanceResponse.java    |  12 +
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     |  38 +-
 ui/public/locales/en.json                          |  34 +-
 ui/src/components/CheckBoxInputPair.vue            | 117 +++
 ui/src/components/CheckBoxSelectPair.vue           | 103 ++-
 ui/src/components/view/InfoCard.vue                |  48 +-
 ui/src/components/view/SearchView.vue              |   9 +-
 ui/src/components/widgets/Status.vue               |   2 +
 ui/src/config/router.js                            |   2 +
 ui/src/config/section/tools.js                     |  34 +
 ui/src/views/AutogenView.vue                       |   4 +-
 ui/src/views/compute/DeployVM.vue                  |   6 +-
 ui/src/views/compute/ScaleVM.vue                   |   6 +-
 ui/src/views/compute/wizard/ComputeSelection.vue   |   8 +-
 ui/src/views/compute/wizard/MultiDiskSelection.vue | 128 +++-
 .../views/compute/wizard/MultiNetworkSelection.vue | 257 +++++++
 ui/src/views/offering/AddNetworkOffering.vue       |   2 +-
 ui/src/views/offering/AddVpcOffering.vue           |   2 +-
 ui/src/views/tools/ImportUnmanagedInstance.vue     | 739 ++++++++++++++++++
 ui/src/views/tools/ManageInstances.vue             | 822 +++++++++++++++++++++
 20 files changed, 2246 insertions(+), 127 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java
index 5167f17..e866b19 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java
@@ -43,6 +43,10 @@ public class UnmanagedInstanceResponse extends BaseResponse {
     @Param(description = "the ID of the host to which virtual machine belongs")
     private String hostId;
 
+    @SerializedName(ApiConstants.HOST_NAME)
+    @Param(description = "the name of the host to which virtual machine belongs")
+    private String hostName;
+
     @SerializedName(ApiConstants.POWER_STATE)
     @Param(description = "the power state of the virtual machine")
     private String  powerState;
@@ -108,6 +112,14 @@ public class UnmanagedInstanceResponse extends BaseResponse {
         this.hostId = hostId;
     }
 
+    public String getHostName() {
+        return hostName;
+    }
+
+    public void setHostName(String hostName) {
+        this.hostName = hostName;
+    }
+
     public String getPowerState() {
         return powerState;
     }
diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index 501ca63..a470486 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -25,17 +25,6 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
-import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
-import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
-import com.cloud.event.ActionEvent;
-import com.cloud.exception.UnsupportedServiceException;
-import com.cloud.storage.Snapshot;
-import com.cloud.storage.SnapshotVO;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.vm.NicVO;
-import com.cloud.vm.UserVmVO;
-import com.cloud.vm.dao.UserVmDao;
-import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiErrorCode;
 import org.apache.cloudstack.api.ResponseGenerator;
@@ -59,12 +48,15 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.GetUnmanagedInstancesAnswer;
 import com.cloud.agent.api.GetUnmanagedInstancesCommand;
+import com.cloud.agent.api.PrepareUnmanageVMInstanceAnswer;
+import com.cloud.agent.api.PrepareUnmanageVMInstanceCommand;
 import com.cloud.capacity.CapacityManager;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.Resource;
@@ -75,6 +67,7 @@ import com.cloud.deploy.DataCenterDeployment;
 import com.cloud.deploy.DeployDestination;
 import com.cloud.deploy.DeploymentPlanner;
 import com.cloud.deploy.DeploymentPlanningManager;
+import com.cloud.event.ActionEvent;
 import com.cloud.event.EventTypes;
 import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.InsufficientAddressCapacityException;
@@ -83,6 +76,7 @@ import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
 import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.UnsupportedServiceException;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.Status;
@@ -103,6 +97,8 @@ import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.GuestOS;
 import com.cloud.storage.GuestOSHypervisor;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateStoragePoolVO;
 import com.cloud.storage.VMTemplateVO;
@@ -112,6 +108,7 @@ import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.GuestOSHypervisorDao;
+import com.cloud.storage.dao.SnapshotDao;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VMTemplatePoolDao;
 import com.cloud.storage.dao.VolumeDao;
@@ -127,7 +124,9 @@ import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.DiskProfile;
 import com.cloud.vm.NicProfile;
+import com.cloud.vm.NicVO;
 import com.cloud.vm.UserVmManager;
+import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineManager;
@@ -135,7 +134,9 @@ import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.VirtualMachineProfileImpl;
 import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.NicDao;
+import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 import com.google.common.base.Strings;
 import com.google.gson.Gson;
 
@@ -243,6 +244,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
         }
         if (host != null) {
             response.setHostId(host.getUuid());
+            response.setHostName(host.getName());
         }
         response.setPowerState(instance.getPowerState().toString());
         response.setCpuCores(instance.getCpuCores());
@@ -1078,6 +1080,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
         if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
             throw new InvalidParameterValueException(String.format("VM ingestion is currently not supported for hypervisor: %s", cluster.getHypervisorType().toString()));
         }
+        String keyword = cmd.getKeyword();
+        if (StringUtils.isNotEmpty(keyword)) {
+            keyword = keyword.toLowerCase();
+        }
         List<HostVO> hosts = resourceManager.listHostsInClusterByStatus(clusterId, Status.Up);
         List<String> additionalNameFilters = getAdditionalNameFilters(cluster);
         List<UnmanagedInstanceResponse> responses = new ArrayList<>();
@@ -1097,11 +1103,15 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
                 continue;
             }
             GetUnmanagedInstancesAnswer unmanagedInstancesAnswer = (GetUnmanagedInstancesAnswer) answer;
-            HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>();
-            unmanagedInstances.putAll(unmanagedInstancesAnswer.getUnmanagedInstances());
+            HashMap<String, UnmanagedInstanceTO> unmanagedInstances = new HashMap<>(unmanagedInstancesAnswer.getUnmanagedInstances());
             Set<String> keys = unmanagedInstances.keySet();
             for (String key : keys) {
-                responses.add(createUnmanagedInstanceResponse(unmanagedInstances.get(key), cluster, host));
+                UnmanagedInstanceTO instance = unmanagedInstances.get(key);
+                if (StringUtils.isNotEmpty(keyword) &&
+                        !instance.getName().toLowerCase().contains(keyword)) {
+                    continue;
+                }
+                responses.add(createUnmanagedInstanceResponse(instance, cluster, host));
             }
         }
         ListResponse<UnmanagedInstanceResponse> listResponses = new ListResponse<>();
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 8c5e8ee..178f255 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -219,6 +219,7 @@
 "label.action.lock.account.processing": "Locking account....",
 "label.action.manage.cluster": "Manage Cluster",
 "label.action.manage.cluster.processing": "Managing Cluster....",
+"label.action.import.export.instances":"Import-Export Instances",
 "label.action.migrate.instance": "Migrate Instance",
 "label.action.migrate.instance.processing": "Migrating Instance....",
 "label.action.migrate.router": "Migrate Router",
@@ -276,6 +277,8 @@
 "label.action.unmanage.cluster": "Unmanage Cluster",
 "label.action.unmanage.cluster.processing": "Unmanaging Cluster....",
 "label.action.unmanage.virtualmachine": "Unmanage VM",
+"label.action.unmanage.instance": "Unmanage Instance",
+"label.action.unmanage.instances": "Unmanage Instances",
 "label.action.update.offering.access": "Update Offering Access",
 "label.action.update.os.preference": "Update OS Preference",
 "label.action.update.os.preference.processing": "Updating OS Preference....",
@@ -461,6 +464,8 @@
 "label.asyncbackup": "Async Backup",
 "label.author.email": "Author e-mail",
 "label.author.name": "Author name",
+"label.auto.assign.diskoffering.disk.size": "Automatically assign offering matching the disk size",
+"label.auto.assign.random.ip": "Automatically assign a random IP address",
 "label.autoscale": "AutoScale",
 "label.autoscale.configuration.wizard": "AutoScale Configuration Wizard",
 "label.availability": "Availability",
@@ -773,6 +778,7 @@
 "label.disk.offering.access": "Disk offering access",
 "label.disk.offering.details": "Disk offering details",
 "label.disk.offerings": "Disk Offerings",
+"label.disk.selection": "Disk Selection",
 "label.disk.size": "Disk Size",
 "label.disk.volume": "Disk Volume",
 "label.diskbytesreadrate": "Disk Read Rate (BPS)",
@@ -1057,6 +1063,7 @@
 "label.ikeversion": "IKE Version",
 "label.images": "Images",
 "label.import.backup.offering": "Import Backup Offering",
+"label.import.instance": "Import Instance",
 "label.import.offering": "Import Offering",
 "label.import.role": "Import Role",
 "label.in.progress": "in progress",
@@ -1301,6 +1308,7 @@
 "label.manage.resources": "Manage Resources",
 "label.manage.vpn.user": "Manage VPN Users",
 "label.managedstate": "Managed State",
+"label.managed.instances": "Managed Instances",
 "label.management": "Management",
 "label.management.ips": "Management IP Addresses",
 "label.management.server": "Management Server",
@@ -1383,6 +1391,7 @@
 "label.metrics.network.usage": "Network Usage",
 "label.metrics.network.write": "Write",
 "label.metrics.num.cpu.cores": "Cores",
+"label.migrate.allowed": "Migrate Allowed",
 "label.migrate.data.from.image.store": "Migrate Data from Image store",
 "label.migrate.instance.to": "Migrate instance to",
 "label.migrate.instance.to.host": "Migrate instance to another host",
@@ -1453,6 +1462,7 @@
 "label.network.offering.display.text": "Network Offering Display Text",
 "label.network.offering.name": "Network Offering Name",
 "label.network.offerings": "Network Offerings",
+"label.network.selection": "Network Selection",
 "label.network.service.providers": "Network Service Providers",
 "label.networkcidr": "Network CIDR",
 "label.networkdevicetype": "Type",
@@ -1493,6 +1503,7 @@
 "label.nfscachepath": "Path",
 "label.nfscachezoneid": "Zone",
 "label.nfsserver": "NFS Server",
+"label.nic": "NIC",
 "label.nicadaptertype": "NIC adapter type",
 "label.nicira.controller.address": "Controller Address",
 "label.nicira.nvp.details": "Nicira NVP details",
@@ -1844,6 +1855,7 @@
 "label.rolename": "Role",
 "label.roles": "Roles",
 "label.roletype": "Role Type",
+"label.rootdisk": "ROOT disk",
 "label.rootdisksize": "Root disk size (GB)",
 "label.root.certificate": "Root certificate",
 "label.root.disk.offering": "Root Disk Offering",
@@ -2133,6 +2145,8 @@
 "label.templatesubject": "Subject",
 "label.templatetotal": "Template",
 "label.templatetype": "Template Type",
+"label.template.temporary.import": "Use a temporary template for import",
+"label.template.select.existing": "Select an existing template",
 "label.tftp.dir": "TFTP Directory",
 "label.tftpdir": "Tftp root directory",
 "label.theme.default": "Default Theme",
@@ -2151,6 +2165,7 @@
 "label.to": "to",
 "label.token": "Token",
 "label.token.for.dashboard.login": "Token for dashboard login can be retrieved using following command",
+"label.tools": "Tools",
 "label.total": "Total",
 "label.total.hosts": "Total Hosts",
 "label.total.memory": "Total Memory",
@@ -2177,6 +2192,9 @@
 "label.unit": "Usage Unit",
 "label.unknown": "Unknown",
 "label.unlimited": "Unlimited",
+"label.unmanage.instance": "Unmanage Instance",
+"label.unmanaged.instance": "Unmanaged Instance",
+"label.unmanaged.instances": "Unmanaged Instances",
 "label.untagged": "Untagged",
 "label.update.instance.group": "Update Instance Group",
 "label.update.physical.network": "Update Physical Network",
@@ -2487,7 +2505,10 @@
 "message.action.stop.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to stop this router.",
 "message.action.stop.systemvm": "Please confirm that you want to stop this system VM.",
 "message.action.unmanage.cluster": "Please confirm that you want to unmanage the cluster.",
+"message.action.unmanage.instance": "Please confirm that you want to unmanage the instance.",
+"message.action.unmanage.instances": "Please confirm that you want to unmanage the instances.",
 "message.action.unmanage.virtualmachine": "Please confirm that you want to unmanage the virtual machine.",
+"message.action.unmanage.virtualmachines": "Please confirm that you want to unmanage the virtual machines.",
 "message.action.vmsnapshot.create": "Please confirm that you want to take a snapshot of this instance. <br>Please notice that the instance will be paused during the snapshoting, and resumed after snapshotting, if it runs on KVM.",
 "message.action.vmsnapshot.delete": "Please confirm that you want to delete this VM snapshot. <br>Please notice that the instance will be paused before the snapshot deletion, and resumed after deletion, if it runs on KVM.",
 "message.action.vmsnapshot.revert": "Revert VM snapshot",
@@ -2786,6 +2807,7 @@
 "message.enabling.zone.dots": "Enabling zone...",
 "message.enter.seperated.list.multiple.cidrs": "Please enter a comma separated list of CIDRs if more than one",
 "message.enter.token": "Please enter the token that you were given in your invite e-mail.",
+"message.enter.valid.nic.ip": "Please enter a valid IP address for NIC",
 "message.error.access.key": "Please enter Access Key",
 "message.error.add.guest.network": "Either IPv4 fields or IPv6 fields need to be filled when adding a guest network",
 "message.error.add.secondary.ipaddress": "There was an error adding the secondary IP Address",
@@ -2917,6 +2939,7 @@
 "message.guestnetwork.state.shutdown": "Indicates the network configuration is being destroyed",
 "message.host.dedicated": "Host Dedicated",
 "message.host.dedication.released": "Host dedication released",
+"message.desc.importexportinstancewizard": "Import and export instances to/from an existing VMware zone.<br/><br/>This feature only applies Cloudstack VMware zones. By choosing to Manage an instance, CloudStack takes over the orchestration of that instance. The instance is left running and not physically moved. Unmanaging instances, removes CloudStack ability to mange them (but they are left running and not destroyed)",
 "message.info.cloudian.console": "Cloudian Management Console should open in another window",
 "message.installwizard.click.retry": "Click the button to retry launch.",
 "message.installwizard.copy.whatisacluster": "A cluster provides a way to group hosts. The hosts in a cluster all have identical hardware, run the same hypervisor, are on the same subnet, and access the same shared storage. Virtual machine instances (VMs) can be live-migrated from one host to another within the same cluster, without interrupting service to the user. A cluster is the third-largest organizational unit within a CloudStack™; deployment. Clusters are contained within pods, an [...]
@@ -2952,10 +2975,13 @@
 "message.installwizard.tooltip.configureguesttraffic.guestnetmask": "The netmask in use on the subnet that the guests should use",
 "message.installwizard.tooltip.configureguesttraffic.gueststartip": "The range of IP addresses that will be available for allocation to guests in this zone.  If one NIC is used, these IPs should be in the same CIDR as the pod CIDR.",
 "message.installwizard.tooltip.configureguesttraffic.name": "A name for your network",
-"message.instance.scaled.up.confirm": "Do you really want to scale Up your instance ?",
+"message.instances.managed": "Instances or VMs controlled by CloudStack",
+"message.instances.scaled.up.confirm": "Do you really want to scale Up your instance ?",
+"message.instances.unmanaged": "Instances or VMs not controlled by CloudStack",
 "message.instancewizard.notemplates": "You do not have any templates available; please add a compatible template, and re-launch the instance wizard.",
 "message.interloadbalance.not.return.elementid": "error: listInternalLoadBalancerElements API doesn't return Internal LB Element Id",
 "message.ip.address.changed": "Your IP addresses may have changed; would you like to refresh the listing? Note that in this case the details pane will close.",
+"message.ip.address.changes.effect.after.vm.restart": "IP address changes takes effect only after VM restart.",
 "message.iso.desc": "Disc image containing data or bootable media for OS",
 "message.join.project": "You have now joined a project. Please switch to Project view to see the project.",
 "message.kubeconfig.cluster.not.available": "Kubernetes cluster kubeconfig not available currently",
@@ -3037,6 +3063,7 @@
 "message.pending.projects.2": "To view, please go to the projects section, then select invitations from the drop-down.",
 "message.please.add.at.lease.one.traffic.range": "Please add at least one traffic range.",
 "message.please.confirm.remove.ssh.key.pair": "Please confirm that you want to remove this SSH Key Pair",
+"message.please.enter.valid.value": "Please enter a valid value",
 "message.please.enter.value": "Please enter values",
 "message.please.proceed": "Please proceed to the next step.",
 "message.please.select.a.configuration.for.your.zone": "Please select a configuration for your zone.",
@@ -3110,10 +3137,12 @@
 "message.select.a.zone": "A zone typically corresponds to a single datacenter. Multiple zones help make the cloud more reliable by providing physical isolation and redundancy.",
 "message.select.affinity.groups": "Please select any affinity groups you want this VM to belong to:",
 "message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to",
+"message.select.disk.offering": "Please select a disk offering for disk",
 "message.select.instance": "Please select an instance.",
 "message.select.iso": "Please select an ISO for your new virtual instance.",
 "message.select.item": "Please select an item.",
 "message.select.migration.policy": "Please select a migration Policy",
+"message.select.nic.network": "Please select a network for NIC",
 "message.select.security.groups": "Please select security group(s) for your new VM",
 "message.select.template": "Please select a template for your new virtual instance.",
 "message.select.tier": "Please select a tier",
@@ -3184,6 +3213,7 @@
 "message.success.edit.acl": "Successfully edited ACL rule",
 "message.success.edit.rule": "Successfully edited rule",
 "message.success.enable.saml.auth": "Successfully enabled SAML Authorization",
+"message.success.import.instance": "Successfully imported instance",
 "message.success.migrate.volume": "Successfully migrated volume",
 "message.success.migrating": "Migration completed successfully for",
 "message.success.move.acl.order": "Successfully moved ACL rule",
@@ -3213,6 +3243,7 @@
 "message.success.upload.iso.description": "This ISO file has been uploaded. Please check its status in the Images > ISOs menu",
 "message.success.upload.template.description": "This template file has been uploaded. Please check its status at Templates menu",
 "message.success.upload.volume.description": "This Volume has been uploaded. Please check its status in the Volumes menu",
+"message.success.unmanage.instance": "Successfully unmanaged instance",
 "message.suspend.project": "Are you sure you want to suspend this project?",
 "message.sussess.discovering.feature": "Discovered all available features!",
 "message.switch.to": "Switched to",
@@ -3221,6 +3252,7 @@
 "message.template.copying": "Template is being copied.",
 "message.template.desc": "OS image that can be used to boot VMs",
 "message.template.iso": "Please select a template or ISO to continue",
+"message.template.import.vm.temporary": "If  a temporary template is used, reset VM operation will not work after import.",
 "message.tier.required": "Tier is required",
 "message.tooltip.dns.1": "Name of a DNS server for use by VMs in the zone. The public IP addresses for the zone must have a route to this server.",
 "message.tooltip.dns.2": "A second DNS server name for use by VMs in the zone. The public IP addresses for the zone must have a route to this server.",
diff --git a/ui/src/components/CheckBoxInputPair.vue b/ui/src/components/CheckBoxInputPair.vue
new file mode 100644
index 0000000..fee6f4d
--- /dev/null
+++ b/ui/src/components/CheckBoxInputPair.vue
@@ -0,0 +1,117 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <div v-if="visible" style="width: 100%">
+    <a-col>
+      <a-row :md="24" :lg="layout === 'horizontal' ? 12 : 24">
+        <a-checkbox
+          v-decorator="[checkBoxDecorator, {}]"
+          :checked="checked"
+          @change="handleCheckChange">
+          {{ checkBoxLabel }}
+        </a-checkbox>
+      </a-row>
+      <a-row :md="24" :lg="layout === 'horizontal' ? 12 : 24">
+        <a-form-item
+          :label="inputLabel"
+          v-if="reversed !== checked">
+          <a-input
+            v-decorator="[inputDecorator, {
+              initialValue: defaultInputValue
+            }]"
+            @change="val => handleInputChangeTimed(val)" />
+        </a-form-item>
+      </a-row>
+    </a-col>
+  </div>
+</template>
+
+<script>
+
+export default {
+  name: 'CheckBoxInputPair',
+  props: {
+    layout: {
+      type: String,
+      default: 'horizontal'
+    },
+    resourceKey: {
+      type: String,
+      required: true
+    },
+    checkBoxLabel: {
+      type: String,
+      required: true
+    },
+    checkBoxDecorator: {
+      type: String,
+      default: ''
+    },
+    defaultCheckBoxValue: {
+      type: Boolean,
+      default: false
+    },
+    defaultInputValue: {
+      type: String,
+      default: ''
+    },
+    inputLabel: {
+      type: String,
+      default: ''
+    },
+    inputDecorator: {
+      type: String,
+      default: ''
+    },
+    visible: {
+      type: Boolean,
+      default: true
+    },
+    reversed: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      checked: false,
+      inputValue: '',
+      inputUpdateTimer: null
+    }
+  },
+  created () {
+    this.checked = this.defaultCheckBoxValue
+  },
+  methods: {
+    handleCheckChange (e) {
+      this.checked = e.target.checked
+      this.$emit('handle-checkinputpair-change', this.resourceKey, this.checked, this.inputValue)
+    },
+    handleInputChange (e) {
+      this.inputValue = e.target.value
+      this.$emit('handle-checkinputpair-change', this.resourceKey, this.checked, this.inputValue)
+    },
+    handleInputChangeTimed (e) {
+      clearTimeout(this.inputUpdateTimer)
+      this.inputUpdateTimer = setTimeout(() => {
+        this.handleInputChange(e)
+      }, 500)
+    }
+  }
+}
+</script>
diff --git a/ui/src/components/CheckBoxSelectPair.vue b/ui/src/components/CheckBoxSelectPair.vue
index ec6c6fa..29035e6 100644
--- a/ui/src/components/CheckBoxSelectPair.vue
+++ b/ui/src/components/CheckBoxSelectPair.vue
@@ -16,26 +16,39 @@
 // under the License.
 
 <template>
-  <div>
-    <a-checkbox v-decorator="[checkBoxDecorator, {}]" class="pair-checkbox" @change="handleCheckChange">
-      {{ checkBoxLabel }}
-    </a-checkbox>
-    <a-form-item class="pair-select-container" :label="selectLabel" v-if="this.checked">
-      <a-select
-        v-decorator="[selectDecorator, {
-          initialValue: selectedOption ? selectedOption : this.getSelectInitialValue()
-        }]"
-        showSearch
-        optionFilterProp="children"
-        :filterOption="(input, option) => {
-          return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
-        }"
-        @change="val => { this.handleSelectChange(val) }">
-        <a-select-option v-for="(opt) in selectOptions" :key="opt.name" :disabled="opt.enabled === false">
-          {{ opt.name || opt.description }}
-        </a-select-option>
-      </a-select>
-    </a-form-item>
+  <div style="width: 100%">
+    <a-row :gutter="6">
+      <a-col :md="24" :lg="layout === 'horizontal' ? 12 : 24">
+        <a-checkbox
+          v-decorator="[checkBoxDecorator, {}]"
+          :checked="checked"
+          @change="handleCheckChange">
+          {{ checkBoxLabel }}
+        </a-checkbox>
+      </a-col>
+      <a-col :md="24" :lg="layout === 'horizontal' ? 12 : 24">
+        <a-form-item
+          v-if="reversed != checked"
+          :label="selectLabel">
+          <a-select
+            v-decorator="[selectDecorator, { initialValue: selectedOption ? selectedOption : getSelectInitialValue()}]"
+            :defaultValue="selectDecorator ? undefined : selectedOption ? selectedOption : getSelectInitialValue()"
+            showSearch
+            optionFilterProp="children"
+            :filterOption="(input, option) => {
+              return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }"
+            @change="val => { this.handleSelectChange(val) }">
+            <a-select-option
+              v-for="(opt) in selectSource"
+              :key="opt.id"
+              :disabled="opt.enabled === false">
+              {{ opt.displaytext || opt.name || opt.description }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-col>
+    </a-row>
   </div>
 </template>
 
@@ -44,6 +57,10 @@
 export default {
   name: 'CheckBoxSelectPair',
   props: {
+    layout: {
+      type: String,
+      default: 'horizontal'
+    },
     resourceKey: {
       type: String,
       required: true
@@ -56,6 +73,10 @@ export default {
       type: String,
       default: ''
     },
+    defaultCheckBoxValue: {
+      type: Boolean,
+      default: false
+    },
     selectOptions: {
       type: Array,
       required: true
@@ -67,12 +88,30 @@ export default {
     selectDecorator: {
       type: String,
       default: ''
+    },
+    reversed: {
+      type: Boolean,
+      default: false
     }
   },
   data () {
     return {
       checked: false,
-      selectedOption: ''
+      selectedOption: null
+    }
+  },
+  created () {
+    this.checked = this.defaultCheckBoxValue
+  },
+  computed: {
+    selectSource () {
+      return this.selectOptions.map(item => {
+        var option = { ...item }
+        if (!('id' in option)) {
+          option.id = option.name
+        }
+        return option
+      })
     }
   },
   methods: {
@@ -80,30 +119,18 @@ export default {
       return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
     },
     getSelectInitialValue () {
-      const provider = this.selectOptions?.filter(x => x.enabled)?.[0]?.name || ''
-      this.handleSelectChange(provider)
-      return provider
+      const initialValue = this.selectSource?.filter(x => x.enabled !== false)?.[0]?.id || ''
+      this.handleSelectChange(initialValue)
+      return initialValue
     },
     handleCheckChange (e) {
       this.checked = e.target.checked
-      this.$emit('handle-checkpair-change', this.resourceKey, this.checked, '')
+      this.$emit('handle-checkselectpair-change', this.resourceKey, this.checked, this.selectedOption)
     },
     handleSelectChange (val) {
       this.selectedOption = val
-      this.$emit('handle-checkpair-change', this.resourceKey, this.checked, val)
+      this.$emit('handle-checkselectpair-change', this.resourceKey, this.checked, this.selectedOption)
     }
   }
 }
 </script>
-
-<style scoped lang="scss">
-  .pair-checkbox {
-    width: 180px;
-  }
-  .pair-select-container {
-    position: relative;
-    float: right;
-    margin-bottom: -5px;
-    width: 20vw;
-  }
-</style>
diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue
index b2a8dd3..57f8e66 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -280,7 +280,7 @@
                 :key="eth.id"
                 style="margin-left: -24px; margin-top: 5px;">
                 <a-icon type="api" />eth{{ index }} {{ eth.ipaddress }}
-                <router-link v-if="eth.networkname && eth.networkid" :to="{ path: '/guestnetwork/' + eth.networkid }">({{ eth.networkname }})</router-link>
+                <router-link v-if="!isStatic && eth.networkname && eth.networkid" :to="{ path: '/guestnetwork/' + eth.networkid }">({{ eth.networkname }})</router-link>
                 <a-tag v-if="eth.isdefault">
                   {{ $t('label.default') }}
                 </a-tag >
@@ -311,7 +311,7 @@
               type="environment"
               @click="$message.success(`${$t('label.copied.clipboard')} : ${ ipaddress }`)"
               v-clipboard:copy="ipaddress" />
-            <router-link v-if="resource.ipaddressid" :to="{ path: '/publicip/' + resource.ipaddressid }">{{ ipaddress }}</router-link>
+            <router-link v-if="!isStatic && resource.ipaddressid" :to="{ path: '/publicip/' + resource.ipaddressid }">{{ ipaddress }}</router-link>
             <span v-else>{{ ipaddress }}</span>
           </div>
         </div>
@@ -329,7 +329,7 @@
           <div class="resource-detail-item__label">{{ $t('label.project') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="project" />
-            <router-link v-if="resource.projectid" :to="{ path: '/project/' + resource.projectid }">{{ resource.project || resource.projectname || resource.projectid }}</router-link>
+            <router-link v-if="!isStatic && resource.projectid" :to="{ path: '/project/' + resource.projectid }">{{ resource.project || resource.projectname || resource.projectid }}</router-link>
             <router-link v-else :to="{ path: '/project', query: { name: resource.projectname }}">{{ resource.projectname }}</router-link>
           </div>
         </div>
@@ -412,10 +412,10 @@
           <div class="resource-detail-item__details">
             <a-icon type="picture" />
             <div v-if="resource.isoid">
-              <router-link :to="{ path: '/iso/' + resource.isoid }">{{ resource.isoname || resource.isoid }} </router-link>
+              <router-link :to="{ path: '/iso/' + resource.isoid }">{{ resource.isodisplaytext || resource.isoname || resource.isoid }} </router-link>
             </div>
             <div v-else>
-              <router-link :to="{ path: '/template/' + resource.templateid }">{{ resource.templatename || resource.templateid }} </router-link>
+              <router-link :to="{ path: '/template/' + resource.templateid }">{{ resource.templatedisplaytext || resource.templatename || resource.templateid }} </router-link>
             </div>
           </div>
         </div>
@@ -423,7 +423,7 @@
           <div class="resource-detail-item__label">{{ $t('label.serviceofferingname') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="cloud" />
-            <router-link v-if="$route.meta.name === 'router'" :to="{ path: '/computeoffering/' + resource.serviceofferingid, query: { issystem: true } }">{{ resource.serviceofferingname || resource.serviceofferingid }} </router-link>
+            <router-link v-if="!isStatic && $route.meta.name === 'router'" :to="{ path: '/computeoffering/' + resource.serviceofferingid, query: { issystem: true } }">{{ resource.serviceofferingname || resource.serviceofferingid }} </router-link>
             <router-link v-else-if="$router.resolve('/computeoffering/' + resource.serviceofferingid).route.name !== '404'" :to="{ path: '/computeoffering/' + resource.serviceofferingid }">{{ resource.serviceofferingname || resource.serviceofferingid }} </router-link>
             <span v-else>{{ resource.serviceofferingname || resource.serviceofferingid }}</span>
           </div>
@@ -432,21 +432,21 @@
           <div class="resource-detail-item__label">{{ $t('label.diskoffering') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="hdd" />
-            <router-link v-if="$router.resolve('/diskoffering/' + resource.diskofferingid).route.name !== '404'" :to="{ path: '/diskoffering/' + resource.diskofferingid }">{{ resource.diskofferingname || resource.diskofferingid }} </router-link>
+            <router-link v-if="!isStatic && $router.resolve('/diskoffering/' + resource.diskofferingid).route.name !== '404'" :to="{ path: '/diskoffering/' + resource.diskofferingid }">{{ resource.diskofferingname || resource.diskofferingid }} </router-link>
             <span v-else>{{ resource.diskofferingname || resource.diskofferingid }}</span>
           </div>
         </div>
         <div class="resource-detail-item" v-if="resource.backupofferingid">
           <div class="resource-detail-item__label">{{ $t('label.backupofferingid') }}</div>
           <a-icon type="cloud-upload" />
-          <router-link v-if="$router.resolve('/backupoffering/' + resource.backupofferingid).route.name !== '404'" :to="{ path: '/backupoffering/' + resource.backupofferingid }">{{ resource.backupofferingname || resource.backupofferingid }} </router-link>
+          <router-link v-if="!isStatic && $router.resolve('/backupoffering/' + resource.backupofferingid).route.name !== '404'" :to="{ path: '/backupoffering/' + resource.backupofferingid }">{{ resource.backupofferingname || resource.backupofferingid }} </router-link>
           <span v-else>{{ resource.backupofferingname || resource.backupofferingid }}</span>
         </div>
         <div class="resource-detail-item" v-if="resource.networkofferingid">
           <div class="resource-detail-item__label">{{ $t('label.networkofferingid') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="wifi" />
-            <router-link v-if="$router.resolve('/networkoffering/' + resource.networkofferingid).route.name !== '404'" :to="{ path: '/networkoffering/' + resource.networkofferingid }">{{ resource.networkofferingname || resource.networkofferingid }} </router-link>
+            <router-link v-if="!isStatic && $router.resolve('/networkoffering/' + resource.networkofferingid).route.name !== '404'" :to="{ path: '/networkoffering/' + resource.networkofferingid }">{{ resource.networkofferingname || resource.networkofferingid }} </router-link>
             <span v-else>{{ resource.networkofferingname || resource.networkofferingid }}</span>
           </div>
         </div>
@@ -454,7 +454,7 @@
           <div class="resource-detail-item__label">{{ $t('label.vpcoffering') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="deployment-unit" />
-            <router-link v-if="$router.resolve('/vpcoffering/' + resource.vpcofferingid).route.name !== '404'" :to="{ path: '/vpcoffering/' + resource.vpcofferingid }">{{ resource.vpcofferingname || resource.vpcofferingid }} </router-link>
+            <router-link v-if="!isStatic && $router.resolve('/vpcoffering/' + resource.vpcofferingid).route.name !== '404'" :to="{ path: '/vpcoffering/' + resource.vpcofferingid }">{{ resource.vpcofferingname || resource.vpcofferingid }} </router-link>
             <span v-else>{{ resource.vpcofferingname || resource.vpcofferingid }}</span>
           </div>
         </div>
@@ -462,7 +462,7 @@
           <div class="resource-detail-item__label">{{ $t('label.storagepool') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="database" />
-            <router-link v-if="$router.resolve('/storagepool/' + resource.storageid).route.name !== '404'" :to="{ path: '/storagepool/' + resource.storageid }">{{ resource.storage || resource.storageid }} </router-link>
+            <router-link v-if="!isStatic && $router.resolve('/storagepool/' + resource.storageid).route.name !== '404'" :to="{ path: '/storagepool/' + resource.storageid }">{{ resource.storage || resource.storageid }} </router-link>
             <span v-else>{{ resource.storage || resource.storageid }}</span>
             <a-tag style="margin-left: 5px;" v-if="resource.storagetype">
               {{ resource.storagetype }}
@@ -473,7 +473,7 @@
           <div class="resource-detail-item__label">{{ $t('label.hostname') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="desktop" />
-            <router-link v-if="$router.resolve('/host/' + resource.hostid).route.name !== '404'" :to="{ path: '/host/' + resource.hostid }">{{ resource.hostname || resource.hostid }} </router-link>
+            <router-link v-if="!isStatic && $router.resolve('/host/' + resource.hostid).route.name !== '404'" :to="{ path: '/host/' + resource.hostid }">{{ resource.hostname || resource.hostid }} </router-link>
             <span v-else>{{ resource.hostname || resource.hostid }}</span>
           </div>
         </div>
@@ -481,7 +481,7 @@
           <div class="resource-detail-item__label">{{ $t('label.clusterid') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="cluster" />
-            <router-link v-if="$router.resolve('/cluster/' + resource.clusterid).route.name !== '404'" :to="{ path: '/cluster/' + resource.clusterid }">{{ resource.clustername || resource.cluster || resource.clusterid }}</router-link>
+            <router-link v-if="!isStatic && $router.resolve('/cluster/' + resource.clusterid).route.name !== '404'" :to="{ path: '/cluster/' + resource.clusterid }">{{ resource.clustername || resource.cluster || resource.clusterid }}</router-link>
             <span v-else>{{ resource.clustername || resource.cluster || resource.clusterid }}</span>
           </div>
         </div>
@@ -489,7 +489,7 @@
           <div class="resource-detail-item__label">{{ $t('label.podid') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="appstore" />
-            <router-link v-if="$router.resolve('/pod/' + resource.podid).route.name !== '404'" :to="{ path: '/pod/' + resource.podid }">{{ resource.podname || resource.pod || resource.podid }}</router-link>
+            <router-link v-if="!isStatic && $router.resolve('/pod/' + resource.podid).route.name !== '404'" :to="{ path: '/pod/' + resource.podid }">{{ resource.podname || resource.pod || resource.podid }}</router-link>
             <span v-else>{{ resource.podname || resource.pod || resource.podid }}</span>
           </div>
         </div>
@@ -497,7 +497,7 @@
           <div class="resource-detail-item__label">{{ $t('label.zone') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="global" />
-            <router-link v-if="$router.resolve('/zone/' + resource.zoneid).route.name !== '404'" :to="{ path: '/zone/' + resource.zoneid }">{{ resource.zone || resource.zonename || resource.zoneid }}</router-link>
+            <router-link v-if="!isStatic && $router.resolve('/zone/' + resource.zoneid).route.name !== '404'" :to="{ path: '/zone/' + resource.zoneid }">{{ resource.zone || resource.zonename || resource.zoneid }}</router-link>
             <span v-else>{{ resource.zone || resource.zonename || resource.zoneid }}</span>
           </div>
         </div>
@@ -508,7 +508,7 @@
             <template v-for="(item,idx) in resource.owner">
               <span style="margin-right:5px" :key="idx">
                 <span v-if="$store.getters.userInfo.roletype !== 'User'">
-                  <router-link v-if="'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: resource.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link>
+                  <router-link v-if="!isStatic && 'user' in item" :to="{ path: '/accountuser', query: { username: item.user, domainid: resource.domainid }}">{{ item.account + '(' + item.user + ')' }}</router-link>
                   <router-link v-else :to="{ path: '/account', query: { name: item.account, domainid: resource.domainid } }">{{ item.account }}</router-link>
                 </span>
                 <span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span>
@@ -520,7 +520,7 @@
           <div class="resource-detail-item__label">{{ $t('label.account') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="user" />
-            <router-link v-if="$store.getters.userInfo.roletype !== 'User'" :to="{ path: '/account', query: { name: resource.account, domainid: resource.domainid } }">{{ resource.account }}</router-link>
+            <router-link v-if="!isStatic && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/account', query: { name: resource.account, domainid: resource.domainid } }">{{ resource.account }}</router-link>
             <span v-else>{{ resource.account }}</span>
           </div>
         </div>
@@ -528,7 +528,7 @@
           <div class="resource-detail-item__label">{{ $t('label.role') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="idcard" />
-            <router-link v-if="$router.resolve('/role/' + resource.roleid).route.name !== '404'" :to="{ path: '/role/' + resource.roleid }">{{ resource.rolename || resource.role || resource.roleid }}</router-link>
+            <router-link v-if="!isStatic && $router.resolve('/role/' + resource.roleid).route.name !== '404'" :to="{ path: '/role/' + resource.roleid }">{{ resource.rolename || resource.role || resource.roleid }}</router-link>
             <span v-else>{{ resource.rolename || resource.role || resource.roleid }}</span>
           </div>
         </div>
@@ -536,7 +536,7 @@
           <div class="resource-detail-item__label">{{ $t('label.domain') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="block" />
-            <router-link v-if="$store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + resource.domainid + '?tab=details' }">{{ resource.domain || resource.domainid }}</router-link>
+            <router-link v-if="!isStatic && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + resource.domainid + '?tab=details' }">{{ resource.domain || resource.domainid }}</router-link>
             <span v-else>{{ resource.domain || resource.domainid }}</span>
           </div>
         </div>
@@ -544,7 +544,7 @@
           <div class="resource-detail-item__label">{{ $t('label.management.servers') }}</div>
           <div class="resource-detail-item__details">
             <a-icon type="rocket" />
-            <router-link v-if="$router.resolve('/managementserver/' + resource.managementserverid).route.name !== '404'" :to="{ path: '/managementserver/' + resource.managementserverid }">{{ resource.managementserver || resource.managementserverid }}</router-link>
+            <router-link v-if="!isStatic && $router.resolve('/managementserver/' + resource.managementserverid).route.name !== '404'" :to="{ path: '/managementserver/' + resource.managementserverid }">{{ resource.managementserver || resource.managementserverid }}</router-link>
             <span v-else>{{ resource.managementserver || resource.managementserverid }}</span>
           </div>
         </div>
@@ -607,7 +607,7 @@
         </div>
       </div>
 
-      <div class="account-center-tags" v-if="resourceType && 'listTags' in $store.getters.apis">
+      <div class="account-center-tags" v-if="!isStatic && resourceType && 'listTags' in $store.getters.apis">
         <a-divider/>
         <a-spin :spinning="loadingTags">
           <div class="title">{{ $t('label.tags') }}</div>
@@ -639,7 +639,7 @@
         </a-spin>
       </div>
 
-      <div class="account-center-team" v-if="annotationType && 'listAnnotations' in $store.getters.apis">
+      <div class="account-center-team" v-if="!isStatic && annotationType && 'listAnnotations' in $store.getters.apis">
         <a-divider :dashed="true"/>
         <a-spin :spinning="loadingAnnotations">
           <div class="title">
@@ -725,6 +725,10 @@ export default {
     bordered: {
       type: Boolean,
       default: true
+    },
+    isStatic: {
+      type: Boolean,
+      default: false
     }
   },
   data () {
diff --git a/ui/src/components/view/SearchView.vue b/ui/src/components/view/SearchView.vue
index 668783b..0c27e40 100644
--- a/ui/src/components/view/SearchView.vue
+++ b/ui/src/components/view/SearchView.vue
@@ -133,7 +133,6 @@ export default {
       default: () => {}
     }
   },
-  inject: ['parentSearch', 'parentChangeFilter'],
   data () {
     return {
       searchQuery: null,
@@ -419,7 +418,7 @@ export default {
     onSearch (value) {
       this.paramsFilter = {}
       this.searchQuery = value
-      this.parentSearch({ searchQuery: this.searchQuery })
+      this.$emit('search', { searchQuery: this.searchQuery })
     },
     onClear () {
       this.searchFilters.map(item => {
@@ -432,7 +431,7 @@ export default {
       this.inputValue = null
       this.searchQuery = null
       this.paramsFilter = {}
-      this.parentSearch(this.paramsFilter)
+      this.$emit('search', this.paramsFilter)
     },
     handleSubmit (e) {
       e.preventDefault()
@@ -455,7 +454,7 @@ export default {
             this.paramsFilter['tags[0].value'] = this.inputValue
           }
         }
-        this.parentSearch(this.paramsFilter)
+        this.$emit('search', this.paramsFilter)
       })
     },
     handleKeyChange (e) {
@@ -465,7 +464,7 @@ export default {
       this.inputValue = e.target.value
     },
     changeFilter (filter) {
-      this.parentChangeFilter(filter)
+      this.$emit('change-filter', filter)
     }
   }
 }
diff --git a/ui/src/components/widgets/Status.vue b/ui/src/components/widgets/Status.vue
index cd68103..beffeb4 100644
--- a/ui/src/components/widgets/Status.vue
+++ b/ui/src/components/widgets/Status.vue
@@ -105,6 +105,7 @@ export default {
         case 'True':
         case 'Up':
         case 'enabled':
+        case 'PowerOn':
         case 'success':
           status = 'success'
           break
@@ -116,6 +117,7 @@ export default {
         case 'Error':
         case 'False':
         case 'Stopped':
+        case 'PowerOff':
         case 'failed':
           status = 'error'
           break
diff --git a/ui/src/config/router.js b/ui/src/config/router.js
index a07f0d6..586d5a2 100644
--- a/ui/src/config/router.js
+++ b/ui/src/config/router.js
@@ -34,6 +34,7 @@ import role from '@/config/section/role'
 import infra from '@/config/section/infra'
 import offering from '@/config/section/offering'
 import config from '@/config/section/config'
+import tools from '@/config/section/tools'
 import quota from '@/config/section/plugin/quota'
 import cloudian from '@/config/section/plugin/cloudian'
 
@@ -224,6 +225,7 @@ export function asyncRouterMap () {
       generateRouterMap(infra),
       generateRouterMap(offering),
       generateRouterMap(config),
+      generateRouterMap(tools),
       generateRouterMap(quota),
       generateRouterMap(cloudian),
 
diff --git a/ui/src/config/section/tools.js b/ui/src/config/section/tools.js
new file mode 100644
index 0000000..1406e2c
--- /dev/null
+++ b/ui/src/config/section/tools.js
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+export default {
+  name: 'tools',
+  title: 'label.tools',
+  icon: 'tool',
+  permission: ['listInfrastructure'],
+  children: [
+    {
+      name: 'manageinstances',
+      title: 'label.action.import.export.instances',
+      icon: 'interaction',
+      docHelp: 'adminguide/virtual_machines.html#importing-and-unmanaging-virtual-machine',
+      resourceType: 'UserVm',
+      permission: ['listUnmanagedInstances'],
+      component: () => import('@/views/tools/ManageInstances.vue')
+    }
+  ]
+}
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index 427160f..bfd5ba1 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -79,7 +79,9 @@
             v-if="!dataView"
             :searchFilters="searchFilters"
             :searchParams="searchParams"
-            :apiName="apiName"/>
+            :apiName="apiName"
+            @search="onSearch"
+            @change-filter="changeFilter"/>
         </a-col>
       </a-row>
     </a-card>
diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue
index 68d0c1a..f229d85 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -200,9 +200,9 @@
                     ></compute-offering-selection>
                     <compute-selection
                       v-if="serviceOffering && (serviceOffering.iscustomized || serviceOffering.iscustomizediops)"
-                      cpunumber-input-decorator="cpunumber"
-                      cpuspeed-input-decorator="cpuspeed"
-                      memory-input-decorator="memory"
+                      cpuNumberInputDecorator="cpunumber"
+                      cpuSpeedInputDecorator="cpuspeed"
+                      memoryInputDecorator="memory"
                       :preFillContent="dataPreFill"
                       :computeOfferingId="instanceConfig.computeofferingid"
                       :isConstrained="'serviceofferingdetails' in serviceOffering"
diff --git a/ui/src/views/compute/ScaleVM.vue b/ui/src/views/compute/ScaleVM.vue
index a477d73..939c5e3 100644
--- a/ui/src/views/compute/ScaleVM.vue
+++ b/ui/src/views/compute/ScaleVM.vue
@@ -33,9 +33,9 @@
 
     <compute-selection
       v-if="selectedOffering && selectedOffering.iscustomized"
-      :cpunumber-input-decorator="cpuNumberKey"
-      :cpuspeed-input-decorator="cpuSpeedKey"
-      :memory-input-decorator="memoryKey"
+      :cpuNumberInputDecorator="cpuNumberKey"
+      :cpuSpeedInputDecorator="cpuSpeedKey"
+      :memoryInputDecorator="memoryKey"
       :computeOfferingId="selectedOffering.id"
       :isConstrained="'serviceofferingdetails' in selectedOffering"
       :minCpu="getMinCpu()"
diff --git a/ui/src/views/compute/wizard/ComputeSelection.vue b/ui/src/views/compute/wizard/ComputeSelection.vue
index 816e0c9..8c47c6e 100644
--- a/ui/src/views/compute/wizard/ComputeSelection.vue
+++ b/ui/src/views/compute/wizard/ComputeSelection.vue
@@ -123,11 +123,11 @@ export default {
       type: Number,
       default: 256
     },
-    cpunumberInputDecorator: {
+    cpuNumberInputDecorator: {
       type: String,
       default: ''
     },
-    cpuspeedInputDecorator: {
+    cpuSpeedInputDecorator: {
       type: String,
       default: ''
     },
@@ -219,10 +219,10 @@ export default {
       if (!this.validateInput('cpu', value)) {
         return
       }
-      this.$emit('update-compute-cpunumber', this.cpunumberInputDecorator, value)
+      this.$emit('update-compute-cpunumber', this.cpuNumberInputDecorator, value)
     },
     updateComputeCpuSpeed (value) {
-      this.$emit('update-compute-cpuspeed', this.cpuspeedInputDecorator, value)
+      this.$emit('update-compute-cpuspeed', this.cpuSpeedInputDecorator, value)
     },
     updateComputeMemory (value) {
       if (!value) this.memoryInputValue = 0
diff --git a/ui/src/views/compute/wizard/MultiDiskSelection.vue b/ui/src/views/compute/wizard/MultiDiskSelection.vue
index 630d3b4..166f4c3 100644
--- a/ui/src/views/compute/wizard/MultiDiskSelection.vue
+++ b/ui/src/views/compute/wizard/MultiDiskSelection.vue
@@ -26,16 +26,35 @@
       :rowSelection="rowSelection"
       :scroll="{ y: 225 }" >
 
-      <span slot="offering" slot-scope="text, record">
-        <a-select
-          autoFocus
-          v-if="validOfferings[record.id] && validOfferings[record.id].length > 0"
-          @change="updateOffering($event, record.id)"
-          :defaultValue="validOfferings[record.id][0].id">
-          <a-select-option v-for="offering in validOfferings[record.id]" :key="offering.id">
-            {{ offering.displaytext }}
-          </a-select-option>
-        </a-select>
+      <span slot="name" slot-scope="text, record">
+        <span>{{ record.displaytext || record.name }}</span>
+        <div v-if="record.meta">
+          <template v-for="meta in record.meta">
+            <a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
+          </template>
+        </div>
+      </span>
+      <span slot="offering" slot-scope="text, record" style="width: 50%">
+        <span
+          v-if="validOfferings[record.id] && validOfferings[record.id].length > 0">
+          <check-box-select-pair
+            v-if="selectedCustomDiskOffering!=null"
+            layout="vertical"
+            :resourceKey="record.id"
+            :selectOptions="validOfferings[record.id]"
+            :checkBoxLabel="autoSelectLabel"
+            :defaultCheckBoxValue="true"
+            :reversed="true"
+            @handle-checkselectpair-change="updateOfferingCheckPairSelect" />
+          <a-select
+            v-else
+            @change="updateOfferingSelect($event, record.id)"
+            :defaultValue="validOfferings[record.id][0].id">
+            <a-select-option v-for="offering in validOfferings[record.id]" :key="offering.id">
+              {{ offering.displaytext }}
+            </a-select-option>
+          </a-select>
+        </span>
         <span v-else>
           {{ $t('label.no.matching.offering') }}
         </span>
@@ -46,9 +65,13 @@
 
 <script>
 import { api } from '@/api'
+import CheckBoxSelectPair from '@/components/CheckBoxSelectPair'
 
 export default {
   name: 'MultiDiskSelection',
+  components: {
+    CheckBoxSelectPair
+  },
   props: {
     items: {
       type: Array,
@@ -57,6 +80,22 @@ export default {
     zoneId: {
       type: String,
       default: () => ''
+    },
+    selectionEnabled: {
+      type: Boolean,
+      default: true
+    },
+    customOfferingsAllowed: {
+      type: Boolean,
+      default: false
+    },
+    autoSelectCustomOffering: {
+      type: Boolean,
+      default: false
+    },
+    autoSelectLabel: {
+      type: String,
+      default: ''
     }
   },
   data () {
@@ -64,7 +103,8 @@ export default {
       columns: [
         {
           dataIndex: 'name',
-          title: this.$t('label.data.disk')
+          title: this.$t('label.data.disk'),
+          scopedSlots: { customRender: 'name' }
         },
         {
           dataIndex: 'offering',
@@ -76,33 +116,35 @@ export default {
       selectedRowKeys: [],
       diskOfferings: [],
       validOfferings: {},
+      selectedCustomDiskOffering: null,
       values: {}
     }
   },
   computed: {
     tableSource () {
       return this.items.map(item => {
-        return {
-          id: item.id,
-          name: `${item.name} (${item.size} GB)`,
-          disabled: this.validOfferings[item.id] && this.validOfferings[item.id].length === 0
-        }
+        var disk = { ...item, disabled: this.validOfferings[item.id] && this.validOfferings[item.id].length === 0 }
+        disk.name = `${item.name} (${item.size} GB)`
+        return disk
       })
     },
     rowSelection () {
-      return {
-        type: 'checkbox',
-        selectedRowKeys: this.selectedRowKeys,
-        getCheckboxProps: record => ({
-          props: {
-            disabled: record.disabled
+      if (this.selectionEnabled === true) {
+        return {
+          type: 'checkbox',
+          selectedRowKeys: this.selectedRowKeys,
+          getCheckboxProps: record => ({
+            props: {
+              disabled: record.disabled
+            }
+          }),
+          onChange: (rows) => {
+            this.selectedRowKeys = rows
+            this.sendValues()
           }
-        }),
-        onChange: (rows) => {
-          this.selectedRowKeys = rows
-          this.sendValues()
         }
       }
+      return null
     }
   },
   watch: {
@@ -128,7 +170,9 @@ export default {
         listall: true
       }).then(response => {
         this.diskOfferings = response.listdiskofferingsresponse.diskoffering || []
-        this.diskOfferings = this.diskOfferings.filter(x => !x.iscustomized)
+        if (!this.customOfferingsAllowed) {
+          this.diskOfferings = this.diskOfferings.filter(x => !x.iscustomized)
+        }
         this.orderDiskOfferings()
       }).finally(() => {
         this.loading = false
@@ -137,8 +181,11 @@ export default {
     orderDiskOfferings () {
       this.loading = true
       this.validOfferings = {}
+      if (this.customOfferingsAllowed && this.autoSelectCustomOffering) {
+        this.selectedCustomDiskOffering = this.diskOfferings.filter(x => x.iscustomized)?.[0]
+      }
       for (const item of this.items) {
-        this.validOfferings[item.id] = this.diskOfferings.filter(x => x.disksize >= item.size)
+        this.validOfferings[item.id] = this.diskOfferings.filter(x => x.disksize >= item.size || (this.customOfferingsAllowed && x.iscustomized))
       }
       this.setDefaultValues()
       this.loading = false
@@ -146,18 +193,31 @@ export default {
     setDefaultValues () {
       this.values = {}
       for (const item of this.items) {
-        this.values[item.id] = this.validOfferings[item.id].length > 0 ? this.validOfferings[item.id][0].id : ''
+        this.values[item.id] = this.selectedCustomDiskOffering?.id || this.validOfferings[item.id]?.[0]?.id || ''
       }
+      this.sendValues()
     },
-    updateOffering (value, templateid) {
-      this.values[templateid] = value
+    updateOfferingCheckPairSelect (diskId, checked, value) {
+      if (this.selectedCustomDiskOffering) {
+        this.values[diskId] = checked ? this.selectedCustomDiskOffering.id : value
+        this.sendValues()
+      }
+    },
+    updateOfferingSelect (value, diskId) {
+      this.values[diskId] = value
       this.sendValues()
     },
     sendValues () {
       const data = {}
-      this.selectedRowKeys.map(x => {
-        data[x] = this.values[x]
-      })
+      if (this.selectionEnabled) {
+        this.selectedRowKeys.map(x => {
+          data[x] = this.values[x]
+        })
+      } else {
+        for (var x in this.values) {
+          data[x] = this.values[x]
+        }
+      }
       this.$emit('select-multi-disk-offering', data)
     }
   }
diff --git a/ui/src/views/compute/wizard/MultiNetworkSelection.vue b/ui/src/views/compute/wizard/MultiNetworkSelection.vue
new file mode 100644
index 0000000..f94cc2b
--- /dev/null
+++ b/ui/src/views/compute/wizard/MultiNetworkSelection.vue
@@ -0,0 +1,257 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <div>
+    <a-table
+      :loading="loading"
+      :columns="columns"
+      :dataSource="tableSource"
+      :rowKey="record => record.id"
+      :pagination="false"
+      :rowSelection="rowSelection"
+      :scroll="{ y: 225 }" >
+
+      <span slot="name" slot-scope="text, record">
+        <span>{{ record.displaytext || record.name }}</span>
+        <div v-if="record.meta">
+          <template v-for="meta in record.meta">
+            <a-tag style="margin-top: 5px" :key="meta.key">{{ meta.key + ': ' + meta.value }}</a-tag>
+          </template>
+        </div>
+      </span>
+      <span slot="network" slot-scope="text, record">
+        <a-select
+          v-if="validNetworks[record.id] && validNetworks[record.id].length > 0"
+          :defaultValue="validNetworks[record.id][0].id"
+          @change="val => handleNetworkChange(record, val)">
+          <a-select-option v-for="network in validNetworks[record.id]" :key="network.id">
+            {{ network.displaytext + (network.broadcasturi ? ' (' + network.broadcasturi + ')' : '') }}
+          </a-select-option>
+        </a-select>
+        <span v-else>
+          {{ $t('label.no.matching.network') }}
+        </span>
+      </span>
+      <span slot="ipaddress" slot-scope="text, record">
+        <check-box-input-pair
+          layout="vertical"
+          :resourceKey="record.id"
+          :checkBoxLabel="$t('label.auto.assign.random.ip')"
+          :defaultCheckBoxValue="true"
+          :reversed="true"
+          :visible="(indexNum > 0 && ipAddressesEnabled[record.id])"
+          @handle-checkinputpair-change="setIpAddress" />
+      </span>
+    </a-table>
+  </div>
+</template>
+
+<script>
+import { api } from '@/api'
+import _ from 'lodash'
+import CheckBoxInputPair from '@/components/CheckBoxInputPair'
+
+export default {
+  name: 'MultiDiskSelection',
+  components: {
+    CheckBoxInputPair
+  },
+  props: {
+    items: {
+      type: Array,
+      default: () => []
+    },
+    zoneId: {
+      type: String,
+      default: () => ''
+    },
+    selectionEnabled: {
+      type: Boolean,
+      default: true
+    },
+    filterUnimplementedNetworks: {
+      type: Boolean,
+      default: false
+    },
+    filterMatchKey: {
+      type: String,
+      default: null
+    }
+  },
+  data () {
+    return {
+      columns: [
+        {
+          dataIndex: 'name',
+          title: this.$t('label.nic'),
+          scopedSlots: { customRender: 'name' }
+        },
+        {
+          dataIndex: 'network',
+          title: this.$t('label.network'),
+          scopedSlots: { customRender: 'network' }
+        },
+        {
+          dataIndex: 'ipaddress',
+          title: this.$t('label.ipaddress'),
+          scopedSlots: { customRender: 'ipaddress' }
+        }
+      ],
+      loading: false,
+      selectedRowKeys: [],
+      networks: [],
+      validNetworks: {},
+      values: {},
+      ipAddressesEnabled: {},
+      ipAddresses: {},
+      indexNum: 1,
+      sendValuesTimer: null
+    }
+  },
+  computed: {
+    tableSource () {
+      return this.items.map(item => {
+        var nic = { ...item, disabled: this.validNetworks[item.id] && this.validNetworks[item.id].length === 0 }
+        nic.name = item.displaytext || item.name
+        return nic
+      })
+    },
+    rowSelection () {
+      if (this.selectionEnabled === true) {
+        return {
+          type: 'checkbox',
+          selectedRowKeys: this.selectedRowKeys,
+          getCheckboxProps: record => ({
+            props: {
+              disabled: record.disabled
+            }
+          }),
+          onChange: (rows) => {
+            this.selectedRowKeys = rows
+            this.sendValues()
+          }
+        }
+      }
+      return null
+    }
+  },
+  watch: {
+    items (newData, oldData) {
+      this.items = newData
+      this.selectedRowKeys = []
+      this.fetchNetworks()
+    },
+    zoneId (newData) {
+      this.zoneId = newData
+      this.fetchNetworks()
+    }
+  },
+  created () {
+    this.fetchNetworks()
+  },
+  methods: {
+    fetchNetworks () {
+      this.networks = []
+      if (!this.zoneId || this.zoneId.length === 0) {
+        return
+      }
+      this.loading = true
+      api('listNetworks', {
+        zoneid: this.zoneId,
+        listall: true
+      }).then(response => {
+        this.networks = response.listnetworksresponse.network || []
+        this.orderNetworks()
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+    orderNetworks () {
+      this.loading = true
+      this.validNetworks = {}
+      for (const item of this.items) {
+        this.validNetworks[item.id] = this.networks
+        if (this.filterUnimplementedNetworks) {
+          this.validNetworks[item.id] = this.validNetworks[item.id].filter(x => x.state === 'Implemented')
+        }
+        if (this.filterMatchKey) {
+          this.validNetworks[item.id] = this.validNetworks[item.id].filter(x => x[this.filterMatchKey] === item[this.filterMatchKey])
+        }
+      }
+      this.setDefaultValues()
+      this.loading = false
+    },
+    setIpAddressEnabled (nic, network) {
+      this.ipAddressesEnabled[nic.id] = network && network.type !== 'L2'
+      this.indexNum = (this.indexNum % 2) + 1
+    },
+    setIpAddress (nicId, autoAssign, ipAddress) {
+      this.ipAddresses[nicId] = autoAssign ? 'auto' : ipAddress
+      this.sendValuesTimed()
+    },
+    setDefaultValues () {
+      this.values = {}
+      this.ipAddresses = {}
+      for (const item of this.items) {
+        var network = this.validNetworks[item.id]?.[0] || null
+        this.values[item.id] = network ? network.id : ''
+        this.ipAddresses[item.id] = (!network || network.type === 'L2') ? null : 'auto'
+        this.setIpAddressEnabled(item, network)
+      }
+      this.sendValuesTimed()
+    },
+    handleNetworkChange (nic, networkId) {
+      this.setIpAddressEnabled(nic, _.find(this.validNetworks[nic.id], (option) => option.id === networkId))
+      this.sendValuesTimed()
+    },
+    sendValuesTimed () {
+      clearTimeout(this.sendValuesTimer)
+      this.sendValuesTimer = setTimeout(() => {
+        this.sendValues(this.selectedScope)
+      }, 500)
+    },
+    sendValues () {
+      const data = {}
+      if (this.selectionEnabled) {
+        this.selectedRowKeys.map(x => {
+          var d = { network: this.values[x] }
+          if (this.ipAddresses[x]) {
+            d.ipAddress = this.ipAddresses[x]
+          }
+          data[x] = d
+        })
+      } else {
+        for (var x in this.values) {
+          var d = { network: this.values[x] }
+          if (this.ipAddresses[x] != null && this.ipAddresses[x] !== undefined) {
+            d.ipAddress = this.ipAddresses[x]
+          }
+          data[x] = d
+        }
+      }
+      this.$emit('select-multi-network', data)
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .ant-table-wrapper {
+    margin: 2rem 0;
+  }
+</style>
diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue
index f304cb5..852a939 100644
--- a/ui/src/views/offering/AddNetworkOffering.vue
+++ b/ui/src/views/offering/AddNetworkOffering.vue
@@ -174,7 +174,7 @@
                   :checkBoxDecorator="'service.' + item.name"
                   :selectOptions="item.provider"
                   :selectDecorator="item.name + '.provider'"
-                  @handle-checkpair-change="handleSupportedServiceChange"/>
+                  @handle-checkselectpair-change="handleSupportedServiceChange"/>
               </a-list-item>
             </a-list>
           </div>
diff --git a/ui/src/views/offering/AddVpcOffering.vue b/ui/src/views/offering/AddVpcOffering.vue
index b222473..ed0e1e4 100644
--- a/ui/src/views/offering/AddVpcOffering.vue
+++ b/ui/src/views/offering/AddVpcOffering.vue
@@ -51,7 +51,7 @@
                   :checkBoxDecorator="'service.' + item.name"
                   :selectOptions="item.provider"
                   :selectDecorator="item.name + '.provider'"
-                  @handle-checkpair-change="handleSupportedServiceChange"/>
+                  @handle-checkselectpair-change="handleSupportedServiceChange"/>
               </a-list-item>
             </a-list>
           </div>
diff --git a/ui/src/views/tools/ImportUnmanagedInstance.vue b/ui/src/views/tools/ImportUnmanagedInstance.vue
new file mode 100644
index 0000000..c0b7662
--- /dev/null
+++ b/ui/src/views/tools/ImportUnmanagedInstance.vue
@@ -0,0 +1,739 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <div>
+    <a-spin :spinning="loading">
+      <a-row :gutter="12">
+        <a-col :md="24" :lg="7">
+          <info-card
+            class="vm-info-card"
+            :isStatic="true"
+            :resource="resource"
+            :title="this.$t('label.unmanaged.instance')" />
+        </a-col>
+        <a-col :md="24" :lg="17">
+          <a-card :bordered="true">
+            <a-form
+              :form="form"
+              @submit="handleSubmit"
+              layout="vertical">
+              <a-form-item>
+                <tooltip-label slot="label" :title="$t('label.displayname')" :tooltip="apiParams.displayname.description"/>
+                <a-input
+                  v-decorator="['displayname', {
+                    rules: [{ required: true, message: $t('message.error.input.value') }]
+                  }]"
+                  :placeholder="apiParams.displayname.description"
+                  ref="displayname"
+                  autoFocus />
+              </a-form-item>
+              <a-form-item>
+                <tooltip-label slot="label" :title="$t('label.hostnamelabel')" :tooltip="apiParams.hostname.description"/>
+                <a-input
+                  v-decorator="['hostname', {}]"
+                  :placeholder="apiParams.hostname.description" />
+              </a-form-item>
+              <a-form-item>
+                <tooltip-label slot="label" :title="$t('label.domainid')" :tooltip="apiParams.domainid.description"/>
+                <a-select
+                  v-decorator="['domainid', {}]"
+                  showSearch
+                  optionFilterProp="children"
+                  :filterOption="(input, option) => {
+                    return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  }"
+                  :options="domainSelectOptions"
+                  :loading="optionsLoading.domains"
+                  :placeholder="apiParams.domainid.description"
+                  @change="val => { this.selectedDomainId = val }" />
+              </a-form-item>
+              <a-form-item v-if="selectedDomainId">
+                <tooltip-label slot="label" :title="$t('label.account')" :tooltip="apiParams.account.description"/>
+                <a-input
+                  v-decorator="['account', {}]"
+                  :placeholder="apiParams.account.description"/>
+              </a-form-item>
+              <a-form-item>
+                <tooltip-label slot="label" :title="$t('label.project')" :tooltip="apiParams.projectid.description"/>
+                <a-select
+                  v-decorator="['projectid', {}]"
+                  showSearch
+                  optionFilterProp="children"
+                  :filterOption="(input, option) => {
+                    return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  }"
+                  :options="projectSelectOptions"
+                  :loading="optionsLoading.projects"
+                  :placeholder="apiParams.projectid.description" />
+              </a-form-item>
+              <a-form-item>
+                <tooltip-label slot="label" :title="$t('label.templatename')" :tooltip="apiParams.templateid.description + '. ' + $t('message.template.import.vm.temporary')"/>
+                <a-radio-group
+                  style="width:100%"
+                  :value="templateType"
+                  @change="changeTemplateType">
+                  <a-row :gutter="12">
+                    <a-col :md="24" :lg="12">
+                      <a-radio value="auto">
+                        {{ $t('label.template.temporary.import') }}
+                      </a-radio>
+                    </a-col>
+                    <a-col :md="24" :lg="12">
+                      <a-radio value="custom">
+                        {{ $t('label.template.select.existing') }}
+                      </a-radio>
+                      <a-select
+                        :disabled="templateType === 'auto'"
+                        style="margin-top:10px"
+                        v-decorator="['templateid', {
+                          rules: [{ required: templateType !== 'auto', message: $t('message.error.input.value') }]
+                        }]"
+                        showSearch
+                        optionFilterProp="children"
+                        :filterOption="(input, option) => {
+                          return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }"
+                        :options="templateSelectOptions"
+                        :loading="optionsLoading.templates"
+                        :placeholder="apiParams.templateid.description" />
+                    </a-col>
+                  </a-row>
+                </a-radio-group>
+              </a-form-item>
+              <a-form-item>
+                <tooltip-label slot="label" :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/>
+              </a-form-item>
+              <compute-offering-selection
+                :compute-items="computeOfferings"
+                :loading="computeOfferingLoading"
+                :rowCount="totalComputeOfferings"
+                :value="computeOffering ? computeOffering.id : ''"
+                :minimumCpunumber="isVmRunning ? resource.cpunumber : null"
+                :minimumCpuspeed="isVmRunning ? resource.cpuspeed : null"
+                :minimumMemory="isVmRunning ? resource.memory : null"
+                size="small"
+                @select-compute-item="($event) => updateComputeOffering($event)"
+                @handle-search-filter="($event) => fetchComputeOfferings($event)" />
+              <compute-selection
+                class="row-element"
+                v-if="computeOffering && computeOffering.iscustomized"
+                :cpuNumberInputDecorator="cpuNumberKey"
+                :cpuSpeedInputDecorator="cpuSpeedKey"
+                :memoryInputDecorator="memoryKey"
+                :computeOfferingId="computeOffering.id"
+                :preFillContent="this.resource"
+                :isConstrained="'serviceofferingdetails' in computeOffering"
+                :minCpu="getMinCpu()"
+                :maxCpu="getMaxCpu()"
+                :minMemory="getMinMemory()"
+                :maxMemory="getMaxMemory()"
+                @update-compute-cpunumber="updateFieldValue"
+                @update-compute-cpuspeed="updateFieldValue"
+                @update-compute-memory="updateFieldValue" />
+              <div v-if="resource.disk && resource.disk.length > 1">
+                <a-form-item>
+                  <tooltip-label slot="label" :title="$t('label.disk.selection')" :tooltip="apiParams.datadiskofferinglist.description"/>
+                </a-form-item>
+                <a-form-item :title="$t('label.rootdisk')">
+                  <a-select
+                    v-decorator="['rootdiskid', {
+                      rules: [{ required: true, message: $t('message.error.input.value'), }],
+                      initialValue: 0
+                    }]"
+                    defaultActiveFirstOption
+                    showSearch
+                    optionFilterProp="children"
+                    :filterOption="(input, option) => {
+                      return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }"
+                    @change="val => { selectedRootDiskIndex = val }">
+                    <a-select-option v-for="(opt, optIndex) in resource.disk" :key="optIndex">
+                      {{ opt.label || opt.id }}
+                    </a-select-option>
+                  </a-select>
+                </a-form-item>
+                <multi-disk-selection
+                  :items="dataDisks"
+                  :zoneId="cluster.zoneid"
+                  :selectionEnabled="false"
+                  :customOfferingsAllowed="true"
+                  :autoSelectCustomOffering="true"
+                  :autoSelectLabel="$t('label.auto.assign.diskoffering.disk.size')"
+                  @select-multi-disk-offering="updateMultiDiskOffering" />
+              </div>
+              <div v-if="resource.nic && resource.nic.length > 0">
+                <a-form-item>
+                  <tooltip-label slot="label" :title="$t('label.network.selection')" :tooltip="apiParams.nicnetworklist.description"/>
+                  <span>{{ $t('message.ip.address.changes.effect.after.vm.restart') }}</span>
+                </a-form-item>
+                <multi-network-selection
+                  :items="nics"
+                  :zoneId="cluster.zoneid"
+                  :selectionEnabled="false"
+                  :filterUnimplementedNetworks="true"
+                  filterMatchKey="broadcasturi"
+                  @select-multi-network="updateMultiNetworkOffering" />
+              </div>
+              <a-row :gutter="12">
+                <a-col :md="24" :lg="12">
+                  <a-form-item>
+                    <tooltip-label slot="label" :title="$t('label.migrate.allowed')" :tooltip="apiParams.migrateallowed.description"/>
+                    <a-switch v-decorator="['migrateallowed', {initialValue: this.switches.migrateAllowed}]" :checked="this.switches.migrateAllowed" @change="val => { this.switches.migrateAllowed = val }" />
+                  </a-form-item>
+                </a-col>
+                <a-col :md="24" :lg="12">
+                  <a-form-item>
+                    <tooltip-label slot="label" :title="$t('label.forced')" :tooltip="apiParams.forced.description"/>
+                    <a-switch v-decorator="['forced', {initialValue: this.switches.forced}]" :checked="this.switches.forced" @change="val => { this.switches.forced = val }" />
+                  </a-form-item>
+                </a-col>
+              </a-row>
+              <div :span="24" class="action-button">
+                <a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
+                <a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
+              </div>
+            </a-form>
+          </a-card>
+        </a-col>
+      </a-row>
+    </a-spin>
+  </div>
+</template>
+
+<script>
+import { api } from '@/api'
+import _ from 'lodash'
+import InfoCard from '@/components/view/InfoCard'
+import TooltipLabel from '@/components/widgets/TooltipLabel'
+import ComputeOfferingSelection from '@views/compute/wizard/ComputeOfferingSelection'
+import ComputeSelection from '@views/compute/wizard/ComputeSelection'
+import MultiDiskSelection from '@views/compute/wizard/MultiDiskSelection'
+import MultiNetworkSelection from '@views/compute/wizard/MultiNetworkSelection'
+
+export default {
+  name: 'ImportUnmanagedInstances',
+  components: {
+    InfoCard,
+    TooltipLabel,
+    ComputeOfferingSelection,
+    ComputeSelection,
+    MultiDiskSelection,
+    MultiNetworkSelection
+  },
+  props: {
+    cluster: {
+      type: Object,
+      required: true
+    },
+    resource: {
+      type: Object,
+      required: true
+    },
+    isOpen: {
+      type: Boolean,
+      required: false
+    }
+  },
+  data () {
+    return {
+      options: {
+        domains: [],
+        projects: [],
+        templates: []
+      },
+      rowCount: {},
+      optionsLoading: {
+        domains: false,
+        projects: false,
+        templates: false
+      },
+      domains: [],
+      domainLoading: false,
+      selectedDomainId: null,
+      templates: [],
+      templateLoading: false,
+      templateType: 'auto',
+      totalComputeOfferings: 0,
+      computeOfferings: [],
+      computeOfferingLoading: false,
+      computeOffering: {},
+      selectedRootDiskIndex: 0,
+      dataDisksOfferingsMapping: {},
+      nicsNetworksMapping: {},
+      cpuNumberKey: 'cpuNumber',
+      cpuSpeedKey: 'cpuSpeed',
+      memoryKey: 'memory',
+      switches: {},
+      loading: false
+    }
+  },
+  beforeCreate () {
+    this.form = this.$form.createForm(this)
+    this.apiConfig = this.$store.getters.apis.importUnmanagedInstance || {}
+    this.apiParams = {}
+    this.apiConfig.params.forEach(param => {
+      this.apiParams[param.name] = param
+    })
+  },
+  created () {
+    this.fetchData()
+    this.form.getFieldDecorator('computeofferingid', { initialValue: undefined, preserve: true })
+    this.form.getFieldDecorator(this.cpuNumberKey, { initialValue: undefined, preserve: true })
+    this.form.getFieldDecorator(this.cpuSpeedKey, { initialValue: undefined, preserve: true })
+    this.form.getFieldDecorator(this.memoryKey, { initialValue: undefined, preserve: true })
+  },
+  computed: {
+    params () {
+      return {
+        domains: {
+          list: 'listDomains',
+          isLoad: true,
+          field: 'domainid',
+          options: {
+            details: 'min'
+          }
+        },
+        pods: {
+          list: 'listProjects',
+          isLoad: true,
+          field: 'projectid',
+          options: {
+            details: 'min'
+          }
+        },
+        templates: {
+          list: 'listTemplates',
+          isLoad: true,
+          options: {
+            templatefilter: 'all',
+            hypervisor: this.cluster.hypervisortype
+          },
+          field: 'templateid'
+        }
+      }
+    },
+    isVmRunning () {
+      if (this.resource && this.resource.powerstate === 'PowerOn') {
+        return true
+      }
+      return false
+    },
+    domainSelectOptions () {
+      var domains = this.options.domains.map((domain) => {
+        return {
+          label: domain.path || domain.name,
+          value: domain.id
+        }
+      })
+      domains.unshift({
+        label: '',
+        value: null
+      })
+      return domains
+    },
+    projectSelectOptions () {
+      var projects = this.options.projects.map((project) => {
+        return {
+          label: project.name,
+          value: project.id
+        }
+      })
+      projects.unshift({
+        label: '',
+        value: null
+      })
+      return projects
+    },
+    templateSelectOptions () {
+      return this.options.templates.map((template) => {
+        return {
+          label: template.name,
+          value: template.id
+        }
+      })
+    },
+    dataDisks () {
+      var disks = []
+      if (this.resource.disk && this.resource.disk.length > 1) {
+        for (var index = 0; index < this.resource.disk.length; ++index) {
+          if (index !== this.selectedRootDiskIndex) {
+            var disk = { ...this.resource.disk[index] }
+            disk.size = disk.capacity / (1024 * 1024 * 1024)
+            disk.name = disk.label
+            disk.meta = this.getMeta(disk, { controller: 'controller', datastorename: 'datastore', position: 'position' })
+            disks.push(disk)
+          }
+        }
+      }
+      return disks
+    },
+    nics () {
+      var nics = []
+      if (this.resource.nic && this.resource.nic.length > 0) {
+        for (var nicEntry of this.resource.nic) {
+          var nic = { ...nicEntry }
+          nic.name = nic.name || nic.id
+          nic.displaytext = nic.name
+          if (nic.vlanid) {
+            nic.broadcasturi = 'vlan://' + nic.vlanid
+            if (nic.isolatedpvlan) {
+              nic.broadcasturi = 'pvlan://' + nic.vlanid + '-i' + nic.isolatedpvlan
+            }
+          }
+          nic.meta = this.getMeta(nic, { macaddress: 'mac', vlanid: 'vlan', networkname: 'network' })
+          nics.push(nic)
+        }
+      }
+      return nics
+    }
+  },
+  watch: {
+    isOpen (newValue) {
+      if (newValue) {
+        this.resetForm()
+        this.$refs.displayname.focus()
+        this.selectMatchingComputeOffering()
+      }
+    }
+  },
+  methods: {
+    fetchData () {
+      _.each(this.params, (param, name) => {
+        if (param.isLoad) {
+          this.fetchOptions(param, name)
+        }
+      })
+      this.fetchComputeOfferings({
+        keyword: '',
+        pageSize: 10,
+        page: 1
+      })
+    },
+    getMeta (obj, metaKeys) {
+      var meta = []
+      for (var key in metaKeys) {
+        if (key in obj) {
+          meta.push({ key: metaKeys[key], value: obj[key] })
+        }
+      }
+      return meta
+    },
+    getMinCpu () {
+      if (this.isVmRunning) {
+        return this.resource.cpunumber
+      }
+      return 'serviceofferingdetails' in this.computeOffering ? this.computeOffering.serviceofferingdetails.mincpunumber * 1 : 1
+    },
+    getMinMemory () {
+      if (this.isVmRunning) {
+        return this.resource.memory
+      }
+      return 'serviceofferingdetails' in this.computeOffering ? this.computeOffering.serviceofferingdetails.minmemory * 1 : 32
+    },
+    getMaxCpu () {
+      if (this.isVmRunning) {
+        return this.resource.cpunumber
+      }
+      return 'serviceofferingdetails' in this.computeOffering ? this.computeOffering.serviceofferingdetails.maxcpunumber * 1 : Number.MAX_SAFE_INTEGER
+    },
+    getMaxMemory () {
+      if (this.isVmRunning) {
+        return this.resource.memory
+      }
+      return 'serviceofferingdetails' in this.computeOffering ? this.computeOffering.serviceofferingdetails.maxmemory * 1 : Number.MAX_SAFE_INTEGER
+    },
+    fetchOptions (param, name, exclude) {
+      if (exclude && exclude.length > 0) {
+        if (exclude.includes(name)) {
+          return
+        }
+      }
+      this.optionsLoading[name] = true
+      param.loading = true
+      param.opts = []
+      const options = param.options || {}
+      if (!('listall' in options)) {
+        options.listall = true
+      }
+      api(param.list, options).then((response) => {
+        param.loading = false
+        _.map(response, (responseItem, responseKey) => {
+          if (Object.keys(responseItem).length === 0) {
+            this.rowCount[name] = 0
+            this.options[name] = []
+            this.$forceUpdate()
+            return
+          }
+          if (!responseKey.includes('response')) {
+            return
+          }
+          _.map(responseItem, (response, key) => {
+            if (key === 'count') {
+              this.rowCount[name] = response
+              return
+            }
+            param.opts = response
+            this.options[name] = response
+
+            this.$forceUpdate()
+          })
+        })
+      }).catch(function (error) {
+        console.log(error.stack)
+        param.loading = false
+      }).finally(() => {
+        this.optionsLoading[name] = false
+      })
+    },
+    fetchComputeOfferings (options) {
+      this.computeOfferingLoading = true
+      this.totalComputeOfferings = 0
+      this.computeOfferings = []
+      this.offeringsMap = []
+      api('listServiceOfferings', {
+        keyword: options.keyword,
+        page: options.page,
+        pageSize: options.pageSize,
+        details: 'min',
+        response: 'json'
+      }).then(response => {
+        this.totalComputeOfferings = response.listserviceofferingsresponse.count
+        if (this.totalComputeOfferings === 0) {
+          return
+        }
+        this.computeOfferings = response.listserviceofferingsresponse.serviceoffering
+        this.computeOfferings.map(i => { this.offeringsMap[i.id] = i })
+      }).finally(() => {
+        this.computeOfferingLoading = false
+        this.selectMatchingComputeOffering()
+      })
+    },
+    updateFieldValue (name, value) {
+      if (name === this.cpuNumberKey) {
+      }
+      this.form.setFieldsValue({
+        [name]: value
+      })
+    },
+    updateComputeOffering (id) {
+      this.updateFieldValue('computeofferingid', id)
+      this.computeOffering = this.computeOfferings.filter(x => x.id === id)[0]
+    },
+    updateMultiDiskOffering (data) {
+      this.dataDisksOfferingsMapping = data
+    },
+    updateMultiNetworkOffering (data) {
+      this.nicsNetworksMapping = data
+    },
+    changeTemplateType (e) {
+      this.templateType = e.target.value
+      if (this.templateType === 'auto') {
+        this.updateFieldValue('templateid', undefined)
+      }
+    },
+    selectMatchingComputeOffering () {
+      var offerings = [...this.computeOfferings]
+      offerings.sort(function (a, b) {
+        return a.cpunumber - b.cpunumber
+      })
+      for (var offering of offerings) {
+        var cpuNumberMatches = false
+        var cpuSpeedMatches = false
+        var memoryMatches = false
+        if (!offering.iscustomized) {
+          cpuNumberMatches = offering.cpunumber === this.resource.cpunumber
+          cpuSpeedMatches = !this.resource.cpuspeed || offering.cpuspeed === this.resource.cpuspeed
+          memoryMatches = offering.memory === this.resource.memory
+        } else {
+          cpuNumberMatches = cpuSpeedMatches = memoryMatches = true
+          if (offering.serviceofferingdetails) {
+            cpuNumberMatches = (this.resource.cpunumber >= offering.serviceofferingdetails.mincpunumber &&
+              this.resource.cpunumber <= offering.serviceofferingdetails.maxcpunumber)
+            memoryMatches = (this.resource.memory >= offering.serviceofferingdetails.minmemory &&
+              this.resource.memory <= offering.serviceofferingdetails.maxmemory)
+            cpuSpeedMatches = !this.resource.cpuspeed || offering.cpuspeed === this.resource.cpuspeed
+          }
+        }
+        if (cpuNumberMatches && cpuSpeedMatches && memoryMatches) {
+          setTimeout(() => {
+            this.updateComputeOffering(offering.id)
+          }, 250)
+          break
+        }
+      }
+    },
+    handleSubmit (e) {
+      e.preventDefault()
+      this.form.validateFields((err, values) => {
+        if (err) {
+          return
+        }
+        const params = {
+          name: this.resource.name,
+          clusterid: this.cluster.id,
+          displayname: values.displayname
+        }
+        if (!this.computeOffering || !this.computeOffering.id) {
+          this.$notification.error({
+            message: this.$t('message.request.failed'),
+            description: this.$t('message.step.2.continue')
+          })
+          return
+        }
+        params.serviceofferingid = values.computeofferingid
+        if (this.computeOffering.iscustomized) {
+          var details = [this.cpuNumberKey, this.cpuSpeedKey, this.memoryKey]
+          for (var detail of details) {
+            if (!(values[detail] || this.computeOffering[detail])) {
+              this.$notification.error({
+                message: this.$t('message.request.failed'),
+                description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.' + detail.toLowerCase())
+              })
+              return
+            }
+            if (values[detail]) {
+              params['details[0].' + detail] = values[detail]
+            }
+          }
+        }
+        var keys = ['hostname', 'domainid', 'projectid', 'account', 'migrateallowed', 'forced']
+        if (this.templateType !== 'auto') {
+          keys.push('templateid')
+        }
+        for (var key of keys) {
+          if (values[key]) {
+            params[key] = values[key]
+          }
+        }
+        var diskOfferingIndex = 0
+        for (var diskId in this.dataDisksOfferingsMapping) {
+          if (!this.dataDisksOfferingsMapping[diskId]) {
+            this.$notification.error({
+              message: this.$t('message.request.failed'),
+              description: this.$t('message.select.disk.offering') + ': ' + diskId
+            })
+            return
+          }
+          params['datadiskofferinglist[' + diskOfferingIndex + '].disk'] = diskId
+          params['datadiskofferinglist[' + diskOfferingIndex + '].diskOffering'] = this.dataDisksOfferingsMapping[diskId]
+          diskOfferingIndex++
+        }
+        var nicNetworkIndex = 0
+        var nicIpIndex = 0
+        for (var nicId in this.nicsNetworksMapping) {
+          if (!this.nicsNetworksMapping[nicId].network) {
+            this.$notification.error({
+              message: this.$t('message.request.failed'),
+              description: this.$t('message.select.nic.network') + ': ' + nicId
+            })
+            return
+          }
+          params['nicnetworklist[' + nicNetworkIndex + '].nic'] = nicId
+          params['nicnetworklist[' + nicNetworkIndex + '].network'] = this.nicsNetworksMapping[nicId].network
+          nicNetworkIndex++
+          if ('ipAddress' in this.nicsNetworksMapping[nicId]) {
+            if (!this.nicsNetworksMapping[nicId].ipAddress) {
+              this.$notification.error({
+                message: this.$t('message.request.failed'),
+                description: this.$t('message.enter.valid.nic.ip') + ': ' + nicId
+              })
+              return
+            }
+            params['nicipaddresslist[' + nicIpIndex + '].nic'] = nicId
+            params['nicipaddresslist[' + nicIpIndex + '].ip4Address'] = this.nicsNetworksMapping[nicId].ipAddress
+            nicIpIndex++
+          }
+        }
+        this.updateLoading(true)
+        const name = this.resource.name
+        api('importUnmanagedInstance', params).then(json => {
+          const jobId = json.importunmanagedinstanceresponse.jobid
+          this.$store.dispatch('AddAsyncJob', {
+            title: this.$t('label.import.instance'),
+            jobid: jobId,
+            description: name,
+            status: 'progress'
+          })
+          this.$pollJob({
+            jobId,
+            loadingMessage: `${this.$t('label.import.instance')} ${name} ${this.$t('label.in.progress')}`,
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            successMessage: this.$t('message.success.import.instance') + ' ' + name,
+            successMethod: result => {
+              this.$emit('refresh-data')
+            }
+          })
+          this.closeAction()
+        }).catch(error => {
+          this.$notifyError(error)
+        }).finally(() => {
+          this.updateLoading(false)
+        })
+      })
+    },
+    updateLoading (value) {
+      this.loading = value
+      this.$emit('loading-changed', value)
+    },
+    resetForm () {
+      var fields = ['displayname', 'hostname', 'domainid', 'account', 'projectid', 'computeofferingid']
+      for (var field of fields) {
+        this.updateFieldValue(field, undefined)
+      }
+      this.templateType = 'auto'
+      this.updateComputeOffering(undefined)
+      this.switches = {}
+    },
+    closeAction () {
+      this.$emit('close-action')
+    }
+  }
+}
+</script>
+
+<style lang="less">
+  @import url('../../style/index');
+  .ant-table-selection-column {
+    // Fix for the table header if the row selection use radio buttons instead of checkboxes
+    > div:empty {
+      width: 16px;
+    }
+  }
+
+  .ant-collapse-borderless > .ant-collapse-item {
+    border: 1px solid @border-color-split;
+    border-radius: @border-radius-base !important;
+    margin: 0 0 1.2rem;
+  }
+
+  .form-layout {
+    width: 120vw;
+
+    @media (min-width: 1000px) {
+      width: 550px;
+    }
+  }
+
+  .action-button {
+    text-align: right;
+
+    button {
+      margin-right: 5px;
+    }
+  }
+</style>
diff --git a/ui/src/views/tools/ManageInstances.vue b/ui/src/views/tools/ManageInstances.vue
new file mode 100644
index 0000000..6bf3375
--- /dev/null
+++ b/ui/src/views/tools/ManageInstances.vue
@@ -0,0 +1,822 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+<template>
+  <a-row :gutter="12" v-if="isPageAllowed">
+    <a-col :md="24">
+      <a-card class="breadcrumb-card">
+        <a-col :md="24" style="display: flex">
+          <breadcrumb style="padding-top: 6px; padding-left: 8px" />
+          <a-button
+            style="margin-left: 12px; margin-top: 4px"
+            :loading="viewLoading"
+            icon="reload"
+            size="small"
+            shape="round"
+            @click="fetchData()" >
+            {{ $t('label.refresh') }}
+          </a-button>
+        </a-col>
+      </a-card>
+    </a-col>
+    <a-col
+      :md="24">
+      <div>
+        <a-card>
+          <a-card class="row-element"><span v-html="$t('message.desc.importexportinstancewizard')" /></a-card>
+          <a-row :gutter="18">
+            <a-form
+              style="min-width: 170px"
+              :form="form"
+              layout="vertical">
+              <a-col :md="24" :lg="8">
+                <a-form-item :label="this.$t('label.zoneid')">
+                  <a-select
+                    v-decorator="['zoneid', {}]"
+                    showSearch
+                    optionFilterProp="children"
+                    :filterOption="filterOption"
+                    :options="zoneSelectOptions"
+                    @change="onSelectZoneId"
+                    :loading="optionLoading.zones"
+                    autoFocus
+                  ></a-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="24" :lg="8">
+                <a-form-item
+                  :label="this.$t('label.podid')">
+                  <a-select
+                    v-decorator="['podid']"
+                    showSearch
+                    optionFilterProp="children"
+                    :filterOption="filterOption"
+                    :options="podSelectOptions"
+                    :loading="optionLoading.pods"
+                    @change="onSelectPodId"
+                  ></a-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="24" :lg="8">
+                <a-form-item
+                  :label="this.$t('label.clusterid')">
+                  <a-select
+                    v-decorator="['clusterid']"
+                    showSearch
+                    optionFilterProp="children"
+                    :filterOption="filterOption"
+                    :options="clusterSelectOptions"
+                    :loading="optionLoading.clusters"
+                    @change="onSelectClusterId"
+                  ></a-select>
+                </a-form-item>
+              </a-col>
+            </a-form>
+          </a-row>
+          <a-divider />
+          <a-row :gutter="12" style="display: flex">
+            <a-col :md="24" :lg="12">
+              <a-card class="instances-card">
+                <span slot="title">
+                  {{ $t('label.unmanaged.instances') }}
+                  <a-tooltip :title="$t('message.instances.unmanaged')">
+                    <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+                  </a-tooltip>
+                  <a-button
+                    style="margin-left: 12px; margin-top: 4px"
+                    :loading="unmanagedInstancesLoading"
+                    icon="reload"
+                    size="small"
+                    shape="round"
+                    @click="fetchUnmanagedInstances()" >
+                  </a-button>
+                  <span style="margin-left: 12px">
+                    <search-view
+                      :searchFilters="searchFilters.unmanaged"
+                      :searchParams="searchParams.unmanaged"
+                      :apiName="listInstancesApi.unmanaged"
+                      @search="searchUnmanagedInstances"
+                    />
+                  </span>
+                </span>
+                <a-table
+                  class="instances-card-table"
+                  :loading="unmanagedInstancesLoading"
+                  :rowSelection="unmanagedInstanceSelection"
+                  :rowKey="(record, index) => index"
+                  :columns="unmanagedInstancesColumns"
+                  :data-source="unmanagedInstances"
+                  :pagination="false"
+                  size="middle"
+                  :rowClassName="getRowClassName"
+                >
+                  <template slot="state" slot-scope="text">
+                    <status :text="text ? text : ''" displayText />
+                  </template>
+                </a-table>
+                <div class="instances-card-footer">
+                  <a-pagination
+                    class="row-element"
+                    size="small"
+                    :current="page.unmanaged"
+                    :pageSize="pageSize.unmanaged"
+                    :total="itemCount.unmanaged"
+                    :showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page.unmanaged-1)*pageSize.unmanaged))}-${Math.min(page.unmanaged*pageSize.unmanaged, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
+                    @change="fetchUnmanagedInstances"
+                    showQuickJumper>
+                    <template slot="buildOptionText" slot-scope="props">
+                      <span>{{ props.value }} / {{ $t('label.page') }}</span>
+                    </template>
+                  </a-pagination>
+                  <div :span="24" class="action-button-left">
+                    <a-button
+                      :loading="importUnmanagedInstanceLoading"
+                      :disabled="!(('importUnmanagedInstance' in $store.getters.apis) && unmanagedInstancesSelectedRowKeys.length > 0)"
+                      type="primary"
+                      icon="import"
+                      @click="onManageInstanceAction">
+                      {{ $t('label.import.instance') }}
+                    </a-button>
+                  </div>
+                </div>
+              </a-card>
+            </a-col>
+            <a-col :md="24" :lg="12">
+              <a-card class="instances-card">
+                <span slot="title">
+                  {{ $t('label.managed.instances') }}
+                  <a-tooltip :title="$t('message.instances.managed')">
+                    <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+                  </a-tooltip>
+                  <a-button
+                    style="margin-left: 12px; margin-top: 4px"
+                    :loading="managedInstancesLoading"
+                    icon="reload"
+                    size="small"
+                    shape="round"
+                    @click="fetchManagedInstances()" >
+                  </a-button>
+                  <span style="margin-left: 12px">
+                    <search-view
+                      :searchFilters="searchFilters.managed"
+                      :searchParams="searchParams.managed"
+                      :apiName="listInstancesApi.managed"
+                      @search="searchManagedInstances"
+                    />
+                  </span>
+                </span>
+                <a-table
+                  class="instances-card-table"
+                  :loading="managedInstancesLoading"
+                  :rowSelection="managedInstanceSelection"
+                  :rowKey="(record, index) => index"
+                  :columns="managedInstancesColumns"
+                  :data-source="managedInstances"
+                  :pagination="false"
+                  size="middle"
+                  :rowClassName="getRowClassName"
+                >
+                  <a slot="name" slot-scope="text, record" href="javascript:;">
+                    <router-link :to="{ path: '/vm/' + record.id }">{{ text }}</router-link>
+                  </a>
+                  <template slot="state" slot-scope="text">
+                    <status :text="text ? text : ''" displayText />
+                  </template>
+                </a-table>
+                <div class="instances-card-footer">
+                  <a-pagination
+                    class="row-element"
+                    size="small"
+                    :current="page.managed"
+                    :pageSize="pageSize.managed"
+                    :total="itemCount.managed"
+                    :showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page.managed-1)*pageSize.managed))}-${Math.min(page.managed*pageSize.managed, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
+                    @change="fetchManagedInstances"
+                    showQuickJumper>
+                    <template slot="buildOptionText" slot-scope="props">
+                      <span>{{ props.value }} / {{ $t('label.page') }}</span>
+                    </template>
+                  </a-pagination>
+                  <div :span="24" class="action-button-right">
+                    <a-button
+                      :disabled="!(('unmanageVirtualMachine' in $store.getters.apis) && managedInstancesSelectedRowKeys.length > 0)"
+                      type="primary"
+                      icon="disconnect"
+                      @click="onUnmanageInstanceAction">
+                      {{ managedInstancesSelectedRowKeys.length > 1 ? $t('label.action.unmanage.instances') : $t('label.action.unmanage.instance') }}
+                    </a-button>
+                  </div>
+                </div>
+              </a-card>
+            </a-col>
+          </a-row>
+        </a-card>
+
+        <a-modal
+          :visible="showUnmanageForm"
+          :title="$t('label.import.instance')"
+          :closable="true"
+          :maskClosable="false"
+          :footer="null"
+          :cancelText="$t('label.cancel')"
+          @cancel="showUnmanageForm = false"
+          centered
+          ref="importModal"
+          width="auto">
+          <import-unmanaged-instances
+            class="importform"
+            :resource="selectedUnmanagedInstance"
+            :cluster="selectedCluster"
+            :isOpen="showUnmanageForm"
+            @refresh-data="fetchInstances"
+            @close-action="closeImportUnmanagedInstanceForm"
+            @loading-changed="updateManageInstanceActionLoading"
+          />
+        </a-modal>
+      </div>
+    </a-col>
+  </a-row>
+</template>
+
+<script>
+import { api } from '@/api'
+import _ from 'lodash'
+import Breadcrumb from '@/components/widgets/Breadcrumb'
+import Status from '@/components/widgets/Status'
+import SearchView from '@/components/view/SearchView'
+import ImportUnmanagedInstances from '@/views/tools/ImportUnmanagedInstance'
+
+export default {
+  components: {
+    Breadcrumb,
+    Status,
+    SearchView,
+    ImportUnmanagedInstances
+  },
+  name: 'ManageVms',
+  data () {
+    const unmanagedInstancesColumns = [
+      {
+        title: this.$t('label.name'),
+        dataIndex: 'name',
+        width: 100
+      },
+      {
+        title: this.$t('label.state'),
+        dataIndex: 'powerstate',
+        scopedSlots: { customRender: 'state' }
+      },
+      {
+        title: this.$t('label.hostname'),
+        dataIndex: 'hostname'
+      },
+      {
+        title: this.$t('label.ostypename'),
+        dataIndex: 'osdisplayname'
+      }
+    ]
+    const managedInstancesColumns = [
+      {
+        title: this.$t('label.name'),
+        dataIndex: 'name',
+        width: 100,
+        scopedSlots: { customRender: 'name' }
+      },
+      {
+        title: this.$t('label.instancename'),
+        dataIndex: 'instancename'
+      },
+      {
+        title: this.$t('label.state'),
+        dataIndex: 'state',
+        scopedSlots: { customRender: 'state' }
+      },
+      {
+        title: this.$t('label.hostname'),
+        dataIndex: 'hostname'
+      },
+      {
+        title: this.$t('label.templatename'),
+        dataIndex: 'templatedisplaytext'
+      }
+    ]
+    return {
+      options: {
+        zones: [],
+        pods: [],
+        clusters: []
+      },
+      rowCount: {},
+      optionLoading: {
+        zones: false,
+        pods: false,
+        clusters: false
+      },
+      page: {
+        unmanaged: 1,
+        managed: 1
+      },
+      pageSize: {
+        unmanaged: 10,
+        managed: 10
+      },
+      searchFilters: {
+        unmanaged: [],
+        managed: []
+      },
+      searchParams: {
+        unmanaged: {},
+        managed: {}
+      },
+      itemCount: {},
+      zone: {},
+      zoneId: undefined,
+      podId: undefined,
+      clusterId: undefined,
+      listInstancesApi: {
+        unmanaged: 'listUnmanagedInstances',
+        managed: 'listVirtualMachines'
+      },
+      unmanagedInstancesColumns,
+      unmanagedInstancesLoading: false,
+      unmanagedInstances: [],
+      unmanagedInstancesSelectedRowKeys: [],
+      importUnmanagedInstanceLoading: false,
+      managedInstancesColumns,
+      managedInstancesLoading: false,
+      managedInstances: [],
+      managedInstancesSelectedRowKeys: [],
+      showUnmanageForm: false,
+      selectedUnmanagedInstance: {},
+      query: {}
+    }
+  },
+  beforeCreate () {
+    this.form = this.$form.createForm(this)
+  },
+  created () {
+    this.page.unmanaged = parseInt(this.$route.query.unmanagedpage || 1)
+    this.page.managed = parseInt(this.$route.query.managedpage || 1)
+    this.fetchData()
+  },
+  computed: {
+    isPageAllowed () {
+      if (this.$route.meta.permission) {
+        for (var apiName of this.$route.meta.permission) {
+          if (!(apiName in this.$store.getters.apis)) {
+            return false
+          }
+        }
+      }
+      return true
+    },
+    params () {
+      return {
+        zones: {
+          list: 'listZones',
+          isLoad: true,
+          field: 'zoneid'
+        },
+        pods: {
+          list: 'listPods',
+          isLoad: false,
+          options: {
+            zoneid: _.get(this.zone, 'id')
+          },
+          field: 'podid'
+        },
+        clusters: {
+          list: 'listClusters',
+          isLoad: false,
+          options: {
+            zoneid: _.get(this.zone, 'id'),
+            podid: this.podId
+          },
+          field: 'clusterid'
+        }
+      }
+    },
+    viewLoading () {
+      for (var key in this.optionLoading) {
+        if (this.optionLoading[key]) {
+          return true
+        }
+      }
+      return this.unmanagedInstancesLoading || this.managedInstancesLoading
+    },
+    zoneSelectOptions () {
+      return this.options.zones.map((zone) => {
+        return {
+          label: zone.name,
+          value: zone.id
+        }
+      })
+    },
+    podSelectOptions () {
+      const options = this.options.pods.map((pod) => {
+        return {
+          label: pod.name,
+          value: pod.id
+        }
+      })
+      return options
+    },
+    clusterSelectOptions () {
+      const options = this.options.clusters.map((cluster) => {
+        return {
+          label: cluster.name,
+          value: cluster.id
+        }
+      })
+      return options
+    },
+    unmanagedInstanceSelection () {
+      return {
+        type: 'radio',
+        selectedRowKeys: this.unmanagedInstancesSelectedRowKeys || [],
+        onChange: this.onUnmanagedInstanceSelectRow
+      }
+    },
+    managedInstanceSelection () {
+      return {
+        type: 'checkbox',
+        selectedRowKeys: this.managedInstancesSelectedRowKeys || [],
+        onChange: this.onManagedInstanceSelectRow
+      }
+    },
+    selectedCluster () {
+      if (this.options.clusters &&
+        this.options.clusters.length > 0 &&
+        this.clusterId) {
+        return _.find(this.options.clusters, (option) => option.id === this.clusterId)
+      }
+      return {}
+    }
+  },
+  methods: {
+    fetchData () {
+      this.unmanagedInstances = []
+      this.managedInstances = []
+      _.each(this.params, (param, name) => {
+        if (param.isLoad) {
+          this.fetchOptions(param, name)
+        }
+      })
+    },
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toUpperCase().indexOf(input.toUpperCase()) >= 0
+      )
+    },
+    fetchOptions (param, name, exclude) {
+      if (exclude && exclude.length > 0) {
+        if (exclude.includes(name)) {
+          return
+        }
+      }
+      this.optionLoading[name] = true
+      param.loading = true
+      param.opts = []
+      const options = param.options || {}
+      if (!('listall' in options)) {
+        options.listall = true
+      }
+      api(param.list, options).then((response) => {
+        param.loading = false
+        _.map(response, (responseItem, responseKey) => {
+          if (Object.keys(responseItem).length === 0) {
+            this.rowCount[name] = 0
+            this.options[name] = []
+            this.$forceUpdate()
+            return
+          }
+          if (!responseKey.includes('response')) {
+            return
+          }
+          _.map(responseItem, (response, key) => {
+            if (key === 'count') {
+              this.rowCount[name] = response
+              return
+            }
+            param.opts = response
+            this.options[name] = response
+
+            this.$forceUpdate()
+          })
+          this.handleFetchOptionsSuccess(name, param)
+        })
+      }).catch(function (error) {
+        console.log(error.stack)
+        param.loading = false
+      }).finally(() => {
+        this.optionLoading[name] = false
+      })
+    },
+    getRowClassName (record, index) {
+      if (index % 2 === 0) {
+        return 'light-row'
+      }
+      return 'dark-row'
+    },
+    handleFetchOptionsSuccess (name, param) {
+      if (['zones', 'pods', 'clusters'].includes(name)) {
+        let paramid = ''
+        const query = Object.assign({}, this.$route.query)
+        if (query[param.field] && _.find(this.options[name], (option) => option.id === query[param.field])) {
+          paramid = query[param.field]
+        }
+        if (!paramid && this.options[name].length === 1) {
+          paramid = (this.options[name])[0].id
+        }
+        if (paramid) {
+          this.form.getFieldDecorator([param.field], { initialValue: paramid })
+          if (name === 'zones') {
+            this.onSelectZoneId(paramid)
+          } else if (name === 'pods') {
+            this.form.setFieldsValue({
+              podid: paramid
+            })
+            this.onSelectPodId(paramid)
+          } else if (name === 'clusters') {
+            this.form.setFieldsValue({
+              clusterid: paramid
+            })
+            this.onSelectClusterId(paramid)
+          }
+        }
+      }
+    },
+    updateQuery (field, value) {
+      const query = Object.assign({}, this.$route.query)
+      if (query[field] === value + '') {
+        return
+      }
+      query[field] = value
+      if (['zoneid', 'podid', 'clusterid'].includes(field)) {
+        query.managedpage = 1
+        query.unmanagedpage = 1
+      }
+      this.$router.push({ query })
+    },
+    resetLists () {
+      this.page.unmanaged = 1
+      this.unmanagedInstances = []
+      this.unmanagedInstancesSelectedRowKeys = []
+      this.page.managed = 1
+      this.managedInstances = []
+      this.managedInstancesSelectedRowKeys = []
+    },
+    onSelectZoneId (value) {
+      this.zoneId = value
+      this.podId = null
+      this.clusterId = null
+      this.zone = _.find(this.options.zones, (option) => option.id === value)
+      this.resetLists()
+      this.form.setFieldsValue({
+        clusterid: undefined,
+        podid: undefined
+      })
+      this.updateQuery('zoneid', value)
+      this.fetchOptions(this.params.pods, 'pods')
+    },
+    onSelectPodId (value) {
+      this.podId = value
+      this.resetLists()
+      this.form.setFieldsValue({
+        clusterid: undefined
+      })
+      this.updateQuery('podid', value)
+      this.fetchOptions(this.params.clusters, 'clusters', value)
+    },
+    onSelectClusterId (value) {
+      this.clusterId = value
+      this.resetLists()
+      this.updateQuery('clusterid', value)
+      this.fetchInstances()
+    },
+    fetchInstances () {
+      if (this.selectedCluster.hypervisortype === 'VMware') {
+        this.fetchUnmanagedInstances()
+        this.fetchManagedInstances()
+      }
+    },
+    fetchUnmanagedInstances (page, pageSize) {
+      const params = {
+        clusterid: this.clusterId
+      }
+      const query = Object.assign({}, this.$route.query)
+      this.page.unmanaged = page || parseInt(query.unmanagedpage) || this.page.unmanaged
+      this.updateQuery('unmanagedpage', this.page.unmanaged)
+      params.page = this.page.unmanaged
+      this.pageSize.unmanaged = pageSize || this.pageSize.unmanaged
+      params.pagesize = this.pageSize.unmanaged
+      this.unmanagedInstances = []
+      this.unmanagedInstancesSelectedRowKeys = []
+      if (this.searchParams.unmanaged.keyword) {
+        params.keyword = this.searchParams.unmanaged.keyword
+      }
+      if (!this.clusterId) {
+        return
+      }
+      this.unmanagedInstancesLoading = true
+      this.searchParams.unmanaged = params
+      api(this.listInstancesApi.unmanaged, params).then(json => {
+        const listUnmanagedInstances = json.listunmanagedinstancesresponse.unmanagedinstance
+        if (this.arrayHasItems(listUnmanagedInstances)) {
+          this.unmanagedInstances = this.unmanagedInstances.concat(listUnmanagedInstances)
+        }
+        this.itemCount.unmanaged = json.listunmanagedinstancesresponse.count
+      }).finally(() => {
+        this.unmanagedInstancesLoading = false
+      })
+    },
+    searchUnmanagedInstances (params) {
+      this.searchParams.unmanaged.keyword = params.searchQuery
+      this.fetchUnmanagedInstances()
+    },
+    fetchManagedInstances (page, pageSize) {
+      const params = {
+        listall: true,
+        clusterid: this.clusterId
+      }
+      const query = Object.assign({}, this.$route.query)
+      this.page.managed = page || parseInt(query.managedpage) || this.page.managed
+      this.updateQuery('managedpage', this.page.managed)
+      params.page = this.page.managed
+      this.pageSize.managed = pageSize || this.pageSize.managed
+      params.pagesize = this.pageSize.managed
+      this.managedInstances = []
+      this.managedInstancesSelectedRowKeys = []
+      if (this.searchParams.managed.keyword) {
+        params.keyword = this.searchParams.managed.keyword
+      }
+      if (!this.clusterId) {
+        return
+      }
+      this.managedInstancesLoading = true
+      this.searchParams.managed = params
+      api(this.listInstancesApi.managed, params).then(json => {
+        const listManagedInstances = json.listvirtualmachinesresponse.virtualmachine
+        if (this.arrayHasItems(listManagedInstances)) {
+          this.managedInstances = this.managedInstances.concat(listManagedInstances)
+        }
+        this.itemCount.managed = json.listvirtualmachinesresponse.count
+      }).finally(() => {
+        this.managedInstancesLoading = false
+      })
+    },
+    searchManagedInstances (params) {
+      this.searchParams.managed.keyword = params.searchQuery
+      this.fetchManagedInstances()
+    },
+    onUnmanagedInstanceSelectRow (value) {
+      this.unmanagedInstancesSelectedRowKeys = value
+    },
+    onManagedInstanceSelectRow (value) {
+      this.managedInstancesSelectedRowKeys = value
+    },
+    isValidValueForKey (obj, key) {
+      return key in obj && obj[key] != null
+    },
+    arrayHasItems (array) {
+      return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
+    },
+    isObjectEmpty (obj) {
+      return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
+    },
+    updateManageInstanceActionLoading (value) {
+      this.importUnmanagedInstanceLoading = value
+    },
+    onManageInstanceAction () {
+      this.selectedUnmanagedInstance = {}
+      if (this.unmanagedInstances.length > 0 &&
+        this.unmanagedInstancesSelectedRowKeys.length > 0) {
+        this.selectedUnmanagedInstance = this.unmanagedInstances[this.unmanagedInstancesSelectedRowKeys[0]]
+        this.selectedUnmanagedInstance.ostypename = this.selectedUnmanagedInstance.osdisplayname
+        this.selectedUnmanagedInstance.state = this.selectedUnmanagedInstance.powerstate
+      }
+      this.showUnmanageForm = true
+    },
+    closeImportUnmanagedInstanceForm () {
+      this.selectedUnmanagedInstance = {}
+      this.showUnmanageForm = false
+      this.$refs.importModal.$forceUpdate()
+    },
+    onUnmanageInstanceAction () {
+      const self = this
+      const title = this.managedInstancesSelectedRowKeys.length > 1
+        ? this.$t('message.action.unmanage.instances')
+        : this.$t('message.action.unmanage.instance')
+      var vmNames = []
+      for (var index of this.managedInstancesSelectedRowKeys) {
+        vmNames.push(this.managedInstances[index].name)
+      }
+      const content = vmNames.join(', ')
+      this.$confirm({
+        title: title,
+        okText: this.$t('label.ok'),
+        okType: 'danger',
+        content: content,
+        cancelText: this.$t('label.cancel'),
+        onOk () {
+          self.unmanageInstances()
+        }
+      })
+    },
+    unmanageInstances () {
+      for (var index of this.managedInstancesSelectedRowKeys) {
+        const vm = this.managedInstances[index]
+        var params = { id: vm.id }
+        api('unmanageVirtualMachine', params).then(json => {
+          const jobId = json.unmanagevirtualmachineresponse.jobid
+          this.$store.dispatch('AddAsyncJob', {
+            title: this.$t('label.unmanage.instance'),
+            jobid: jobId,
+            description: vm.name,
+            status: 'progress'
+          })
+          this.$pollJob({
+            jobId,
+            loadingMessage: `${this.$t('label.unmanage.instance')} ${vm.name} ${this.$t('label.in.progress')}`,
+            catchMessage: this.$t('error.fetching.async.job.result'),
+            successMessage: this.$t('message.success.unmanage.instance') + ' ' + vm.name,
+            successMethod: result => {
+              if (index === this.managedInstancesSelectedRowKeys[this.managedInstancesSelectedRowKeys.length - 1]) {
+                this.fetchInstances()
+              }
+            }
+          })
+        }).catch(error => {
+          this.$notifyError(error)
+        }).finally(() => {
+          this.loading = false
+        })
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+/deep/ .ant-table-thead {
+  background-color: #f9f9f9;
+}
+
+/deep/ .ant-table-small > .ant-table-content > .ant-table-body {
+  margin: 0;
+}
+
+/deep/ .light-row {
+  background-color: #fff;
+}
+
+/deep/ .dark-row {
+  background-color: #f9f9f9;
+}
+</style>
+
+<style scoped lang="less">
+  .importform {
+    width: 80vw;
+  }
+  .instances-card {
+    height: 100%;
+  }
+  .instances-card-table {
+    overflow-y: auto;
+    margin-bottom: 100px;
+  }
+  .instances-card-footer {
+    height: 100px;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    margin-left: 10px;
+    right: 0;
+    margin-right: 10px;
+  }
+  .row-element {
+    margin-top: 10px;
+    margin-bottom: 10px;
+  }
+  .action-button-left {
+    text-align: left;
+  }
+  .action-button-right {
+    text-align: right;
+  }
+</style>