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 2020/04/22 09:13:18 UTC

[cloudstack-primate] branch master updated: compute: Fix VM Deployment Wizard Issues (#273)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 334be08  compute: Fix VM Deployment Wizard Issues  (#273)
334be08 is described below

commit 334be08053df495f5f7534c6f5ebb6f1f02039a7
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Wed Apr 22 16:13:12 2020 +0700

    compute: Fix VM Deployment Wizard Issues  (#273)
    
    Fixes #254
    
     Pod, cluster, host should be hidden for normal and domain admin user
     Listing of all items are not done by passing account/domainid, or zoneid (for example listing of networks, template, isos for zone; keypair, affiinity groups etc for account/domain)
     Add a way to filter templates by featured, community, shared, mine?
     Multiple listing of templates for the same zone? (multiple radios button)
     Add button to add network that can open the add network popup or add a router-link?
     User data not properly base64 encoded (check encoding?)
     Add support for processing min/max cpu/ram based on type of compute offering selected and custom disk offering
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
    Co-authored-by: Hoang Nguyen <ho...@unitech.vn>
    Co-authored-by: Abhishek Kumar <ab...@gmail.com>
    Co-authored-by: Rohit Yadav <ro...@shapeblue.com>
---
 src/components/header/ProjectMenu.vue              |   4 +-
 src/locales/en.json                                |   9 +-
 src/views/AutogenView.vue                          |   2 +
 src/views/compute/DeployVM.vue                     | 256 ++++++++++++++++---
 .../compute/wizard/AffinityGroupSelection.vue      |  15 ++
 ...eSelection.vue => ComputeOfferingSelection.vue} |  38 ++-
 src/views/compute/wizard/ComputeSelection.vue      | 278 ++++++++++++++-------
 src/views/compute/wizard/DiskOfferingSelection.vue |  37 ++-
 src/views/compute/wizard/DiskSizeSelection.vue     |  47 +++-
 src/views/compute/wizard/NetworkConfiguration.vue  |   8 +-
 src/views/compute/wizard/NetworkSelection.vue      |  54 +++-
 src/views/compute/wizard/SshKeyPairSelection.vue   |  31 ++-
 src/views/compute/wizard/TemplateIsoRadioGroup.vue |  24 +-
 src/views/compute/wizard/TemplateIsoSelection.vue  | 144 ++++++++++-
 14 files changed, 768 insertions(+), 179 deletions(-)

diff --git a/src/components/header/ProjectMenu.vue b/src/components/header/ProjectMenu.vue
index 2c922d1..f3ea38b 100644
--- a/src/components/header/ProjectMenu.vue
+++ b/src/components/header/ProjectMenu.vue
@@ -67,9 +67,9 @@ export default {
           if (json && json.listprojectsresponse && json.listprojectsresponse.project) {
             this.projects.push(...json.listprojectsresponse.project)
           }
-          const currentProject = Vue.ls.get(CURRENT_PROJECT)
+          const currentProject = Vue.ls.get(CURRENT_PROJECT) || {}
           for (var project of this.projects) {
-            if (project.id === currentProject.id) {
+            if (project && currentProject && project.id === currentProject.id) {
               this.setSelectedProject(project)
               break
             }
diff --git a/src/locales/en.json b/src/locales/en.json
index fad2146..285f182 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -1151,6 +1151,8 @@
 "message.required.traffic.type": "Error in configuration! All required traffic types should be added and with multiple physical networks each network should have a label.",
 "message.desc.primary.storage": "Each cluster must contain one or more primary storage servers, and we will add the first one now. Primary storage contains the disk volumes for all the VMs running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.",
 "message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server, and we will add the first one now. Secondary storage stores VM templates, ISO images, and VM disk volume snapshots. This server must be available to all hosts in the zone.<br/><br/>Provide the IP address and exported path.",
+"message.error.required.input": "Please enter input",
+"message.error.invalid.range": "Please enter values from {min} to {max}",
 "label.name": "Name",
 "label.ipv4.dns1": "IPv4 DNS1",
 "label.ipv4.dns2": "IPv4 DNS2",
@@ -1266,5 +1268,10 @@
 "label.launch.zone": "Launch Zone",
 "label.done": "Done",
 "label.fix.errors": "Fix errors",
-"error.something.went.wrong.please.correct.the.following": "Something went wrong; please correct the following"
+"error.something.went.wrong.please.correct.the.following": "Something went wrong; please correct the following",
+"filter": "Filter",
+"featured": "Featured",
+"community": "Community",
+"selfexecutable": "Self",
+"sharedexecutable": "Shared"
 }
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index 47b10af..07f471f 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -396,6 +396,8 @@ export default {
       if (this.searchQuery !== '') {
         if (this.apiName === 'listRoles') {
           params.name = this.searchQuery
+        } else if (this.apiName === 'quotaEmailTemplateList') {
+          params.templatetype = this.searchQuery
         } else {
           params.keyword = this.searchQuery
         }
diff --git a/src/views/compute/DeployVM.vue b/src/views/compute/DeployVM.vue
index f1a4c9e..d7b7eee 100644
--- a/src/views/compute/DeployVM.vue
+++ b/src/views/compute/DeployVM.vue
@@ -44,21 +44,27 @@
                         :loading="loading.zones"
                       ></a-select>
                     </a-form-item>
-                    <a-form-item :label="this.$t('podId')">
+                    <a-form-item
+                      v-if="!isNormalAndDomainUser"
+                      :label="this.$t('podId')">
                       <a-select
                         v-decorator="['podid']"
                         :options="podSelectOptions"
                         :loading="loading.pods"
                       ></a-select>
                     </a-form-item>
-                    <a-form-item :label="this.$t('clusterid')">
+                    <a-form-item
+                      v-if="!isNormalAndDomainUser"
+                      :label="this.$t('clusterid')">
                       <a-select
                         v-decorator="['clusterid']"
                         :options="clusterSelectOptions"
                         :loading="loading.clusters"
                       ></a-select>
                     </a-form-item>
-                    <a-form-item :label="this.$t('hostId')">
+                    <a-form-item
+                      v-if="!isNormalAndDomainUser"
+                      :label="this.$t('hostId')">
                       <a-select
                         v-decorator="['hostid']"
                         :options="hostSelectOptions"
@@ -101,10 +107,12 @@
                           :items="options.templates"
                           :selected="tabKey"
                           :loading="loading.templates"
+                          :preFillContent="dataPreFill"
                           @update-template-iso="updateFieldValue"
                         ></template-iso-selection>
                         <disk-size-selection
                           input-decorator="rootdisksize"
+                          :preFillContent="dataPreFill"
                           @update-disk-size="updateFieldValue"/>
                       </p>
                       <p v-else>
@@ -113,8 +121,22 @@
                           :items="options.isos"
                           :selected="tabKey"
                           :loading="loading.isos"
+                          :preFillContent="dataPreFill"
                           @update-template-iso="updateFieldValue"
                         ></template-iso-selection>
+                        <a-form-item :label="this.$t('hypervisor')">
+                          <a-select
+                            v-decorator="['hypervisor', {
+                              initialValue: hypervisorSelectOptions && hypervisorSelectOptions.length > 0
+                                ? hypervisorSelectOptions[0].value
+                                : null,
+                              rules: [{ required: true, message: 'Please select option' }]
+                            }]"
+                            :options="hypervisorSelectOptions"
+                            @change="value => this.hypervisor = value"
+                          >
+                          </a-select>
+                        </a-form-item>
                       </p>
                     </a-card>
                     <a-form-item class="form-item-hidden">
@@ -134,13 +156,42 @@
                 :status="zoneSelected ? 'process' : 'wait'">
                 <template slot="description">
                   <div v-if="zoneSelected">
-                    <compute-selection
+                    <compute-offering-selection
                       :compute-items="options.serviceOfferings"
                       :value="serviceOffering ? serviceOffering.id : ''"
                       :loading="loading.serviceOfferings"
+                      :preFillContent="dataPreFill"
                       @select-compute-item="($event) => updateComputeOffering($event)"
                       @handle-search-filter="($event) => handleSearchFilter('serviceOfferings', $event)"
-                    ></compute-selection>
+                    ></compute-offering-selection>
+                    <compute-selection
+                      v-if="serviceOffering && serviceOffering.iscustomized"
+                      cpunumber-input-decorator="cpunumber"
+                      cpuspeed-input-decorator="cpuspeed"
+                      memory-input-decorator="memory"
+                      :preFillContent="dataPreFill"
+                      :computeOfferingId="instanceConfig.computeofferingid"
+                      :isConstrained="'serviceofferingdetails' in serviceOffering"
+                      :minCpu="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.mincpunumber*1 : 1"
+                      :maxCpu="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.maxcpunumber*1 : Number.MAX_SAFE_INTEGER"
+                      :minMemory="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.minmemory*1 : 1"
+                      :maxMemory="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.maxmemory*1 : Number.MAX_SAFE_INTEGER"
+                      @update-compute-cpunumber="updateFieldValue"
+                      @update-compute-cpuspeed="updateFieldValue"
+                      @update-compute-memory="updateFieldValue" />
+                    <span v-if="serviceOffering && serviceOffering.iscustomized">
+                      <a-form-item class="form-item-hidden" >
+                        <a-input v-decorator="['cpunumber']"/>
+                      </a-form-item>
+                      <a-form-item
+                        class="form-item-hidden"
+                        v-if="serviceOffering && !(serviceOffering.cpuspeed > 0)">
+                        <a-input v-decorator="['cpuspeed']"/>
+                      </a-form-item>
+                      <a-form-item class="form-item-hidden">
+                        <a-input v-decorator="['memory']"/>
+                      </a-form-item>
+                    </span>
                   </div>
                 </template>
               </a-step>
@@ -153,12 +204,14 @@
                       :items="options.diskOfferings"
                       :value="diskOffering ? diskOffering.id : ''"
                       :loading="loading.diskOfferings"
+                      :preFillContent="dataPreFill"
                       @select-disk-offering-item="($event) => updateDiskOffering($event)"
                       @handle-search-filter="($event) => handleSearchFilter('diskOfferings', $event)"
                     ></disk-offering-selection>
                     <disk-size-selection
                       v-if="diskOffering && diskOffering.iscustomized"
                       input-decorator="size"
+                      :preFillContent="dataPreFill"
                       @update-disk-size="updateFieldValue" />
                     <a-form-item class="form-item-hidden">
                       <a-input v-decorator="['size']"/>
@@ -175,6 +228,7 @@
                       :items="options.affinityGroups"
                       :value="affinityGroupIds"
                       :loading="loading.affinityGroups"
+                      :preFillContent="dataPreFill"
                       @select-affinity-group-item="($event) => updateAffinityGroups($event)"
                       @handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"
                     ></affinity-group-selection>
@@ -191,12 +245,14 @@
                       :value="networkOfferingIds"
                       :loading="loading.networks"
                       :zoneId="zoneId"
+                      :preFillContent="dataPreFill"
                       @select-network-item="($event) => updateNetworks($event)"
                       @handle-search-filter="($event) => handleSearchFilter('networks', $event)"
                     ></network-selection>
                     <network-configuration
                       v-if="networks.length > 0"
                       :items="networks"
+                      :preFillContent="dataPreFill"
                       @update-network-config="($event) => updateNetworkConfig($event)"
                       @select-default-network-item="($event) => updateDefaultNetworks($event)"
                     ></network-configuration>
@@ -212,6 +268,7 @@
                       :items="options.sshKeyPairs"
                       :value="sshKeyPair ? sshKeyPair.name : ''"
                       :loading="loading.sshKeyPairs"
+                      :preFillContent="dataPreFill"
                       @select-ssh-key-pair-item="($event) => updateSshKeyPairs($event)"
                       @handle-search-filter="($event) => handleSearchFilter('sshKeyPairs', $event)"
                     />
@@ -261,6 +318,7 @@ import { mixin, mixinDevice } from '@/utils/mixin.js'
 import store from '@/store'
 
 import InfoCard from '@/components/view/InfoCard'
+import ComputeOfferingSelection from './wizard/ComputeOfferingSelection'
 import ComputeSelection from './wizard/ComputeSelection'
 import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
 import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
@@ -281,11 +339,16 @@ export default {
     DiskSizeSelection,
     DiskOfferingSelection,
     InfoCard,
+    ComputeOfferingSelection,
     ComputeSelection
   },
   props: {
     visible: {
       type: Boolean
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   mixins: [mixin, mixinDevice],
@@ -297,6 +360,7 @@ export default {
       options: {
         templates: [],
         isos: [],
+        hypervisors: [],
         serviceOfferings: [],
         diskOfferings: [],
         zones: [],
@@ -313,6 +377,7 @@ export default {
         deploy: false,
         templates: false,
         isos: false,
+        hypervisors: false,
         serviceOfferings: false,
         diskOfferings: false,
         affinityGroups: false,
@@ -324,9 +389,10 @@ export default {
         hosts: false,
         groups: false
       },
-      instanceConfig: [],
+      instanceConfig: {},
       template: {},
       iso: {},
+      hypervisor: '',
       serviceOffering: {},
       diskOffering: {},
       affinityGroups: [],
@@ -368,10 +434,14 @@ export default {
           tab: this.$t('ISOs')
         }
       ],
-      tabKey: 'templateid'
+      tabKey: 'templateid',
+      dataPreFill: {}
     }
   },
   computed: {
+    isNormalAndDomainUser () {
+      return ['DomainAdmin', 'User'].includes(this.$store.getters.userInfo.roletype)
+    },
     diskSize () {
       const rootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 0)
       const customDiskSize = _.get(this.instanceConfig, 'size', 0)
@@ -411,23 +481,33 @@ export default {
           }
         },
         zones: {
-          list: 'listZones'
+          list: 'listZones',
+          isLoad: true,
+          field: 'zoneid'
+        },
+        hypervisors: {
+          list: 'listHypervisors',
+          options: {
+            zoneid: _.get(this.zone, 'id')
+          },
+          field: 'hypervisor'
         },
         affinityGroups: {
           list: 'listAffinityGroups',
           options: {
             page: 1,
             pageSize: 10,
-            keyword: undefined
+            keyword: undefined,
+            listall: false
           }
         },
         sshKeyPairs: {
           list: 'listSSHKeyPairs',
           options: {
-            zoneid: _.get(this.zone, 'id'),
             page: 1,
             pageSize: 10,
-            keyword: undefined
+            keyword: undefined,
+            listall: false
           }
         },
         networks: {
@@ -436,6 +516,8 @@ export default {
             zoneid: _.get(this.zone, 'id'),
             canusefordeploy: true,
             projectid: store.getters.project.id,
+            domainid: store.getters.project.id ? null : store.getters.userInfo.domainid,
+            account: store.getters.project.id ? null : store.getters.userInfo.account,
             page: 1,
             pageSize: 10,
             keyword: undefined
@@ -443,26 +525,37 @@ export default {
         },
         pods: {
           list: 'listPods',
+          isLoad: !this.isNormalAndDomainUser,
           options: {
             zoneid: _.get(this.zone, 'id')
-          }
+          },
+          field: 'podid'
         },
         clusters: {
           list: 'listClusters',
+          isLoad: !this.isNormalAndDomainUser,
           options: {
             zoneid: _.get(this.zone, 'id')
-          }
+          },
+          field: 'clusterid'
         },
         hosts: {
           list: 'listHosts',
+          isLoad: !this.isNormalAndDomainUser,
           options: {
             zoneid: _.get(this.zone, 'id'),
             state: 'Up',
             type: 'Routing'
-          }
+          },
+          field: 'hostid'
         },
         groups: {
-          list: 'listInstanceGroups'
+          list: 'listInstanceGroups',
+          options: {
+            listall: false
+          },
+          isLoad: true,
+          field: 'group'
         }
       }
     },
@@ -477,6 +570,14 @@ export default {
         }
       })
     },
