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

dahn 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 3fe2b46dd0b [UI] Add option to specify account/project while deploying 
VMs and creating networks (#8919)
3fe2b46dd0b is described below

commit 3fe2b46dd0b00a2dbca8c2bb154e61fe436869cb
Author: Bryan Lima <[email protected]>
AuthorDate: Wed Jun 12 04:10:32 2024 -0300

    [UI] Add option to specify account/project while deploying VMs and creating 
networks (#8919)
---
 ui/public/locales/en.json                          |   1 +
 ui/public/locales/pt_BR.json                       |   1 +
 ui/src/views/compute/AssignInstance.vue            | 167 +------------
 ui/src/views/compute/DeployVM.vue                  |  94 +++++++-
 ui/src/views/compute/wizard/OwnershipSelection.vue | 257 +++++++++++++++++++++
 ui/src/views/network/CreateIsolatedNetworkForm.vue | 171 +++-----------
 ui/src/views/network/CreateL2NetworkForm.vue       | 144 +++---------
 7 files changed, 421 insertions(+), 414 deletions(-)

diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index cb279c7a149..86a74384cb7 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1557,6 +1557,7 @@
 "label.ovmnetworklabel": "OVM traffic label",
 "label.ovs": "OVS",
 "label.owner.account": "Owner Account",
+"label.owner.type": "Owner type",
 "label.owners": "Owners",
 "label.pa": "Palo Alto",
 "label.page": "page",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index cc8e22d5e47..9d64cc27150 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -1155,6 +1155,7 @@
 "label.ovmnetworklabel": "R\u00f3tulo de tr\u00e1fego OVM",
 "label.ovs": "OVS",
 "label.owner.account": "Dono da conta",
+"label.owner.type": "Tipo de dono",
 "label.owners": "Donos",
 "label.pa": "Palo Alto",
 "label.page": "p\u00e1gina",
diff --git a/ui/src/views/compute/AssignInstance.vue 
b/ui/src/views/compute/AssignInstance.vue
index 9726dc21577..873d10f5770 100644
--- a/ui/src/views/compute/AssignInstance.vue
+++ b/ui/src/views/compute/AssignInstance.vue
@@ -29,86 +29,7 @@
         </template>
       </a-alert>
 
-      <div class="form__item">
-        <p class="form__label">{{ $t('label.accounttype') }}</p>
-        <a-select
-          v-model:value="selectedAccountType"
-          v-focus="true"
-          showSearch
-          optionFilterProp="value"
-          :filterOption="(input, option) => {
-            return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
-          }">
-          <a-select-option :value="$t('label.account')">{{ $t('label.account') 
}}</a-select-option>
-          <a-select-option :value="$t('label.project')">{{ $t('label.project') 
}}</a-select-option>
-        </a-select>
-      </div>
-
-      <div class="form__item">
-        <p class="form__label"><span class="required">*</span>{{ 
$t('label.domain') }}</p>
-        <a-select
-          @change="changeDomain"
-          v-model:value="selectedDomain"
-          showSearch
-          optionFilterProp="label"
-          :filterOption="(input, option) => {
-            return  option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
-          }" >
-          <a-select-option v-for="domain in domains" :key="domain.name" 
:value="domain.id" :label="domain.path || domain.name || domain.description">
-            <span>
-              <resource-icon v-if="domain && domain.icon" 
:image="domain.icon.base64image" size="1x" style="margin-right: 5px"/>
-              <block-outlined v-else style="margin-right: 5px" />
-              {{ domain.path || domain.name || domain.description }}
-            </span>
-          </a-select-option>
-        </a-select>
-      </div>
-
-      <template v-if="selectedAccountType === $t('label.account')">
-        <div class="form__item">
-          <p class="form__label"><span class="required">*</span>{{ 
$t('label.account') }}</p>
-          <a-select
-            @change="changeAccount"
-            v-model:value="selectedAccount"
-            showSearch
-            optionFilterProp="value"
-            :filterOption="(input, option) => {
-              return option.value.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
-            }" >
-            <a-select-option v-for="account in accounts" :key="account.name" 
:value="account.name">
-              <span>
-                <resource-icon v-if="account && account.icon" 
:image="account.icon.base64image" size="1x" style="margin-right: 5px"/>
-                <team-outlined v-else style="margin-right: 5px" />
-                {{ account.name }}
-              </span>
-            </a-select-option>
-          </a-select>
-          <span v-if="accountError" class="required">{{ $t('label.required') 
}}</span>
-        </div>
-      </template>
-
-      <template v-else>
-        <div class="form__item">
-          <p class="form__label"><span class="required">*</span>{{ 
$t('label.project') }}</p>
-          <a-select
-            @change="changeProject"
-            v-model:value="selectedProject"
-            showSearch
-            optionFilterProp="label"
-            :filterOption="(input, option) => {
-              return  option.label.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
-            }" >
-            <a-select-option v-for="project in projects" :key="project.id" 
:value="project.id" :label="project.name">
-              <span>
-                <resource-icon v-if="project && project.icon" 
:image="project.icon.base64image" size="1x" style="margin-right: 5px"/>
-                <project-outlined v-else style="margin-right: 5px" />
-                {{ project.name }}
-              </span>
-            </a-select-option>
-          </a-select>
-          <span v-if="projectError" class="required">{{ $t('label.required') 
}}</span>
-        </div>
-      </template>
+      <ownership-selection @fetch-owner="fetchOwnerOptions"/>
 
       <div class="form__item">
         <p class="form__label">{{ $t('label.network') }}</p>
@@ -146,6 +67,7 @@
 <script>
 import { api } from '@/api'
 import ResourceIcon from '@/components/view/ResourceIcon'
+import OwnershipSelection from '@views/compute/wizard/OwnershipSelection'
 
 export default {
   name: 'AssignInstance',
@@ -156,7 +78,8 @@ export default {
     }
   },
   components: {
-    ResourceIcon
+    ResourceIcon,
+    OwnershipSelection
   },
   inject: ['parentFetchData'],
   data () {
@@ -175,60 +98,13 @@ export default {
       loading: false
     }
   },
-  created () {
-    this.fetchData()
-  },
   methods: {
-    fetchData () {
-      this.loading = true
-      api('listDomains', {
-        response: 'json',
-        listAll: true,
-        showicon: true,
-        details: 'min'
-      }).then(response => {
-        this.domains = response.listdomainsresponse.domain || []
-        this.selectedDomain = this.domains[0].id
-        this.fetchAccounts()
-        this.fetchProjects()
-      }).catch(error => {
-        this.$notifyError(error)
-      }).finally(() => {
-        this.loading = false
-      })
-    },
-    fetchAccounts () {
-      this.loading = true
-      api('listAccounts', {
-        response: 'json',
-        domainId: this.selectedDomain,
-        showicon: true,
-        state: 'Enabled',
-        isrecursive: false
-      }).then(response => {
-        this.accounts = response.listaccountsresponse.account || []
-      }).catch(error => {
-        this.$notifyError(error)
-      }).finally(() => {
-        this.loading = false
-      })
-    },
-    fetchProjects () {
-      this.loading = true
-      api('listProjects', {
-        response: 'json',
-        domainId: this.selectedDomain,
-        state: 'Active',
-        showicon: true,
-        details: 'min',
-        isrecursive: false
-      }).then(response => {
-        this.projects = response.listprojectsresponse.project || []
-      }).catch(error => {
-        this.$notifyError(error)
-      }).finally(() => {
-        this.loading = false
-      })
+    fetchOwnerOptions (selectedOptions) {
+      this.selectedAccountType = selectedOptions.selectedAccountType
+      this.selectedAccount = selectedOptions.selectedAccount
+      this.selectedDomain = selectedOptions.selectedDomain
+      this.selectedProject = selectedOptions.selectedProject
+      this.fetchNetworks()
     },
     fetchNetworks () {
       this.loading = true
@@ -252,23 +128,6 @@ export default {
         this.loading = false
       })
     },
-    changeDomain () {
-      this.selectedAccount = null
-      this.selectedProject = null
-      this.selectedNetwork = null
-      this.fetchAccounts()
-      this.fetchProjects()
-    },
-    changeAccount () {
-      this.selectedProject = null
-      this.selectedNetwork = null
-      this.fetchNetworks()
-    },
-    changeProject () {
-      this.selectedAccount = null
-      this.selectedNetwork = null
-      this.fetchNetworks()
-    },
     closeAction () {
       this.$emit('close-action')
     },
@@ -356,12 +215,6 @@ export default {
     }
   }
 