+    hypervisorSelectOptions () {
+      return this.options.hypervisors.map((hypervisor) => {
+        return {
+          label: hypervisor.name,
+          value: hypervisor.name
+        }
+      })
+    },
     podSelectOptions () {
       return this.options.pods.map((pod) => {
         return {
@@ -527,6 +628,8 @@ export default {
     instanceConfig (instanceConfig) {
       this.template = _.find(this.options.templates, (option) => option.id === instanceConfig.templateid)
       this.iso = _.find(this.options.isos, (option) => option.id === instanceConfig.isoid)
+      var hypervisorItem = _.find(this.options.hypervisors, (option) => option.name === instanceConfig.hypervisor)
+      this.hypervisor = hypervisorItem ? hypervisorItem.name : null
       this.serviceOffering = _.find(this.options.serviceOfferings, (option) => option.id === instanceConfig.computeofferingid)
       this.diskOffering = _.find(this.options.diskOfferings, (option) => option.id === instanceConfig.diskofferingid)
       this.zone = _.find(this.options.zones, (option) => option.id === instanceConfig.zoneid)
@@ -551,6 +654,9 @@ export default {
         this.vm.templatename = this.iso.displaytext
         this.vm.ostypeid = this.iso.ostypeid
         this.vm.ostypename = this.iso.ostypename
+        if (this.hypervisor) {
+          this.vm.hypervisor = this.hypervisor
+        }
       }
 
       if (this.serviceOffering) {
@@ -572,7 +678,7 @@ export default {
       }
     }
   },
-  beforeCreate () {
+  created () {
     this.form = this.$form.createForm(this, {
       onValuesChange: (props, fields) => {
         if (fields.isoid) {
@@ -584,36 +690,63 @@ export default {
           this.form.setFieldsValue({ isoid: null })
         }
         this.instanceConfig = { ...this.form.getFieldsValue(), ...fields }
-        this.vm = this.instanceConfig
+        this.vm = Object.assign({}, this.instanceConfig)
       }
     })
     this.form.getFieldDecorator('computeofferingid', { initialValue: undefined, preserve: true })
     this.form.getFieldDecorator('diskofferingid', { initialValue: undefined, preserve: true })
     this.form.getFieldDecorator('affinitygroupids', { initialValue: [], preserve: true })
-    this.form.getFieldDecorator('isoid', { initialValue: undefined, preserve: true })
     this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true })
     this.form.getFieldDecorator('keypair', { initialValue: undefined, preserve: true })
-    this.apiParams = {}
-    this.apiDeployVirtualMachine = this.$store.getters.apis.deployVirtualMachine || {}
-    this.apiDeployVirtualMachine.params.forEach(param => {
-      this.apiParams[param.name] = param
-    })
+    this.form.getFieldDecorator('cpunumber', { initialValue: undefined, preserve: true })
+    this.form.getFieldDecorator('cpuSpeed', { initialValue: undefined, preserve: true })
+    this.form.getFieldDecorator('memory', { initialValue: undefined, preserve: true })
   },
-  created () {
+  mounted () {
+    this.dataPreFill = this.preFillContent && Object.keys(this.preFillContent).length > 0 ? this.preFillContent : {}
     this.fetchData()
   },
+  provide () {
+    return {
+      vmFetchTemplates: this.fetchAllTemplates,
+      vmFetchIsos: this.fetchAllIsos,
+      vmFetchNetworks: this.fetchNetwork
+    }
+  },
   methods: {
+    fillValue (field) {
+      this.form.getFieldDecorator([field], { initialValue: this.dataPreFill[field] })
+    },
     fetchData () {
-      this.fetchOptions(this.params.zones, 'zones')
-      this.fetchOptions(this.params.pods, 'pods')
-      this.fetchOptions(this.params.clusters, 'clusters')
-      this.fetchOptions(this.params.hosts, 'hosts')
-      this.fetchOptions(this.params.groups, 'groups')
+      if (this.dataPreFill.zoneid) {
+        this.fetchDataByZone(this.dataPreFill.zoneid)
+      } else {
+        _.each(this.params, (param, name) => {
+          if (param.isLoad) {
+            this.fetchOptions(param, name)
+          }
+        })
+      }
+
       this.fetchKeyboard()
       Vue.nextTick().then(() => {
+        ['name', 'keyboard', 'userdata'].forEach(this.fillValue)
         this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
       })
     },
+    async fetchDataByZone (zoneId) {
+      this.fillValue('zoneid')
+      this.options.zones = await this.fetchZones()
+      this.zoneId = zoneId
+      this.zoneSelected = true
+      this.tabKey = 'templateid'
+      await _.each(this.params, (param, name) => {
+        if (!('isLoad' in param) || param.isLoad) {
+          this.fetchOptions(param, name, ['zones'])
+        }
+      })
+      await this.fetchAllTemplates()
+    },
     fetchKeyboard () {
       const keyboardType = []
       keyboardType.push({
@@ -643,6 +776,10 @@ export default {
 
       this.$set(this.options, 'keyboards', keyboardType)
     },
+    fetchNetwork () {
+      const param = this.params.networks
+      this.fetchOptions(param, 'networks')
+    },
     resetData () {
       this.vm = {}
       this.zoneSelected = false
@@ -654,13 +791,13 @@ export default {
         this.tabKey = 'templateid'
         this.form.setFieldsValue({
           templateid: value,
-          isoid: undefined
+          isoid: null
         })
       } else if (name === 'isoid') {
         this.tabKey = 'isoid'
         this.form.setFieldsValue({
           isoid: value,
-          templateid: undefined
+          templateid: null
         })
       } else {
         this.form.setFieldsValue({
@@ -729,8 +866,8 @@ export default {
         deployVmData.hostid = values.hostid
         deployVmData.group = values.group
         deployVmData.keyboard = values.keyboard
-        if (values.keyboard && values.keyboard.length > 0) {
-          deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.keyboard)))
+        if (values.userdata && values.userdata.length > 0) {
+          deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.userdata)))
         }
         // step 2: select template/iso
         if (this.tabKey === 'templateid') {
@@ -741,15 +878,29 @@ export default {
         if (values.rootdisksize && values.rootdisksize > 0) {
           deployVmData.rootdisksize = values.rootdisksize
         }
+        if (values.hypervisor && values.hypervisor.length > 0) {
+          deployVmData.hypervisor = values.hypervisor
+        }
         // step 3: select service offering
         deployVmData.serviceofferingid = values.computeofferingid
+        if (values.cpunumber || values.cpuspeed || values.memory) {
+          if (values.cpunumber) {
+            deployVmData['details[0].cpuNumber'] = values.cpunumber
+          }
+          if (values.cpuspeed) {
+            deployVmData['details[0].cpuSpeed'] = values.cpuspeed
+          }
+          if (values.memory) {
+            deployVmData['details[0].memory'] = values.memory
+          }
+        }
         // step 4: select disk offering
         deployVmData.diskofferingid = values.diskofferingid
         if (values.size) {
           deployVmData.size = values.size
         }
         // step 5: select an affinity group
-        deployVmData.affinitygroupids = values.affinitygroupids.join(',')
+        deployVmData.affinitygroupids = (values.affinitygroupids || []).join(',')
         // step 6: select network
         if (values.networkids && values.networkids.length > 0) {
           for (let i = 0; i < values.networkids.length; i++) {
@@ -804,6 +955,20 @@ export default {
         })
       })
     },
+    fetchZones () {
+      return new Promise((resolve) => {
+        this.loading.zones = true
+        const param = this.params.zones
+        api(param.list, { listall: true }).then(json => {
+          const zones = json.listzonesresponse.zone || []
+          resolve(zones)
+        }).catch(function (error) {
+          console.log(error.stack)
+        }).finally(() => {
+          this.loading.zones = false
+        })
+      })
+    },
     fetchOptions (param, name, exclude) {
       if (exclude && exclude.length > 0) {
         if (exclude.includes(name)) {
@@ -814,7 +979,9 @@ export default {
       param.loading = true
       param.opts = []
       const options = param.options || {}
-      options.listall = true
+      if (!('listall' in options)) {
+        options.listall = true
+      }
       api(param.list, options).then((response) => {
         param.loading = false
         _.map(response, (responseItem, responseKey) => {
@@ -833,6 +1000,9 @@ export default {
             param.opts = response
             this.options[name] = response
             this.$forceUpdate()
+            if (param.field) {
+              this.fillValue(param.field)
+            }
           })
         })
       }).catch(function (error) {
@@ -869,11 +1039,14 @@ export default {
         })
       })
     },
-    fetchAllTemplates () {
+    fetchAllTemplates (filterKey) {
       const promises = []
       this.options.templates = []
       this.loading.templates = true
       this.templateFilter.forEach((filter) => {
+        if (filterKey && filterKey !== filter) {
+          return true
+        }
         promises.push(this.fetchTemplates(filter))
       })
       Promise.all(promises).then(response => {
@@ -888,11 +1061,14 @@ export default {
         this.loading.templates = false
       })
     },
-    fetchAllIsos () {
+    fetchAllIsos (filterKey) {
       const promises = []
       this.options.isos = []
       this.loading.isos = true
       this.isoFilter.forEach((filter) => {
+        if (filterKey && filterKey !== filter) {
+          return true
+        }
         promises.push(this.fetchIsos(filter))
       })
       Promise.all(promises).then(response => {
@@ -908,7 +1084,9 @@ export default {
       })
     },
     onSelectZoneId (value) {
+      this.dataPreFill = {}
       this.zoneId = value
+      this.zone = _.find(this.options.zones, (option) => option.id === value)
       this.zoneSelected = true
       this.form.setFieldsValue({
         clusterid: undefined,
@@ -919,7 +1097,9 @@ export default {
       })
       this.tabKey = 'templateid'
       _.each(this.params, (param, name) => {
-        this.fetchOptions(param, name, ['zones', 'groups'])
+        if (!('isLoad' in param) || param.isLoad) {
+          this.fetchOptions(param, name, ['zones', 'groups'])
+        }
       })
       this.fetchAllTemplates()
     },
diff --git a/src/views/compute/wizard/AffinityGroupSelection.vue b/src/views/compute/wizard/AffinityGroupSelection.vue
index be14d6f..9c25355 100644
--- a/src/views/compute/wizard/AffinityGroupSelection.vue
+++ b/src/views/compute/wizard/AffinityGroupSelection.vue
@@ -53,6 +53,10 @@ export default {
     loading: {
       type: Boolean,
       default: false
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -96,6 +100,17 @@ export default {
       if (newValue && !_.isEqual(newValue, oldValue)) {
         this.selectedRowKeys = newValue
       }
+    },
+    loading () {
+      if (!this.loading) {
+        if (this.preFillContent.affinitygroupids) {
+          this.selectedRowKeys = this.preFillContent.affinitygroupids
+          this.$emit('select-affinity-group-item', this.preFillContent.affinitygroupids)
+        } else {
+          this.selectedRowKeys = []
+          this.$emit('select-affinity-group-item', null)
+        }
+      }
     }
   },
   methods: {
diff --git a/src/views/compute/wizard/ComputeSelection.vue b/src/views/compute/wizard/ComputeOfferingSelection.vue
similarity index 67%
copy from src/views/compute/wizard/ComputeSelection.vue
copy to src/views/compute/wizard/ComputeOfferingSelection.vue
index e44d17a..4403ee4 100644
--- a/src/views/compute/wizard/ComputeSelection.vue
+++ b/src/views/compute/wizard/ComputeOfferingSelection.vue
@@ -40,7 +40,7 @@
 
 <script>
 export default {
-  name: 'ComputeSelection',
+  name: 'ComputeOfferingSelection',
   props: {
     computeItems: {
       type: Array,
@@ -53,6 +53,10 @@ export default {
     loading: {
       type: Boolean,
       default: false
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -88,11 +92,28 @@ export default {
     },
     tableSource () {
       return this.computeItems.map((item) => {
+        var cpuNumberValue = item.cpunumber + ''
+        var cpuSpeedValue = (item.cpuspeed !== null && item.cpuspeed !== undefined && item.cpuspeed > 0) ? parseFloat(item.cpuspeed / 1000.0).toFixed(2) + '' : ''
+        var ramValue = item.memory + ''
+        if (item.iscustomized === true) {
+          cpuNumberValue = ''
+          ramValue = ''
+          if ('serviceofferingdetails' in item &&
+            'mincpunumber' in item.serviceofferingdetails &&
+            'maxcpunumber' in item.serviceofferingdetails) {
+            cpuNumberValue = item.serviceofferingdetails.mincpunumber + '-' + item.serviceofferingdetails.maxcpunumber
+          }
+          if ('serviceofferingdetails' in item &&
+            'minmemory' in item.serviceofferingdetails &&
+            'maxmemory' in item.serviceofferingdetails) {
+            ramValue = item.serviceofferingdetails.minmemory + '-' + item.serviceofferingdetails.maxmemory
+          }
+        }
         return {
           key: item.id,
           name: item.name,
-          cpu: `${item.cpunumber} CPU x ${parseFloat(item.cpuspeed / 1000.0).toFixed(2)} Ghz`,
-          ram: `${item.memory} MB`
+          cpu: cpuNumberValue.length > 0 ? `${cpuNumberValue} CPU x ${cpuSpeedValue} Ghz` : '',
+          ram: ramValue.length > 0 ? `${ramValue} MB` : ''
         }
       })
     },
@@ -109,6 +130,17 @@ export default {
       if (newValue && newValue !== oldValue) {
         this.selectedRowKeys = [newValue]
       }
+    },
+    loading () {
+      if (!this.loading) {
+        if (this.preFillContent.computeofferingid) {
+          this.selectedRowKeys = [this.preFillContent.computeofferingid]
+          this.$emit('select-compute-item', this.preFillContent.computeofferingid)
+        } else {
+          this.selectedRowKeys = []
+          this.$emit('select-compute-item', null)
+        }
+      }
     }
   },
   methods: {
diff --git a/src/views/compute/wizard/ComputeSelection.vue b/src/views/compute/wizard/ComputeSelection.vue
index e44d17a..68ae442 100644
--- a/src/views/compute/wizard/ComputeSelection.vue
+++ b/src/views/compute/wizard/ComputeSelection.vue
@@ -16,122 +16,228 @@
 // under the License.
 
 <template>
-  <div>
-    <a-input-search
-      style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
-      placeholder="Search"
-      v-model="filter"
-      @search="handleSearch" />
-    <a-table
-      :columns="columns"
-      :dataSource="tableSource"
-      :pagination="{showSizeChanger: true}"
-      :rowSelection="rowSelection"
-      :loading="loading"
-      size="middle"
-      @change="handleTableChange"
-      :scroll="{ y: 225 }"
-    >
-      <span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
-      <span slot="ramTitle"><a-icon type="bulb" /> {{ $t('memory') }}</span>
-    </a-table>
-  </div>
+  <a-card>
+    <a-col>
+      <a-row>
+        <a-col :md="colContraned" :lg="colContraned">
+          <a-form-item
+            :label="this.$t('cpunumber')"
+            :validate-status="errors.cpu.status"
+            :help="errors.cpu.message">
+            <a-row :gutter="12">
+              <a-col :md="10" :lg="10" v-show="isConstrained">
+                <a-slider
+                  :min="minCpu"
+                  :max="maxCpu"
+                  v-model="cpuNumberInputValue"
+                  @change="($event) => updateComputeCpuNumber($event)"
+                />
+              </a-col>
+              <a-col :md="4" :lg="4">
+                <a-input-number
+                  v-model="cpuNumberInputValue"
+                  :formatter="value => `${value}`"
+                  @change="($event) => updateComputeCpuNumber($event)"
+                />
+              </a-col>
+            </a-row>
+          </a-form-item>
+        </a-col>
+        <a-col :md="8" :lg="8" v-show="!isConstrained">
+          <a-form-item
+            :label="this.$t('cpuspeed')"
+            :validate-status="errors.cpuspeed.status"
+            :help="errors.cpuspeed.message">
+            <a-input-number
+              v-model="cpuSpeedInputValue"
+              @change="($event) => updateComputeCpuSpeed($event)"
+            />
+          </a-form-item>
+        </a-col>
+        <a-col :md="colContraned" :lg="colContraned">
+          <a-form-item
+            :label="this.$t('memory')"
+            :validate-status="errors.memory.status"
+            :help="errors.memory.message">
+            <a-row :gutter="12">
+              <a-col :md="10" :lg="10" v-show="isConstrained">
+                <a-slider
+                  :min="minMemory"
+                  :max="maxMemory"
+                  v-model="memoryInputValue"
+                  @change="($event) => updateComputeMemory($event)"
+                />
+              </a-col>
+              <a-col :md="4" :lg="4">
+                <a-input-number
+                  v-model="memoryInputValue"
+                  :formatter="value => `${value} MB`"
+                  :parser="value => value.replace(' MB', '')"
+                  @change="($event) => updateComputeMemory($event)"
+                />
+              </a-col>
+            </a-row>
+          </a-form-item>
+        </a-col>
+      </a-row>
+    </a-col>
+  </a-card>
 </template>
 
 <script>
 export default {
   name: 'ComputeSelection',
   props: {
-    computeItems: {
-      type: Array,
-      default: () => []
+    computeOfferingId: {
+      type: String,
+      default: () => ''
+    },
+    isConstrained: {
+      type: Boolean,
+      default: true
+    },
+    minCpu: {
+      type: Number,
+      default: 1
+    },
+    maxCpu: {
+      type: Number,
+      default: 2
+    },
+    minMemory: {
+      type: Number,
+      default: 1
     },
-    value: {
+    maxMemory: {
+      type: Number,
+      default: 256
+    },
+    cpunumberInputDecorator: {
       type: String,
       default: ''
     },
-    loading: {
-      type: Boolean,
-      default: false
+    cpuspeedInputDecorator: {
+      type: String,
+      default: ''
+    },
+    memoryInputDecorator: {
+      type: String,
+      default: ''
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
     return {
-      filter: '',
-      columns: [
-        {
-          dataIndex: 'name',
-          title: this.$t('serviceOfferingId'),
-          width: '40%'
+      cpuNumberInputValue: 1,
+      cpuSpeedInputValue: 1,
+      memoryInputValue: 1,
+      errors: {
+        cpu: {
+          status: '',
+          message: ''
         },
-        {
-          dataIndex: 'cpu',
-          slots: { title: 'cpuTitle' },
-          width: '30%'
+        cpuspeed: {
+          status: '',
+          message: ''
         },
-        {
-          dataIndex: 'ram',
-          slots: { title: 'ramTitle' },
-          width: '30%'
+        memory: {
+          status: '',
+          message: ''
         }
-      ],
-      selectedRowKeys: []
+      }
     }
   },
   computed: {
-    options () {
-      return {
-        page: 1,
-        pageSize: 10,
-        keyword: ''
-      }
-    },
-    tableSource () {
-      return this.computeItems.map((item) => {
-        return {
-          key: item.id,
-          name: item.name,
-          cpu: `${item.cpunumber} CPU x ${parseFloat(item.cpuspeed / 1000.0).toFixed(2)} Ghz`,
-          ram: `${item.memory} MB`
-        }
-      })
-    },
-    rowSelection () {
-      return {
-        type: 'radio',
-        selectedRowKeys: this.selectedRowKeys,
-        onChange: this.onSelectRow
-      }
+    colContraned () {
+      return this.isConstrained ? 12 : 8
     }
   },
   watch: {
-    value (newValue, oldValue) {
-      if (newValue && newValue !== oldValue) {
-        this.selectedRowKeys = [newValue]
+    computeOfferingId (newValue, oldValue) {
+      if (newValue !== oldValue) {
+        this.cpuNumberInputValue = this.minCpu
+        this.memoryInputValue = this.minMemory
       }
     }
   },
+  mounted () {
+    this.cpuNumberInputValue = this.minCpu
+    this.memoryInputValue = this.minMemory
+    this.fillValue()
+  },
   methods: {
-    onSelectRow (value) {
-      this.selectedRowKeys = value
-      this.$emit('select-compute-item', value[0])
+    fillValue () {
+      if (this.preFillContent.cpunumber) {
+        this.cpuNumberInputValue = this.preFillContent.cpunumber
+      }
+      if (this.preFillContent.cpuspeed) {
+        this.cpuSpeedInputValue = this.preFillContent.cpuspeed
+      }
+      if (this.preFillContent.memory) {
+        this.memoryInputValue = this.preFillContent.memory
+      }
+    },
+    updateComputeCpuNumber (value) {
+      if (!this.validateInput('cpu', value)) {
+        return
+      }
+      this.$emit('update-compute-cpunumber', this.cpunumberInputDecorator, value)
+    },
+    updateComputeCpuSpeed (value) {
+      this.$emit('update-compute-cpuspeed', this.cpuspeedInputDecorator, value)
     },
-    handleSearch (value) {
-      this.filter = value
-      this.options.keyword = this.filter
-      this.$emit('handle-search-filter', this.options)
+    updateComputeMemory (value) {
+      if (!this.validateInput('memory', value)) {
+        return
+      }
+      this.$emit('update-compute-memory', this.memoryInputDecorator, value)
     },
-    handleTableChange (pagination) {
-      this.options.page = pagination.current
-      this.options.pageSize = pagination.pageSize
-      this.$emit('handle-search-filter', this.options)
+    validateInput (input, value) {
+      this.errors[input].status = ''
+      this.errors[input].message = ''
+
+      if (value === null || value === undefined || value.length === 0) {
+        this.errors[input].status = 'error'
+        this.errors[input].message = this.$t('message.error.required.input')
+        return false
+      }
+
+      if (!this.isConstrained) {
+        return true
+      }
+
+      let min
+      let max
+
+      switch (input) {
+        case 'cpu':
+          min = this.minCpu
+          max = this.maxCpu
+          break
+        case 'memory':
+          min = this.minMemory
+          max = this.maxMemory
+          break
+      }
+
+      if (!this.checkValidRange(value, min, max)) {
+        this.errors[input].status = 'error'
+        this.errors[input].message = this.$t('message.error.invalid.range', { min: min, max: max })
+        return false
+      }
+
+      return true
+    },
+    checkValidRange (value, min, max) {
+      if (value < min || value > max) {
+        return false
+      }
+
+      return true
     }
   }
 }
 </script>
-
-<style lang="less" scoped>
-  .ant-table-wrapper {
-    margin: 2rem 0;
-  }
-</style>
diff --git a/src/views/compute/wizard/DiskOfferingSelection.vue b/src/views/compute/wizard/DiskOfferingSelection.vue
index 33d94ac..78b06ef 100644
--- a/src/views/compute/wizard/DiskOfferingSelection.vue
+++ b/src/views/compute/wizard/DiskOfferingSelection.vue
@@ -64,6 +64,10 @@ export default {
     loading: {
       type: Boolean,
       default: false
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -93,15 +97,7 @@ export default {
     }
   },
   created () {
-    this.dataItems = []
-    this.dataItems.push({
-      id: '0',
-      name: this.$t('noselect'),
-      diskSize: undefined,
-      miniops: undefined,
-      maxiops: undefined,
-      isCustomized: undefined
-    })
+    this.initDataItem()
   },
   computed: {
     options () {
@@ -139,11 +135,34 @@ export default {
     },
     items (newData, oldData) {
       if (newData && newData.length > 0) {
+        this.initDataItem()
         this.dataItems = this.dataItems.concat(newData)
       }
+    },
+    loading () {
+      if (!this.loading) {
+        if (this.preFillContent.diskofferingid) {
+          this.selectedRowKeys = [this.preFillContent.diskofferingid]
+          this.$emit('select-disk-offering-item', this.preFillContent.diskofferingid)
+        } else {
+          this.selectedRowKeys = ['0']
+          this.$emit('select-disk-offering-item', '0')
+        }
+      }
     }
   },
   methods: {
+    initDataItem () {
+      this.dataItems = []
+      this.dataItems.push({
+        id: '0',
+        name: this.$t('noselect'),
+        diskSize: undefined,
+        miniops: undefined,
+        maxiops: undefined,
+        isCustomized: undefined
+      })
+    },
     onSelectRow (value) {
       this.selectedRowKeys = value
       this.$emit('select-disk-offering-item', value[0])
diff --git a/src/views/compute/wizard/DiskSizeSelection.vue b/src/views/compute/wizard/DiskSizeSelection.vue
index bec025a..684cb18 100644
--- a/src/views/compute/wizard/DiskSizeSelection.vue
+++ b/src/views/compute/wizard/DiskSizeSelection.vue
@@ -16,22 +16,26 @@
 // under the License.
 
 <template>
-  <a-form-item :label="this.$t('diskSize')">
-    <a-row>
-      <a-col :span="10">
+  <a-form-item
+    :label="this.$t('diskSize')"
+    class="form-item">
+    <a-row :gutter="12">
+      <a-col :md="10" :lg="10">
         <a-slider
           :min="0"
           :max="1024"
           v-model="inputValue"
-          @change="($event) => updateDickSize($event)"
+          @change="($event) => updateDiskSize($event)"
         />
       </a-col>
-      <a-col :span="4">
-        <a-input-number
-          v-model="inputValue"
-          :formatter="value => `${value} GB`"
-          :parser="value => value.replace(' GB', '')"
-        />
+      <a-col :md="4" :lg="4">
+        <span style="display: inline-flex">
+          <a-input-number
+            v-model="inputValue"
+            @change="($event) => updateDiskSize($event)"
+          />
+          <span style="padding-top: 6px">GB</span>
+        </span>
       </a-col>
     </a-row>
   </a-form-item>
@@ -44,6 +48,10 @@ export default {
     inputDecorator: {
       type: String,
       default: ''
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -51,14 +59,27 @@ export default {
       inputValue: 0
     }
   },
+  mounted () {
+    this.fillValue()
+  },
   methods: {
-    updateDickSize (value) {
+    fillValue () {
+      if (this.inputDecorator === 'rootdisksize') {
+        this.inputValue = this.preFillContent.rootdisksize ? this.preFillContent.rootdisksize : 0
+      } else if (this.inputDecorator === 'size') {
+        this.inputValue = this.preFillContent.size ? this.preFillContent.size : 0
+      }
+      this.$emit('update-disk-size', this.inputDecorator, this.inputValue)
+    },
+    updateDiskSize (value) {
       this.$emit('update-disk-size', this.inputDecorator, value)
     }
   }
 }
 </script>
 
-<style scoped>
-
+<style scoped lang="less">
+  .form-item {
+    margin: 0 5px;
+  }
 </style>
diff --git a/src/views/compute/wizard/NetworkConfiguration.vue b/src/views/compute/wizard/NetworkConfiguration.vue
index c53ed19..af20568 100644
--- a/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/src/views/compute/wizard/NetworkConfiguration.vue
@@ -28,13 +28,13 @@
     <template slot="ipAddress" slot-scope="text, record">
       <a-input
         style="width: 150px;"
-        :placeholder="$t('ipAddress')"
+        :placeholder="$t('ipaddress')"
         @change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)" />
     </template>
     <template slot="macAddress" slot-scope="text, record">
       <a-input
         style="width: 150px;"
-        :placeholder="$t('macAddress')"
+        :placeholder="$t('macaddress')"
         @change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)" />
     </template>
   </a-table>
@@ -51,6 +51,10 @@ export default {
     value: {
       type: String,
       default: ''
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
diff --git a/src/views/compute/wizard/NetworkSelection.vue b/src/views/compute/wizard/NetworkSelection.vue
index 21151f2..74678b0 100644
--- a/src/views/compute/wizard/NetworkSelection.vue
+++ b/src/views/compute/wizard/NetworkSelection.vue
@@ -22,6 +22,13 @@
       placeholder="Search"
       v-model="filter"
       @search="handleSearch" />
+    <a-tooltip
+      arrowPointAtCenter
+      placement="bottomRight">
+      <template slot="title">
+        {{ $t('addNewNetworks') }}
+      </template>
+    </a-tooltip>
     <a-table
       :loading="loading"
       :columns="columns"
@@ -70,6 +77,14 @@ export default {
     loading: {
       type: Boolean,
       default: false
+    },
+    zoneId: {
+      type: String,
+      default: () => ''
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -77,7 +92,11 @@ export default {
       filter: '',
       selectedRowKeys: [],
       vpcs: [],
-      filteredInfo: null
+      filteredInfo: null,
+      networkOffering: {
+        loading: false,
+        opts: []
+      }
     }
   },
   computed: {
@@ -147,8 +166,22 @@ export default {
       if (newValue && !_.isEqual(newValue, oldValue)) {
         this.selectedRowKeys = newValue
       }
+    },
+    loading () {
+      if (!this.loading) {
+        if (this.preFillContent.networkids) {
+          this.selectedRowKeys = this.preFillContent.networkids
+          this.$emit('select-network-item', this.preFillContent.networkids)
+        } else {
+          this.selectedRowKeys = []
+          this.$emit('select-network-item', null)
+        }
+      }
     }
   },
+  beforeCreate () {
+    this.form = this.$form.createForm(this)
+  },
   created () {
     api('listVPCs', {
       projectid: store.getters.project.id
@@ -156,6 +189,7 @@ export default {
       this.vpcs = _.get(response, 'listvpcsresponse.vpc')
     })
   },
+  inject: ['vmFetchNetworks'],
   methods: {
     getDetails (network) {
       return [
@@ -178,6 +212,24 @@ export default {
       this.options.page = pagination.current
       this.options.pageSize = pagination.pageSize
       this.$emit('handle-search-filter', this.options)
+    },
+    listNetworkOfferings () {
+      return new Promise((resolve, reject) => {
+        const args = {}
+        args.forvpc = false
+        args.zoneid = this.zoneId
+        args.guestiptype = 'Isolated'
+        args.supportedServices = 'SourceNat'
+        args.specifyvlan = false
+        args.state = 'Enabled'
+
+        api('listNetworkOfferings', args).then(json => {
+          const listNetworkOfferings = json.listnetworkofferingsresponse.networkoffering || []
+          resolve(listNetworkOfferings)
+        }).catch(error => {
+          resolve(error)
+        })
+      })
     }
   }
 }
diff --git a/src/views/compute/wizard/SshKeyPairSelection.vue b/src/views/compute/wizard/SshKeyPairSelection.vue
index 420f406..f443cfc 100644
--- a/src/views/compute/wizard/SshKeyPairSelection.vue
+++ b/src/views/compute/wizard/SshKeyPairSelection.vue
@@ -53,6 +53,10 @@ export default {
     loading: {
       type: Boolean,
       default: false
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -80,12 +84,7 @@ export default {
     }
   },
   created () {
-    this.dataItems = []
-    this.dataItems.push({
-      name: this.$t('noselect'),
-      account: '-',
-      domain: '-'
-    })
+    this.initDataItem()
   },
   computed: {
     options () {
@@ -121,11 +120,31 @@ export default {
     },
     items (newData, oldData) {
       if (newData && newData.length > 0) {
+        this.initDataItem()
         this.dataItems = this.dataItems.concat(newData)
       }
+    },
+    loading () {
+      if (!this.loading) {
+        if (this.preFillContent.keypair) {
+          this.selectedRowKeys = [this.preFillContent.keypair]
+          this.$emit('select-ssh-key-pair-item', this.preFillContent.keypair)
+        } else {
+          this.selectedRowKeys = [this.$t('noselect')]
+          this.$emit('select-ssh-key-pair-item', this.$t('noselect'))
+        }
+      }
     }
   },
   methods: {
+    initDataItem () {
+      this.dataItems = []
+      this.dataItems.push({
+        name: this.$t('noselect'),
+        account: '-',
+        domain: '-'
+      })
+    },
     onSelectRow (value) {
       this.selectedRowKeys = value
       this.$emit('select-ssh-key-pair-item', value[0])
diff --git a/src/views/compute/wizard/TemplateIsoRadioGroup.vue b/src/views/compute/wizard/TemplateIsoRadioGroup.vue
index 594b2d0..c01e753 100644
--- a/src/views/compute/wizard/TemplateIsoRadioGroup.vue
+++ b/src/views/compute/wizard/TemplateIsoRadioGroup.vue
@@ -81,6 +81,14 @@ export default {
     itemCount: {
       type: Number,
       default: 0
+    },
+    osType: {
+      type: String,
+      default: ''
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -90,16 +98,14 @@ export default {
       pageSize: 10
     }
   },
-  created () {
-    this.value = this.selected
-    this.$emit('emit-update-template-iso', this.inputDecorator, this.value)
-  },
-  watch: {
-    inputDecorator (value) {
-      if (value === 'templateid') {
-        this.value = this.selected
-      }
+  mounted () {
+    if (this.inputDecorator === 'templateid') {
+      this.value = !this.preFillContent.templateid ? this.selected : this.preFillContent.templateid
+    } else {
+      this.value = !this.preFillContent.isoid ? this.selected : this.preFillContent.isoid
     }
+
+    this.$emit('emit-update-template-iso', this.inputDecorator, this.value)
   },
   computed: {
     pagination () {
diff --git a/src/views/compute/wizard/TemplateIsoSelection.vue b/src/views/compute/wizard/TemplateIsoSelection.vue
index b26f893..7edef3c 100644
--- a/src/views/compute/wizard/TemplateIsoSelection.vue
+++ b/src/views/compute/wizard/TemplateIsoSelection.vue
@@ -17,26 +17,74 @@
 
 <template>
   <div>
-    <a-input-search
-      class="search-input"
-      placeholder="Search"
-      v-model="filter"
-      @search="filterDataSource"/>
+    <span class="filter-group">
+      <a-input-search
+        class="search-input"
+        placeholder="Search"
+        v-model="filter"
+        @search="filterDataSource">
+        <a-popover
+          placement="bottomRight"
+          slot="addonAfter"
+          trigger="click"
+          v-model="visibleFilter">
+          <template slot="content">
+            <a-form
+              style="width: 170px"
+              :form="form"
+              layout="vertical"
+              @submit="handleSubmit">
+              <a-form-item :label="$t('filter')">
+                <a-select
+                  allowClear
+                  v-decorator="['filter']">
+                  <a-select-option
+                    v-for="(opt) in filterOpts"
+                    :key="opt.id">{{ $t(opt.name) }}</a-select-option>
+                </a-select>
+              </a-form-item>
+              <div class="filter-group-button">
+                <a-button
+                  class="filter-group-button-clear"
+                  type="default"
+                  size="small"
+                  icon="stop"
+                  @click="onClear">Clear</a-button>
+                <a-button
+                  class="filter-group-button-search"
+                  type="primary"
+                  size="small"
+                  icon="search"
+                  @click="handleSubmit">Search</a-button>
+              </div>
+            </a-form>
+          </template>
+          <a-button
+            class="filter-group-button"
+            icon="filter"
+            size="small"/>
+        </a-popover>
+      </a-input-search>
+    </span>
     <a-spin :spinning="loading">
       <a-tabs
-        tabPosition="top"
         :animated="false"
-        :defaultActiveKey="Object.keys(dataSource)[0]">
+        :defaultActiveKey="Object.keys(dataSource)[0]"
+        tabPosition="top"
+        v-model="osType"
+        @change="changeOsName">
         <a-tab-pane v-for="(osList, osName) in dataSource" :key="osName">
           <span slot="tab">
             <os-logo :os-name="osName"></os-logo>
           </span>
           <TemplateIsoRadioGroup
-            :osType="osName"
+            v-if="osType===osName"
+            :osType="osType"
             :osList="dataSource[osName]"
             :input-decorator="inputDecorator"
             :selected="checkedValue"
             :itemCount="itemCount[osName]"
+            :preFillContent="preFillContent"
             @handle-filter-tag="filterDataSource"
             @emit-update-template-iso="updateTemplateIso"
           ></TemplateIsoRadioGroup>
@@ -72,6 +120,10 @@ export default {
     loading: {
       type: Boolean,
       default: false
+    },
+    preFillContent: {
+      type: Object,
+      default: () => {}
     }
   },
   data () {
@@ -80,7 +132,22 @@ export default {
       filteredItems: this.items,
       checkedValue: '',
       dataSource: {},
-      itemCount: {}
+      itemCount: {},
+      visibleFilter: false,
+      filterOpts: [{
+        id: 'featured',
+        name: 'featured'
+      }, {
+        id: 'community',
+        name: 'community'
+      }, {
+        id: 'selfexecutable',
+        name: 'selfexecutable'
+      }, {
+        id: 'sharedexecutable',
+        name: 'sharedexecutable'
+      }],
+      osType: ''
     }
   },
   watch: {
@@ -92,6 +159,7 @@ export default {
         this.checkedValue = items[0].id
       }
       this.dataSource = this.mappingDataSource()
+      this.osType = Object.keys(this.dataSource)[0]
     },
     inputDecorator (newValue, oldValue) {
       if (newValue !== oldValue) {
@@ -99,6 +167,10 @@ export default {
       }
     }
   },
+  beforeCreate () {
+    this.form = this.$form.createForm(this)
+  },
+  inject: ['vmFetchTemplates', 'vmFetchIsos'],
   methods: {
     mappingDataSource () {
       let mappedItems = {}
@@ -124,6 +196,7 @@ export default {
       return mappedItems
     },
     updateTemplateIso (name, id) {
+      this.checkedValue = id
       this.$emit('update-template-iso', name, id)
     },
     filterDataSource (strQuery) {
@@ -164,6 +237,31 @@ export default {
       }
 
       return arrResult
+    },
+    handleSubmit (e) {
+      e.preventDefault()
+      this.form.validateFields((err, values) => {
+        if (err) {
+          return
+        }
+        if (this.inputDecorator === 'template') {
+          this.vmFetchTemplates(values.filter)
+        } else {
+          this.vmFetchIsos(values.filter)
+        }
+      })
+    },
+    onClear () {
+      const field = { filter: undefined }
+      this.form.setFieldsValue(field)
+      if (this.inputDecorator === 'template') {
+        this.vmFetchTemplates()
+      } else {
+        this.vmFetchIsos()
+      }
+    },
+    changeOsName (value) {
+      this.osType = value
     }
   }
 }
@@ -188,4 +286,32 @@ export default {
   /deep/.ant-tabs-nav-scroll {
     min-height: 45px;
   }
+
+  .filter-group {
+    /deep/.ant-input-group-addon {
+      padding: 0 5px;
+    }
+
+    &-button {
+      background: inherit;
+      border: 0;
+      padding: 0;
+    }
+
+    &-button {
+      position: relative;
+      display: block;
+      min-height: 25px;
+
+      &-clear {
+        position: absolute;
+        left: 0;
+      }
+
+      &-search {
+        position: absolute;
+        right: 0;
+      }
+    }
+  }
 </style>