-  .required {
-    margin-right: 2px;
-    color: red;
-    font-size: 0.7rem;
-  }
-
   .loading {
     position: absolute;
     top: 0;
diff --git a/ui/src/views/compute/DeployVM.vue 
b/ui/src/views/compute/DeployVM.vue
index ef3926bbca0..865ee79d76c 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -29,6 +29,17 @@
             layout="vertical"
           >
             <a-steps direction="vertical" size="small">
+              <a-step
+                v-if="!isNormalUserOrProject"
+                :title="this.$t('label.assign.instance.another')">
+                <template #description>
+                  <div style="margin-top: 15px">
+                    {{ $t('label.assigning.vms') }}
+                    <ownership-selection
+                      @fetch-owner="fetchOwnerOptions"/>
+                  </div>
+                </template>
+              </a-step>
               <a-step :title="$t('label.select.deployment.infrastructure')" 
status="process">
                 <template #description>
                   <div style="margin-top: 15px">
@@ -848,6 +859,7 @@ import { mixin, mixinDevice } from '@/utils/mixin.js'
 import store from '@/store'
 import eventBus from '@/config/eventBus'
 
+import OwnershipSelection from '@views/compute/wizard/OwnershipSelection'
 import InfoCard from '@/components/view/InfoCard'
 import ResourceIcon from '@/components/view/ResourceIcon'
 import ComputeOfferingSelection from 
'@views/compute/wizard/ComputeOfferingSelection'
@@ -868,6 +880,7 @@ import InstanceNicsNetworkSelectListView from 
'@/components/view/InstanceNicsNet
 export default {
   name: 'Wizard',
   components: {
+    OwnershipSelection,
     SshKeyPairSelection,
     UserDataSelection,
     NetworkConfiguration,
@@ -965,6 +978,11 @@ export default {
         hosts: false,
         groups: false
       },
+      owner: {
+        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
+      },
       instanceConfig: {},
       template: {},
       defaultBootType: '',
@@ -1056,11 +1074,21 @@ export default {
     isNormalAndDomainUser () {
       return ['DomainAdmin', 
'User'].includes(this.$store.getters.userInfo.roletype)
     },
+    isNormalUserOrProject () {
+      return ['User'].includes(this.$store.getters.userInfo.roletype) || 
store.getters.project.id
+    },
     diskSize () {
-      const rootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 0)
-      const customDiskSize = _.get(this.instanceConfig, 'size', 0)
+      let dataDiskSize
+      let rootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 0)
       const diskOfferingDiskSize = _.get(this.diskOffering, 'disksize', 0)
-      const dataDiskSize = diskOfferingDiskSize > 0 ? diskOfferingDiskSize : 
customDiskSize
+      const customDiskSize = _.get(this.instanceConfig, 'size', 0)
+
+      if (this.vm.isoid != null) {
+        rootDiskSize = diskOfferingDiskSize > 0 ? diskOfferingDiskSize : 
customDiskSize
+      } else {
+        dataDiskSize = diskOfferingDiskSize > 0 ? diskOfferingDiskSize : 
customDiskSize
+      }
+
       const size = []
       if (rootDiskSize > 0) {
         size.push(`${rootDiskSize} GB (Root)`)
@@ -1079,6 +1107,9 @@ export default {
           list: 'listServiceOfferings',
           options: {
             zoneid: _.get(this.zone, 'id'),
+            projectid: this.owner.projectid,
+            domainid: this.owner.domainid,
+            account: this.owner.account,
             issystem: false,
             page: 1,
             pageSize: 10,
@@ -1089,6 +1120,9 @@ export default {
           list: 'listDiskOfferings',
           options: {
             zoneid: _.get(this.zone, 'id'),
+            projectid: this.owner.projectid,
+            domainid: this.owner.domainid,
+            account: this.owner.account,
             page: 1,
             pageSize: 10,
             keyword: undefined
@@ -1111,6 +1145,9 @@ export default {
           options: {
             page: 1,
             pageSize: 10,
+            account: this.owner.account,
+            domainid: this.owner.domainid,
+            projectid: this.owner.projectid,
             keyword: undefined,
             listall: false
           }
@@ -1138,9 +1175,9 @@ export default {
           options: {
             zoneid: _.get(this.zone, 'id'),
             canusefordeploy: true,
-            projectid: store.getters.project ? store.getters.project.id : null,
-            domainid: store.getters.project && store.getters.project.id ? null 
: store.getters.userInfo.domainid,
-            account: store.getters.project && store.getters.project.id ? null 
: store.getters.userInfo.account,
+            projectid: store.getters.project.id || this.owner.projectid,
+            domainid: store.getters.project.id ? null : this.owner.domainid,
+            account: store.getters.project.id ? null : this.owner.account,
             page: 1,
             pageSize: 10,
             keyword: undefined,
@@ -1303,7 +1340,7 @@ export default {
       return tabList
     },
     showSecurityGroupSection () {
-      return (this.networks.length > 0 && this.zone.securitygroupsenabled) || 
(this.zone && this.zone.networktype === 'Basic')
+      return (this.networks.length > 0 && this.zone?.securitygroupsenabled) || 
(this.zone?.networktype === 'Basic')
     },
     isUserAllowedToListSshKeys () {
       return Boolean('listSSHKeyPairs' in this.$store.getters.apis)
@@ -1372,7 +1409,7 @@ export default {
           this.diskOffering = _.find(this.options.diskOfferings, (option) => 
option.id === instanceConfig.diskofferingid)
         }
 
-        this.zone = _.find(this.options.zones, (option) => option.id === 
instanceConfig.zoneid)
+        this.zone = _.find(this.options.zones, (option) => option.id === 
this.instanceConfig.zoneid)
         this.affinityGroups = _.filter(this.options.affinityGroups, (option) 
=> _.includes(instanceConfig.affinitygroupids, option.id))
         this.networks = 
this.getSelectedNetworksWithExistingConfig(_.filter(this.options.networks, 
(option) => _.includes(instanceConfig.networkids, option.id)))
 
@@ -1685,8 +1722,8 @@ export default {
     fetchInstaceGroups () {
       this.options.instanceGroups = []
       api('listInstanceGroups', {
-        account: this.$store.getters.userInfo.account,
-        domainid: this.$store.getters.userInfo.domainid,
+        account: this.$store.getters.project?.id ? null : 
this.$store.getters.userInfo.account,
+        domainid: this.$store.getters.project?.id ? null : 
this.$store.getters.userInfo.domainid,
         listall: true
       }).then(response => {
         const groups = response.listinstancegroupsresponse.instancegroup || []
@@ -1830,7 +1867,7 @@ export default {
       this.userDataParams = []
       api('listUserData', { id: id }).then(json => {
         const resp = json?.listuserdataresponse?.userdata || []
-        if (resp) {
+        if (resp[0]) {
           var params = resp[0].params
           if (params) {
             var dataParams = params.split(',')
@@ -2086,6 +2123,14 @@ export default {
           deployVmData.bootintosetup = values.bootintosetup
         }
 
+        if (this.owner.account) {
+          deployVmData.account = this.owner.account
+          deployVmData.domainid = this.owner.domainid
+        } else if (this.owner.projectid) {
+          deployVmData.domainid = this.owner.domainid
+          deployVmData.projectid = this.owner.projectid
+        }
+
         const title = this.$t('label.launch.vm')
         const description = values.name || ''
         const password = this.$t('label.password')
@@ -2178,6 +2223,29 @@ export default {
         }
       })
     },
+    fetchOwnerOptions (OwnerOptions) {
+      this.owner = {
+        projectid: null,
+        domainid: store.getters.userInfo.domainid,
+        account: store.getters.userInfo.account
+      }
+      if (OwnerOptions.selectedAccountType === this.$t('label.account')) {
+        if (!OwnerOptions.selectedAccount) {
+          return
+        }
+        this.owner.account = OwnerOptions.selectedAccount
+        this.owner.domainid = OwnerOptions.selectedDomain
+        this.owner.projectid = null
+      } else if (OwnerOptions.selectedAccountType === 
this.$t('label.project')) {
+        if (!OwnerOptions.selectedProject) {
+          return
+        }
+        this.owner.account = null
+        this.owner.domainid = null
+        this.owner.projectid = OwnerOptions.selectedProject
+      }
+      this.resetData()
+    },
     fetchZones (zoneId, listZoneAllow) {
       this.zones = []
       return new Promise((resolve) => {
@@ -2275,6 +2343,9 @@ export default {
         args.pageSize = args.pageSize || 10
       }
       args.zoneid = _.get(this.zone, 'id')
+      args.account = store.getters.project?.id ? null : this.owner.account
+      args.domainid = store.getters.project?.id ? null : this.owner.domainid
+      args.projectid = store.getters.project?.id || this.owner.projectid
       args.templatefilter = templateFilter
       args.details = 'all'
       args.showicon = 'true'
@@ -2531,6 +2602,7 @@ export default {
       }
     },
     resetFromTemplateConfiguration () {
+      this.deleteFrom(this.instanceConfig, ['disksize', 'rootdisksize'])
       this.deleteFrom(this.params.serviceOfferings.options, ['templateid', 
'cpuspeed', 'cpunumber', 'memory'])
       this.deleteFrom(this.dataPreFill, ['cpuspeed', 'cpunumber', 'memory'])
       this.handleSearchFilter('serviceOfferings', {
diff --git a/ui/src/views/compute/wizard/OwnershipSelection.vue 
b/ui/src/views/compute/wizard/OwnershipSelection.vue
new file mode 100644
index 00000000000..61c30f340ac
--- /dev/null
+++ b/ui/src/views/compute/wizard/OwnershipSelection.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>
+  <a-form layout="vertical" >
+    <a-form-item :label="$t('label.owner.type')">
+      <a-select
+        @change="changeDomain"
+        v-model:value="selectedAccountType"
+        defaultValue="account"
+        autoFocus
+        showSearch
+        optionFilterProp="label"
+        :filterOption="
+          (input, option) => {
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          }
+        "
+      >
+        <a-select-option :value="$t('label.account')">{{ $t('label.account') 
}}</a-select-option>
+        <a-select-option :value="$t('label.project')">{{ $t('label.project') 
}}</a-select-option>
+      </a-select>
+    </a-form-item>
+    <a-form-item :label="$t('label.domain')" required>
+      <a-select
+        @change="changeDomain"
+        v-model:value="selectedDomain"
+        showSearch
+        optionFilterProp="label"
+        :filterOption="
+          (input, option) => {
+            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          }
+        "
+      >
+        <a-select-option
+          v-for="domain in domains"
+          :key="domain.name"
+          :value="domain.id"
+          :label="domain.path || domain.name || domain.description"
+        >
+          <span>
+            <resource-icon
+              v-if="domain && domain.icon"
+              :image="domain.icon.base64image"
+              size="1x"
+              style="margin-right: 5px"
+            />
+            <block-outlined v-else />
+            {{ domain.path || domain.name || domain.description }}
+          </span>
+        </a-select-option>
+      </a-select>
+    </a-form-item>
+
+    <template v-if="selectedAccountType === $t('label.account')">
+      <a-form-item :label="$t('label.account')" required>
+        <a-select
+          @change="emitChangeEvent"
+          v-model:value="selectedAccount"
+          showSearch
+          optionFilterProp="label"
+          :filterOption="
+            (input, option) => {
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
+            }
+          "
+        >
+          <a-select-option v-for="account in accounts" :key="account.name" 
:value="account.name">
+            <span>
+              <resource-icon
+                v-if="account && account.icon"
+                :image="account.icon.base64image"
+                size="1x"
+                style="margin-right: 5px"
+              />
+              <team-outlined v-else />
+              {{ account.name }}
+            </span>
+          </a-select-option>
+        </a-select>
+      </a-form-item>
+    </template>
+
+    <template v-else>
+      <a-form-item :label="$t('label.project')" required>
+        <a-select
+          @change="emitChangeEvent"
+          v-model:value="selectedProject"
+          showSearch
+          optionFilterProp="label"
+          :filterOption="
+            (input, option) => {
+              return option.label.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
+            }
+          "
+        >
+          <a-select-option v-for="project in projects" :key="project.id" 
:value="project.id" :label="project.name">
+            <span>
+              <resource-icon
+                v-if="project && project.icon"
+                :image="project.icon.base64image"
+                size="1x"
+                style="margin-right: 5px"
+              />
+              <project-outlined v-else />
+              {{ project.name }}
+            </span>
+          </a-select-option>
+        </a-select>
+      </a-form-item>
+    </template>
+  </a-form>
+</template>
+
+<script>
+import { api } from '@/api'
+import ResourceIcon from '@/components/view/ResourceIcon.vue'
+
+export default {
+  name: 'OwnershipSelection',
+  components: { ResourceIcon },
+  data () {
+    return {
+      domains: [],
+      accounts: [],
+      projects: [],
+      selectedAccountType: this.$store.getters.project?.id ? 
this.$t('label.project') : this.$t('label.account'),
+      selectedDomain: null,
+      selectedAccount: null,
+      selectedProject: null,
+      loading: false
+    }
+  },
+  props: {
+    override: {
+      type: Object
+    }
+  },
+  created () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      this.loading = true
+      api('listDomains', {
+        response: 'json',
+        listAll: true,
+        showicon: true,
+        details: 'min'
+      })
+        .then((response) => {
+          this.domains = response.listdomainsresponse.domain
+          if (this.override) {
+            this.domains = this.domains.filter(item => 
this.override.domains.has(item.id))
+          }
+          if (this.domains.length === 0) {
+            this.selectedDomain = null
+            this.selectedProject = null
+            this.selectedAccount = null
+            return
+          }
+          const domainIds = this.domains?.map(domain => domain.id)
+          const ownerDomainId = this.$store.getters.project?.domainid || 
this.$store.getters.userInfo.domainid
+          this.selectedDomain = domainIds?.includes(ownerDomainId) ? 
ownerDomainId : this.domains?.[0]?.id
+          this.changeDomain()
+        })
+        .catch((error) => {
+          this.$notifyError(error)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    fetchAccounts () {
+      this.loading = true
+      api('listAccounts', {
+        response: 'json',
+        domainId: this.selectedDomain,
+        showicon: true,
+        state: 'Enabled',
+        isrecursive: false
+      })
+        .then((response) => {
+          this.accounts = response.listaccountsresponse.account || []
+          if (this.override?.accounts && this.accounts) {
+            this.accounts = this.accounts.filter(item => 
this.override.accounts.has(item.name))
+          }
+          const accountNames = this.accounts.map(account => account.name)
+          if (this.selectedDomain === this.$store.getters.userInfo.domainid && 
accountNames.includes(this.$store.getters.userInfo.account)) {
+            this.selectedAccount = this.$store.getters.userInfo.account
+          } else {
+            this.selectedAccount = this.accounts?.[0]?.name
+          }
+          this.selectedProject = null
+          this.emitChangeEvent()
+        })
+        .catch((error) => {
+          this.$notifyError(error)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    fetchProjects () {
+      this.loading = true
+      api('listProjects', {
+        response: 'json',
+        domainId: this.selectedDomain,
+        state: 'Active',
+        showicon: true,
+        details: 'min',
+        isrecursive: false
+      })
+        .then((response) => {
+          this.projects = response.listprojectsresponse.project
+          if (this.override?.projects && this.projects) {
+            this.projects = this.projects.filter(item => 
this.override.projects.has(item.id))
+          }
+          this.selectedProject = this.projects?.map(project => 
project.id)?.includes(this.$store.getters.project?.id) ? 
this.$store.getters.project?.id : this.projects?.[0]?.id
+          this.selectedAccount = null
+          this.emitChangeEvent()
+        })
+        .catch((error) => {
+          this.$notifyError(error)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    changeDomain () {
+      if (this.selectedAccountType === this.$t('label.account')) {
+        this.fetchAccounts()
+      } else {
+        this.fetchProjects()
+      }
+    },
+    emitChangeEvent () {
+      this.$emit('fetch-owner', this)
+    }
+  }
+}
+</script>
diff --git a/ui/src/views/network/CreateIsolatedNetworkForm.vue 
b/ui/src/views/network/CreateIsolatedNetworkForm.vue
index 919821576e0..0c81a69057c 100644
--- a/ui/src/views/network/CreateIsolatedNetworkForm.vue
+++ b/ui/src/views/network/CreateIsolatedNetworkForm.vue
@@ -66,44 +66,7 @@
               </a-select-option>
             </a-select>
           </a-form-item>
-          <a-form-item ref="domainid" name="domainid" 
v-if="isAdminOrDomainAdmin()">
-            <template #label>
-              <tooltip-label :title="$t('label.domainid')" 
:tooltip="apiParams.domainid.description"/>
-            </template>
-            <a-select
-             v-model:value="form.domainid"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.label.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
-              }"
-              :loading="domain.loading"
-              :placeholder="apiParams.domainid.description"
-              @change="val => { handleDomainChange(domains[val]) }">
-              <a-select-option v-for="(opt, optIndex) in domains" 
:key="optIndex" :label="opt.path || opt.name || opt.description">
-                {{ opt.path || opt.name || opt.description }}
-              </a-select-option>
-            </a-select>
-          </a-form-item>
-          <a-form-item ref="account" name="account" v-if="accountVisible">
-            <template #label>
-              <tooltip-label :title="$t('label.account')" 
:tooltip="apiParams.account.description"/>
-            </template>
-            <a-select
-             v-model:value="form.account"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }"
-              :loading="accountLoading"
-              :placeholder="apiParams.account.description"
-              @change="val => { handleAccountChange(accounts[val]) }">
-              <a-select-option v-for="(opt, optIndex) in accounts" 
:key="optIndex">
-                {{ opt.name || opt.description }}
-              </a-select-option>
-            </a-select>
-          </a-form-item>
+          <ownership-selection v-if="isAdminOrDomainAdmin()" 
@fetch-owner="fetchOwnerOptions"/>
           <a-form-item
             ref="networkdomain"
             name="networkdomain"
@@ -299,28 +262,6 @@
               v-model:value="form.sourcenatipaddress"
               :placeholder="apiParams.sourcenatipaddress?.description"/>
           </a-form-item>
-          <a-form-item
-            ref="networkdomain"
-            name="networkdomain"
-            v-if="!isObjectEmpty(selectedNetworkOffering) && 
!selectedNetworkOffering.forvpc">
-            <template #label>
-              <tooltip-label :title="$t('label.networkdomain')" 
:tooltip="apiParams.networkdomain.description"/>
-            </template>
-            <a-input
-             v-model:value="form.networkdomain"
-              :placeholder="apiParams.networkdomain.description"/>
-          </a-form-item>
-          <a-form-item
-            ref="account"
-            name="account"
-            v-if="accountVisible">
-            <template #label>
-              <tooltip-label :title="$t('label.account')" 
:tooltip="apiParams.account.description"/>
-            </template>
-            <a-input
-             v-model:value="form.account"
-              :placeholder="apiParams.account.description"/>
-          </a-form-item>
           <div :span="24" class="action-button">
             <a-button
               :loading="actionLoading"
@@ -349,13 +290,15 @@ import { isAdmin, isAdminOrDomainAdmin } from '@/role'
 import { mixinForm } from '@/utils/mixin'
 import ResourceIcon from '@/components/view/ResourceIcon'
 import TooltipLabel from '@/components/widgets/TooltipLabel'
+import OwnershipSelection from '@/views/compute/wizard/OwnershipSelection.vue'
 
 export default {
   name: 'CreateIsolatedNetworkForm',
   mixins: [mixinForm],
   components: {
     TooltipLabel,
-    ResourceIcon
+    ResourceIcon,
+    OwnershipSelection
   },
   props: {
     loading: {
@@ -374,13 +317,9 @@ export default {
   data () {
     return {
       actionLoading: false,
-      domains: [],
-      domain: { loading: false },
-      selectedDomain: {},
+      owner: {},
       accountVisible: isAdminOrDomainAdmin(),
-      accounts: [],
       accountLoading: false,
-      selectedAccount: {},
       zones: [],
       zoneLoading: false,
       selectedZone: {},
@@ -410,12 +349,6 @@ export default {
     this.apiParams = this.$getApiParams('createNetwork')
   },
   created () {
-    this.domains = [
-      {
-        id: '-1',
-        name: ' '
-      }
-    ]
     this.initForm()
     this.fetchData()
   },
@@ -449,7 +382,6 @@ export default {
       })
     },
     fetchData () {
-      this.fetchDomainData()
       this.fetchZoneData()
       this.allowSettingMTU()
     },
@@ -496,44 +428,31 @@ export default {
       this.publicMtuMax = zone?.routerpublicinterfacemaxmtu || 1500
       this.updateVPCCheckAndFetchNetworkOfferingData()
     },
-    fetchDomainData () {
-      if ('listDomains' in this.$store.getters.apis) {
-        this.domain.loading = true
-        this.loadMore('listDomains', 1, this.domain)
+    fetchOwnerOptions (OwnerOptions) {
+      this.owner = {
+        projectid: null,
+        domainid: this.$store.getters.userInfo.domainid,
+        account: this.$store.getters.userInfo.account
       }
-    },
-    loadMore (apiToCall, page, sema) {
-      const params = {}
-      params.listAll = true
-      params.details = 'min'
-      params.pagesize = 100
-      params.page = page
-      var count
-      api(apiToCall, params).then(json => {
-        const listDomains = json.listdomainsresponse.domain
-        count = json.listdomainsresponse.count
-        this.domains = this.domains.concat(listDomains)
-      }).finally(() => {
-        if (count <= this.domains.length) {
-          sema.loading = false
-        } else {
-          this.loadMore(apiToCall, page + 1, sema)
+      if (OwnerOptions.selectedAccountType === this.$t('label.account')) {
+        if (!OwnerOptions.selectedAccount) {
+          return
         }
-        this.form.domainid = 0
-        this.handleDomainChange(this.domains[0])
-      })
-    },
-    handleDomainChange (domain) {
-      this.selectedDomain = domain
-      this.accountVisible = domain.id !== '-1'
+        this.owner.account = OwnerOptions.selectedAccount
+        this.owner.domainid = OwnerOptions.selectedDomain
+        this.owner.projectid = null
+      } else if (OwnerOptions.selectedAccountType === 
this.$t('label.project')) {
+        if (!OwnerOptions.selectedProject) {
+          return
+        }
+        this.owner.account = null
+        this.owner.domainid = null
+        this.owner.projectid = OwnerOptions.selectedProject
+      }
       if (isAdminOrDomainAdmin()) {
         this.updateVPCCheckAndFetchNetworkOfferingData()
-        this.fetchAccounts()
       }
     },
-    handleAccountChange (account) {
-      this.selectedAccount = account
-    },
     updateVPCCheckAndFetchNetworkOfferingData () {
       if (this.vpc !== null) { // from VPC section
         this.fetchNetworkOfferingData(true)
@@ -562,8 +481,8 @@ export default {
         guestiptype: 'Isolated',
         state: 'Enabled'
       }
-      if (isAdminOrDomainAdmin() && this.selectedDomain.id !== '-1') { // 
domain is visible only for admins
-        params.domainid = this.selectedDomain.id
+      if (isAdminOrDomainAdmin() && this.owner.domainid !== '-1') { // domain 
is visible only for admins
+        params.domainid = this.owner.domainid
       }
       if (!isAdmin()) { // normal user is not aware of the VLANs in the 
system, so normal user is not allowed to create network with network offerings 
whose specifyvlan = true
         params.specifyvlan = false
@@ -613,31 +532,6 @@ export default {
         }
       })
     },
-    fetchAccounts () {
-      this.accountLoading = true
-      var params = {}
-      if (isAdminOrDomainAdmin() && this.selectedDomain.id !== '-1') { // 
domain is visible only for admins
-        params.domainid = this.selectedDomain.id
-      }
-      this.accounts = [
-        {
-          id: '-1',
-          name: ' '
-        }
-      ]
-      this.selectedAccount = {}
-      api('listAccounts', params).then(json => {
-        const listAccounts = json.listaccountsresponse.account || []
-        this.accounts = this.accounts.concat(listAccounts)
-      }).catch(error => {
-        this.$notifyError(error)
-      }).finally(() => {
-        this.accountLoading = false
-        if (this.arrayHasItems(this.accounts)) {
-          this.form.account = null
-        }
-      })
-    },
     handleSubmit () {
       if (this.actionLoading) return
       this.formRef.value.validate().then(() => {
@@ -666,12 +560,15 @@ export default {
         if ('vpcid' in values) {
           params.vpcid = this.selectedVpc.id
         }
-        if ('domainid' in values && values.domainid > 0) {
-          params.domainid = this.selectedDomain.id
-          if (this.isValidTextValueForKey(values, 'account') && 
this.selectedAccount.id !== '-1') {
-            params.account = this.selectedAccount.name
-          }
+
+        if (this.owner.account) {
+          params.account = this.owner.account
+          params.domainid = this.owner.domainid
+        } else if (this.owner.projectid) {
+          params.domainid = this.owner.domainid
+          params.projectid = this.owner.projectid
         }
+
         api('createNetwork', params).then(json => {
           this.$notification.success({
             message: 'Network',
diff --git a/ui/src/views/network/CreateL2NetworkForm.vue 
b/ui/src/views/network/CreateL2NetworkForm.vue
index 6bb87e178f3..41d2ec8c2f2 100644
--- a/ui/src/views/network/CreateL2NetworkForm.vue
+++ b/ui/src/views/network/CreateL2NetworkForm.vue
@@ -73,48 +73,7 @@
               </a-select-option>
             </a-select>
           </a-form-item>
-          <a-form-item v-if="isAdminOrDomainAdmin()" name="domainid" 
ref="domainid">
-            <template #label>
-              <tooltip-label :title="$t('label.domainid')" 
:tooltip="apiParams.domainid.description"/>
-            </template>
-            <a-select
-              v-model:value="form.domainid"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return option.label.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
-              }"
-              :loading="domainLoading"
-              :placeholder="apiParams.domainid.description"
-              @change="val => { handleDomainChange(domains[val]) }">
-              <a-select-option v-for="(opt, optIndex) in this.domains" 
:key="optIndex" :label="opt.path || opt.name || opt.description">
-                <span>
-                  <resource-icon v-if="opt && opt.icon" 
:image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
-                  <block-outlined v-else-if="optIndex !== 0" 
style="margin-right: 5px" />
-                  {{ opt.path || opt.name || opt.description }}
-                </span>
-              </a-select-option>
-            </a-select>
-          </a-form-item>
-          <a-form-item v-if="accountVisible" name="account" ref="account">
-            <template #label>
-              <tooltip-label :title="$t('label.account')" 
:tooltip="apiParams.account.description"/>
-            </template>
-            <a-select
-             v-model:value="form.account"
-              showSearch
-              optionFilterProp="label"
-              :filterOption="(input, option) => {
-                return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
-              }"
-              :loading="accountLoading"
-              :placeholder="apiParams.account.description"
-              @change="val => { handleAccountChange(accounts[val]) }">
-              <a-select-option v-for="(opt, optIndex) in accounts" 
:key="optIndex">
-                {{ opt.name || opt.description }}
-              </a-select-option>
-            </a-select>
-          </a-form-item>
+          <ownership-selection v-if="isAdminOrDomainAdmin()" 
@fetch-owner="fetchOwnerOptions"/>
           <a-form-item name="networkofferingid" ref="networkofferingid">
             <template #label>
               <tooltip-label :title="$t('label.networkofferingid')" 
:tooltip="apiParams.networkofferingid.description"/>
@@ -217,11 +176,13 @@ import { isAdmin, isAdminOrDomainAdmin } from '@/role'
 import { mixinForm } from '@/utils/mixin'
 import ResourceIcon from '@/components/view/ResourceIcon'
 import TooltipLabel from '@/components/widgets/TooltipLabel'
+import OwnershipSelection from '@/views/compute/wizard/OwnershipSelection.vue'
 
 export default {
   name: 'CreateL2NetworkForm',
   mixins: [mixinForm],
   components: {
+    OwnershipSelection,
     TooltipLabel,
     ResourceIcon
   },
@@ -242,13 +203,8 @@ export default {
   data () {
     return {
       actionLoading: false,
-      domains: [],
-      domainLoading: false,
-      selectedDomain: {},
+      owner: {},
       accountVisible: isAdminOrDomainAdmin(),
-      accounts: [],
-      accountLoading: false,
-      selectedAccount: {},
       zones: [],
       zoneLoading: false,
       selectedZone: {},
@@ -271,12 +227,6 @@ export default {
     this.apiParams = this.$getApiParams('createNetwork')
   },
   created () {
-    this.domains = [
-      {
-        id: '-1',
-        name: ' '
-      }
-    ]
     this.initForm()
     this.fetchData()
   },
@@ -294,7 +244,6 @@ export default {
       })
     },
     fetchData () {
-      this.fetchDomainData()
       this.fetchZoneData()
     },
     isAdminOrDomainAdmin () {
@@ -339,32 +288,31 @@ export default {
       this.isNsxEnabled = zone?.isnsxenabled || false
       this.updateVPCCheckAndFetchNetworkOfferingData()
     },
-    fetchDomainData () {
-      const params = {}
-      params.listAll = true
-      params.showicon = true
-      params.details = 'min'
-      this.domainLoading = true
-      api('listDomains', params).then(json => {
-        const listDomains = json.listdomainsresponse.domain
-        this.domains = this.domains.concat(listDomains)
-      }).finally(() => {
-        this.domainLoading = false
-        this.form.domainid = 0
-        this.handleDomainChange(this.domains[0])
-      })
-    },
-    handleDomainChange (domain) {
-      this.selectedDomain = domain
-      this.accountVisible = domain.id !== '-1'
+    fetchOwnerOptions (OwnerOptions) {
+      this.owner = {
+        projectid: null,
+        domainid: this.$store.getters.userInfo.domainid,
+        account: this.$store.getters.userInfo.account
+      }
+      if (OwnerOptions.selectedAccountType === this.$t('label.account')) {
+        if (!OwnerOptions.selectedAccount) {
+          return
+        }
+        this.owner.account = OwnerOptions.selectedAccount
+        this.owner.domainid = OwnerOptions.selectedDomain
+        this.owner.projectid = null
+      } else if (OwnerOptions.selectedAccountType === 
this.$t('label.project')) {
+        if (!OwnerOptions.selectedProject) {
+          return
+        }
+        this.owner.account = null
+        this.owner.domainid = null
+        this.owner.projectid = OwnerOptions.selectedProject
+      }
       if (isAdminOrDomainAdmin()) {
         this.updateVPCCheckAndFetchNetworkOfferingData()
-        this.fetchAccounts()
       }
     },
-    handleAccountChange (account) {
-      this.selectedAccount = account
-    },
     updateVPCCheckAndFetchNetworkOfferingData () {
       if (this.vpc !== null) { // from VPC section
         this.fetchNetworkOfferingData(true)
@@ -393,8 +341,8 @@ export default {
         guestiptype: 'L2',
         state: 'Enabled'
       }
-      if (isAdminOrDomainAdmin() && this.selectedDomain.id !== '-1') { // 
domain is visible only for admins
-        params.domainid = this.selectedDomain.id
+      if (isAdminOrDomainAdmin() && this.owner.domainid !== '-1') { // domain 
is visible only for admins
+        params.domainid = this.owner.domainid
       }
       if (!isAdmin()) { // normal user is not aware of the VLANs in the 
system, so normal user is not allowed to create network with network offerings 
whose specifyvlan = true
         params.specifyvlan = false
@@ -417,31 +365,6 @@ export default {
     handleNetworkOfferingChange (networkOffering) {
       this.selectedNetworkOffering = networkOffering
     },
-    fetchAccounts () {
-      this.accountLoading = true
-      var params = {}
-      if (isAdminOrDomainAdmin() && this.selectedDomain.id !== '-1') { // 
domain is visible only for admins
-        params.domainid = this.selectedDomain.id
-      }
-      this.accounts = [
-        {
-          id: '-1',
-          name: ' '
-        }
-      ]
-      this.selectedAccount = {}
-      api('listAccounts', params).then(json => {
-        const listAccounts = json.listaccountsresponse.account || []
-        this.accounts = this.accounts.concat(listAccounts)
-      }).catch(error => {
-        this.$notifyError(error)
-      }).finally(() => {
-        this.accountLoading = false
-        if (this.arrayHasItems(this.accounts)) {
-          this.form.account = null
-        }
-      })
-    },
     handleSubmit (e) {
       if (this.actionLoading) return
       this.formRef.value.validate().then(() => {
@@ -460,12 +383,15 @@ export default {
         if (this.isValidValueForKey(values, 'bypassvlanoverlapcheck')) {
           params.bypassvlanoverlapcheck = values.bypassvlanoverlapcheck
         }
-        if ('domainid' in values && values.domainid > 0) {
-          params.domainid = this.selectedDomain.id
-          if (this.isValidTextValueForKey(values, 'account') && 
this.selectedAccount.id !== '-1') {
-            params.account = this.selectedAccount.name
-          }
+
+        if (this.owner.account) {
+          params.account = this.owner.account
+          params.domainid = this.owner.domainid
+        } else if (this.owner.projectid) {
+          params.domainid = this.owner.domainid
+          params.projectid = this.owner.projectid
         }
+
         if (this.isValidValueForKey(values, 'isolatedpvlantype') && 
values.isolatedpvlantype !== 'none') {
           params.isolatedpvlantype = values.isolatedpvlantype
           if (this.isValidValueForKey(values, 'isolatedpvlan')) {


Reply via email to