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

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

commit ae2ffbe40bfc52ad32baf0b7296a04c14d4d9b4d
Merge: 4201a2dc11b 97be6f2e5d1
Author: Abhishek Kumar <[email protected]>
AuthorDate: Fri Jan 31 16:54:22 2025 +0530

    Merge remote-tracking branch 'apache/4.19' into 4.20

 .../cloudstack/api/response/NetworkResponse.java   |   8 +
 .../main/java/com/cloud/user/UserAccountVO.java    |  16 +-
 .../cloud.idempotent_update_api_permission.sql     |  52 +++++
 .../resources/META-INF/db/schema-41910to41920.sql  |  22 ++
 .../allocator/AbstractStoragePoolAllocator.java    |  16 +-
 packaging/el8/cloud.spec                           |   2 +-
 .../KubernetesClusterScaleWorker.java              |   2 +-
 .../contrail/management/MockAccountManager.java    |   4 +
 scripts/util/create-kubernetes-binaries-iso.sh     |   2 +-
 .../main/java/com/cloud/api/ApiResponseHelper.java |   1 +
 server/src/main/java/com/cloud/api/ApiServer.java  |   3 +-
 .../java/com/cloud/storage/StorageManagerImpl.java |  20 +-
 .../com/cloud/storage/VolumeApiServiceImpl.java    | 121 ++++++----
 .../main/java/com/cloud/user/AccountManager.java   |   7 +-
 .../java/com/cloud/user/AccountManagerImpl.java    |  37 ++-
 .../cloud/storage/VolumeApiServiceImplTest.java    | 252 ++++++++++++++++++++-
 .../com/cloud/user/AccountManagerImplTest.java     |  56 ++++-
 .../com/cloud/user/MockAccountManagerImpl.java     |  45 ++--
 ui/src/components/view/ListView.vue                |  16 +-
 ui/src/components/view/SearchView.vue              |   3 +
 ui/src/config/section/network.js                   |  15 +-
 ui/src/views/iam/DomainView.vue                    |  10 +
 ui/src/views/setting/ConfigurationValue.vue        |  23 ++
 23 files changed, 622 insertions(+), 111 deletions(-)

diff --cc engine/schema/src/main/java/com/cloud/user/UserAccountVO.java
index d204f67dc93,a9d4ca9a2b9..e4fcbad6b02
--- a/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java
+++ b/engine/schema/src/main/java/com/cloud/user/UserAccountVO.java
@@@ -33,12 -32,13 +33,12 @@@ import javax.persistence.Table
  import javax.persistence.Transient;
  
  import org.apache.cloudstack.api.InternalIdentity;
++import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
++import org.apache.commons.lang3.StringUtils;
  
  import com.cloud.utils.db.Encrypt;
  import com.cloud.utils.db.GenericDao;
- import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
- import org.apache.commons.lang3.StringUtils;
  
 -import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
 -import org.apache.commons.lang3.StringUtils;
 -
  @Entity
  @Table(name = "user")
  @SecondaryTable(name = "account", pkJoinColumns = {@PrimaryKeyJoinColumn(name 
= "account_id", referencedColumnName = "id")})
@@@ -379,4 -370,9 +373,10 @@@ public class UserAccountVO implements U
      public void setDetails(Map<String, String> details) {
          this.details = details;
      }
+ 
+     @Override
+     public String toString() {
 -        return String.format("User %s", 
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "name", 
"uuid"));
++        return String.format("UserAccount %s.", 
ReflectionToStringBuilderUtils.reflectOnlySelectedFields
++                (this, "id", "uuid", "username", "accountName"));
+     }
  }
diff --cc 
engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
index 63524ccb6db,9e0a28875d1..2c034d8429a
--- 
a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
+++ 
b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java
@@@ -130,15 -131,22 +129,22 @@@ public abstract class AbstractStoragePo
              return null;
          }
  
-         if (pools.get(0).getPoolType().isShared()) {
+         short capacityType = Capacity.CAPACITY_TYPE_LOCAL_STORAGE;
+         String storageType = "local";
+         StoragePool storagePool = pools.get(0);
+         if (storagePool.isShared()) {
              capacityType = Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED;
-         } else {
-             capacityType = Capacity.CAPACITY_TYPE_LOCAL_STORAGE;
+             storageType = "shared";
          }
  
 -        s_logger.debug(String.format(
++        logger.debug(String.format(
+                 "Filtering storage pools by capacity type [%s] as the first 
storage pool of the list, with name [%s] and ID [%s], is a [%s] storage.",
+                 capacityType, storagePool.getName(), storagePool.getUuid(), 
storageType
+         ));
+ 
          List<Long> poolIdsByCapacity = 
capacityDao.orderHostsByFreeCapacity(zoneId, clusterId, capacityType);
  
 -        s_logger.debug(String.format("List of pools in descending order of 
available capacity [%s].", poolIdsByCapacity));
 +        logger.debug(String.format("List of pools in descending order of 
available capacity [%s].", poolIdsByCapacity));
  
  
        //now filter the given list of Pools by this ordered list
@@@ -221,6 -229,8 +227,8 @@@
      }
  
      List<StoragePool> reorderStoragePoolsBasedOnAlgorithm(List<StoragePool> 
pools, DeploymentPlan plan, Account account) {
 -        s_logger.debug(String.format("Using allocation algorithm [%s] to 
reorder pools.", allocationAlgorithm));
++        logger.debug(String.format("Using allocation algorithm [%s] to 
reorder pools.", allocationAlgorithm));
+ 
          if (allocationAlgorithm.equals("random") || 
allocationAlgorithm.equals("userconcentratedpod_random") || (account == null)) {
              reorderRandomPools(pools);
          } else if (StringUtils.equalsAny(allocationAlgorithm, 
"userdispersing", "firstfitleastconsumed")) {
diff --cc packaging/el8/cloud.spec
index fbbb7abe350,00000000000..244f4431a3b
mode 100644,000000..100644
--- a/packaging/el8/cloud.spec
+++ b/packaging/el8/cloud.spec
@@@ -1,712 -1,0 +1,712 @@@
 +# 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.
 +
 +%define __os_install_post %{nil}
 +%global debug_package %{nil}
 +%global __requires_exclude libc\\.so\\..*
 +%define _binaries_in_noarch_packages_terminate_build   0
 +
 +# DISABLE the post-percentinstall java repacking and line number stripping
 +# we need to find a way to just disable the java repacking and line number 
stripping, but not the autodeps
 +
 +Name:      cloudstack
 +Summary:   CloudStack IaaS Platform
 +#http://fedoraproject.org/wiki/PackageNamingGuidelines#Pre-Release_packages
 +%define _maventag %{_fullver}
 +Release:   %{_rel}
 +
 +Version:   %{_ver}
 +License:   ASL 2.0
 +Vendor:    Apache CloudStack <[email protected]>
 +Packager:  Apache CloudStack <[email protected]>
 +Group:     System Environment/Libraries
 +# FIXME do groups for every single one of the subpackages
 +Source0:   %{name}-%{_maventag}.tgz
 +BuildRoot: %{_tmppath}/%{name}-%{_maventag}-%{release}-build
 +BuildArch: noarch
 +
 +BuildRequires: (java-11-openjdk-devel or java-17-openjdk-devel)
 +#BuildRequires: ws-commons-util
 +BuildRequires: jpackage-utils
 +BuildRequires: gcc
 +BuildRequires: glibc-devel
 +BuildRequires: /usr/bin/mkisofs
 +BuildRequires: python3-setuptools
 +BuildRequires: wget
 +BuildRequires: nodejs
 +
 +%description
 +CloudStack is a highly-scalable elastic, open source,
 +intelligent IaaS cloud implementation.
 +
 +%package management
 +Summary:   CloudStack management server UI
 +Requires: java-17-openjdk
 +Requires: (tzdata-java or timezone-java)
 +Requires: python3
 +Requires: bash
 +Requires: gawk
 +Requires: which
 +Requires: file
 +Requires: tar
 +Requires: bzip2
 +Requires: gzip
 +Requires: unzip
 +Requires: /sbin/mount.nfs
 +Requires: (openssh-clients or openssh)
 +Requires: (nfs-utils or nfs-client)
 +Requires: iproute
 +Requires: wget
- Requires: mysql
++Requires: (mysql or mariadb)
 +Requires: sudo
 +Requires: /sbin/service
 +Requires: /sbin/chkconfig
 +Requires: /usr/bin/ssh-keygen
 +Requires: (genisoimage or mkisofs)
 +Requires: ipmitool
 +Requires: %{name}-common = %{_ver}
 +Requires: (iptables-services or iptables)
 +Requires: rng-tools
 +Requires: (qemu-img or qemu-tools)
 +Requires: python3-pip
 +Requires: python3-setuptools
 +Requires: (libgcrypt > 1.8.3 or libgcrypt20)
 +Group:     System Environment/Libraries
 +%description management
 +The CloudStack management server is the central point of coordination,
 +management, and intelligence in CloudStack.
 +
 +%package common
 +Summary: Apache CloudStack common files and scripts
 +Requires: python3
 +Group:   System Environment/Libraries
 +%description common
 +The Apache CloudStack files shared between agent and management server
 +%global __requires_exclude ^(libuuid\\.so\\.1|/usr/bin/python)$
 +
 +%package agent
 +Summary: CloudStack Agent for KVM hypervisors
 +Requires: (openssh-clients or openssh)
 +Requires: java-17-openjdk
 +Requires: tzdata-java
 +Requires: %{name}-common = %{_ver}
 +Requires: libvirt
 +Requires: libvirt-daemon-driver-storage-rbd
 +Requires: ebtables
 +Requires: iptables
 +Requires: ethtool
 +Requires: (net-tools or net-tools-deprecated)
 +Requires: iproute
 +Requires: ipset
 +Requires: perl
 +Requires: rsync
 +Requires: (python3-libvirt or python3-libvirt-python)
 +Requires: (qemu-img or qemu-tools)
 +Requires: qemu-kvm
 +Requires: cryptsetup
 +Requires: rng-tools
 +Requires: (libgcrypt > 1.8.3 or libgcrypt20)
 +Requires: (selinux-tools if selinux-tools)
 +Requires: sysstat
 +Provides: cloud-agent
 +Group: System Environment/Libraries
 +%description agent
 +The CloudStack agent for KVM hypervisors
 +
 +%package baremetal-agent
 +Summary: CloudStack baremetal agent
 +Requires: tftp-server
 +Requires: xinetd
 +Requires: syslinux
 +Requires: chkconfig
 +Requires: dhcp
 +Requires: httpd
 +Group:     System Environment/Libraries
 +%description baremetal-agent
 +The CloudStack baremetal agent
 +
 +%package usage
 +Summary: CloudStack Usage calculation server
 +Requires: java-17-openjdk
 +Requires: tzdata-java
 +Group: System Environment/Libraries
 +%description usage
 +The CloudStack usage calculation service
 +
 +%package ui
 +Summary: CloudStack UI
 +Group: System Environment/Libraries
 +%description ui
 +The CloudStack UI
 +
 +%package marvin
 +Summary: Apache CloudStack Marvin library
 +Requires: python3-pip
 +Requires: gcc
 +Requires: python3-devel
 +Requires: libffi-devel
 +Requires: openssl-devel
 +Group: System Environment/Libraries
 +%description marvin
 +Apache CloudStack Marvin library
 +
 +%package integration-tests
 +Summary: Apache CloudStack Marvin integration tests
 +Requires: %{name}-marvin = %{_ver}
 +Group: System Environment/Libraries
 +%description integration-tests
 +Apache CloudStack Marvin integration tests
 +
 +%if "%{_ossnoss}" == "noredist"
 +%package mysql-ha
 +Summary: Apache CloudStack Balancing Strategy for MySQL
 +Group: System Environmnet/Libraries
 +%description mysql-ha
 +Apache CloudStack Balancing Strategy for MySQL
 +
 +%endif
 +
 +%prep
 +echo Doing CloudStack build
 +
 +%setup -q -n %{name}-%{_maventag}
 +
 +%build
 +
 +cp packaging/el8/replace.properties build/replace.properties
 +echo VERSION=%{_maventag} >> build/replace.properties
 +echo PACKAGE=%{name} >> build/replace.properties
 +touch build/gitrev.txt
 +echo $(git rev-parse HEAD) > build/gitrev.txt
 +
 +if [ "%{_ossnoss}" == "NOREDIST" -o "%{_ossnoss}" == "noredist" ] ; then
 +   echo "Adding noredist flag to the maven build"
 +   FLAGS="$FLAGS -Dnoredist"
 +fi
 +
 +if [ "%{_sim}" == "SIMULATOR" -o "%{_sim}" == "simulator" ] ; then
 +   echo "Adding simulator flag to the maven build"
 +   FLAGS="$FLAGS -Dsimulator"
 +fi
 +
 +if [ \"%{_temp}\" != "" ]; then
 +    echo "Adding flags to package requested templates"
 +    FLAGS="$FLAGS `rpm --eval %{?_temp}`"
 +fi
 +
 +mvn -Psystemvm,developer $FLAGS clean package
 +cd ui && npm install && npm run build && cd ..
 +
 +%install
 +[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT}
 +# Common directories
 +mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/ipallocator
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/work
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/temp
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/mnt
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/management
 +mkdir -p ${RPM_BUILD_ROOT}%{_initrddir}
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/default
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/profile.d
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d
 +
 +# Common
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site
 +mkdir -p ${RPM_BUILD_ROOT}/usr/bin
 +cp -r scripts/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
 +install -D systemvm/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/
 +install python/lib/cloud_utils.py 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/cloud_utils.py
 +cp -r python/lib/cloudutils 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/
 +python3 -m py_compile 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/cloud_utils.py
 +python3 -m compileall 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/python-site/cloudutils
 +cp build/gitrev.txt ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
 +cp packaging/el8/cloudstack-sccs ${RPM_BUILD_ROOT}/usr/bin
 +
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts/network/cisco
 +cp -r plugins/network-elements/cisco-vnmc/src/main/scripts/network/cisco/* 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts/network/cisco
 +
 +# Management
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management
 +mkdir -p 
${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/run
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel
 +
 +# Setup Jetty
 +ln -sf /etc/%{name}/management 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/conf
 +ln -sf /var/log/%{name}/management 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/logs
 +
 +install -D client/target/utilities/bin/cloud-migrate-databases 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-migrate-databases
 +install -D client/target/utilities/bin/cloud-set-guest-password 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-set-guest-password
 +install -D client/target/utilities/bin/cloud-set-guest-sshkey 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-set-guest-sshkey
 +install -D client/target/utilities/bin/cloud-setup-databases 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-databases
 +install -D client/target/utilities/bin/cloud-setup-encryption 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-encryption
 +install -D client/target/utilities/bin/cloud-setup-management 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-management
 +install -D client/target/utilities/bin/cloud-setup-baremetal 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-baremetal
 +install -D client/target/utilities/bin/cloud-sysvmadm 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-sysvmadm
 +install -D client/target/utilities/bin/cloud-update-xenserver-licenses 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-update-xenserver-licenses
 +# Bundle cmk in cloudstack-management
 +CMK_REL=$(wget -O - 
"https://api.github.com/repos/apache/cloudstack-cloudmonkey/releases"; 
2>/dev/null | jq -r '.[0].tag_name')
 +wget 
https://github.com/apache/cloudstack-cloudmonkey/releases/download/$CMK_REL/cmk.linux.x86-64
 -O ${RPM_BUILD_ROOT}%{_bindir}/cmk
 +chmod +x ${RPM_BUILD_ROOT}%{_bindir}/cmk
 +
 +cp -r client/target/utilities/scripts/db/* 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup
 +
 +cp -r client/target/cloud-client-ui-%{_maventag}.jar 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/
 +cp -r client/target/classes/META-INF/webapp 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp
 +cp ui/dist/config.json ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/
 +cp -r ui/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp/
 +rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp/config.json
 +ln -sf /etc/%{name}/management/config.json 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapp/config.json
 +mv 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/cloud-client-ui-%{_maventag}.jar
 ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/cloudstack-%{_maventag}.jar
 +cp client/target/lib/*jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib/
 +
 +# Don't package the scripts in the management webapp
 +rm -rf 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/scripts
 +rm -rf 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/vms
 +
 +for name in db.properties server.properties log4j-cloud.xml 
environment.properties java.security.ciphers
 +do
 +  cp client/target/conf/$name 
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/$name
 +done
 +
 +ln -sf log4j-cloud.xml  
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/log4j2.xml
 +
 +install python/bindir/cloud-external-ipallocator.py 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-external-ipallocator.py
 +install -D client/target/pythonlibs/jasypt-1.9.3.jar 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
 +install -D utils/target/cloud-utils-%{_maventag}-bundled.jar 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/%{name}-utils.jar
 +
 +install -D packaging/el8/cloud-ipallocator.rc 
${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator
 +install -D packaging/el8/cloud.limits 
${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud
 +install -D packaging/el8/filelimit.conf 
${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d
 +install -D packaging/systemd/cloudstack-management.service 
${RPM_BUILD_ROOT}%{_unitdir}/%{name}-management.service
 +install -D packaging/systemd/cloudstack-management.default 
${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-management
 +install -D server/target/conf/cloudstack-sudoers 
${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management
 +touch ${RPM_BUILD_ROOT}%{_localstatedir}/run/%{name}-management.pid
 +#install -D server/target/conf/cloudstack-catalina.logrotate 
${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-catalina
 +install -D server/target/conf/cloudstack-management.logrotate 
${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-management
 +
 +# SystemVM template
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/templates/systemvm
 +cp -r engine/schema/dist/systemvm-templates/* 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/templates/systemvm
 +rm -rf 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/templates/systemvm/md5sum.txt
 +
 +# UI
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/ui
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/
 +cp -r client/target/classes/META-INF/webapp/WEB-INF 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui
 +cp ui/dist/config.json ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/ui/
 +cp -r ui/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/
 +rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/config.json
 +ln -sf /etc/%{name}/ui/config.json 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/config.json
 +
 +# Package mysql-connector-python
 +wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel 
https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl
 +wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel 
https://files.pythonhosted.org/packages/e9/93/4860cebd5ad3ff2664ad3c966490ccb46e3b88458b2095145bca11727ca4/setuptools-47.3.1-py3-none-any.whl
 +wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel 
https://files.pythonhosted.org/packages/32/27/1141a8232723dcb10a595cc0ce4321dcbbd5215300bf4acfc142343205bf/protobuf-3.19.6-py2.py3-none-any.whl
 +wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel 
https://files.pythonhosted.org/packages/08/1f/42d74bae9dd6dcfec67c9ed0f3fa482b1ae5ac5f117ca82ab589ecb3ca19/mysql_connector_python-8.0.31-py2.py3-none-any.whl
 +
 +chmod 440 ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management
 +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/mnt
 +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/management
 +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/work
 +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/cache/%{name}/management/temp
 +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management
 +chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent
 +
 +# KVM Agent
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/agent
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/plugins
 +install -D packaging/systemd/cloudstack-agent.service 
${RPM_BUILD_ROOT}%{_unitdir}/%{name}-agent.service
 +install -D packaging/systemd/[email protected] 
${RPM_BUILD_ROOT}%{_unitdir}/%{name}[email protected]
 +install -D packaging/systemd/cloudstack-agent.default 
${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-agent
 +install -D agent/target/transformed/agent.properties 
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/agent.properties
 +install -D agent/target/transformed/environment.properties 
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/environment.properties
 +install -D agent/target/transformed/log4j-cloud.xml 
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/agent/log4j-cloud.xml
 +install -D agent/target/transformed/cloud-setup-agent 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-setup-agent
 +install -D agent/target/transformed/cloudstack-agent-upgrade 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-agent-upgrade
 +install -D agent/target/transformed/cloud-guest-tool 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-guest-tool
 +install -D agent/target/transformed/libvirtqemuhook 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook
 +install -D agent/target/transformed/rolling-maintenance 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/rolling-maintenance
 +install -D agent/target/transformed/cloud-ssh 
${RPM_BUILD_ROOT}%{_bindir}/%{name}-ssh
 +install -D agent/target/transformed/cloudstack-agent-profile.sh 
${RPM_BUILD_ROOT}%{_sysconfdir}/profile.d/%{name}-agent-profile.sh
 +install -D agent/target/transformed/cloudstack-agent.logrotate 
${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-agent
 +install -D 
plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar 
${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar
 +cp plugins/hypervisors/kvm/target/dependencies/*  
${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
 +cp plugins/storage/volume/storpool/target/*.jar  
${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
 +cp plugins/storage/volume/linstor/target/*.jar  
${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib
 +
 +# Usage server
 +mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/lib
 +install -D usage/target/cloud-usage-%{_maventag}.jar 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/cloud-usage-%{_maventag}.jar
 +install -D usage/target/transformed/db.properties 
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage/db.properties
 +install -D usage/target/transformed/log4j-cloud_usage.xml 
${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage/log4j-cloud.xml
 +cp usage/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/lib/
 +cp client/target/lib/mysql*jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-usage/lib/
 +install -D packaging/systemd/cloudstack-usage.service 
${RPM_BUILD_ROOT}%{_unitdir}/%{name}-usage.service
 +install -D packaging/systemd/cloudstack-usage.default 
${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-usage
 +mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/usage/
 +install -D usage/target/transformed/cloudstack-usage.logrotate 
${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-usage
 +
 +# Marvin
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-marvin
 +cp tools/marvin/dist/Marvin-*.tar.gz 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-marvin/
 +
 +# integration-tests
 +mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-integration-tests
 +cp -r test/integration/* 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-integration-tests/
 +
 +# MYSQL HA
 +if [ "x%{_ossnoss}" == "xnoredist" ] ; then
 +  mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-mysql-ha/lib
 +  cp -r 
plugins/database/mysql-ha/target/cloud-plugin-database-mysqlha-%{_maventag}.jar 
${RPM_BUILD_ROOT}%{_datadir}/%{name}-mysql-ha/lib
 +fi
 +
 +#License files from whisker
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-management-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-management-%{version}/LICENSE
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-common-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-common-%{version}/LICENSE
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-agent-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-agent-%{version}/LICENSE
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-usage-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-usage-%{version}/LICENSE
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-ui-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-ui-%{version}/LICENSE
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-marvin-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-marvin-%{version}/LICENSE
 +install -D tools/whisker/NOTICE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-integration-tests-%{version}/NOTICE
 +install -D tools/whisker/LICENSE 
${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-integration-tests-%{version}/LICENSE
 +
 +%clean
 +[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT}
 +
 +%posttrans common
 +
 +unalias cp
 +python_dir=$(python3 -c "from distutils.sysconfig import get_python_lib; 
print(get_python_lib(1))")
 +if [ ! -z $python_dir ];then
 +  cp -f -r /usr/share/cloudstack-common/python-site/* $python_dir/
 +fi
 +
 +%preun management
 +/usr/bin/systemctl stop cloudstack-management || true
 +/usr/bin/systemctl disable cloudstack-management || true
 +
 +%pre management
 +id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack 
unprivileged user" \
 +     -r -s /bin/sh -d %{_localstatedir}/cloudstack/management cloud || true
 +
 +rm -rf %{_localstatedir}/cache/cloudstack
 +
 +# in case of upgrade to 4.9+ copy commands.properties if not exists in 
/etc/cloudstack/management/
 +if [ "$1" == "2" ] ; then
 +    if [ -f 
"%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/commands.properties"
 ] && [ ! -f "%{_sysconfdir}/%{name}/management/commands.properties" ] ; then
 +        cp -p 
%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/commands.properties
 %{_sysconfdir}/%{name}/management/commands.properties
 +    fi
 +fi
 +
 +# Remove old tomcat symlinks and env config file
 +if [ -L "%{_datadir}/%{name}-management/lib" ]
 +then
 +    rm -f %{_datadir}/%{name}-management/bin
 +    rm -f %{_datadir}/%{name}-management/lib
 +    rm -f %{_datadir}/%{name}-management/temp
 +    rm -f %{_datadir}/%{name}-management/work
 +    rm -f %{_sysconfdir}/default/%{name}-management
 +fi
 +
 +%post management
 +# Install mysql-connector-python
 +pip3 install 
%{_datadir}/%{name}-management/setup/wheel/six-1.15.0-py2.py3-none-any.whl 
%{_datadir}/%{name}-management/setup/wheel/setuptools-47.3.1-py3-none-any.whl 
%{_datadir}/%{name}-management/setup/wheel/protobuf-3.19.6-py2.py3-none-any.whl 
%{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.0.31-py2.py3-none-any.whl
 +
 +/usr/bin/systemctl enable cloudstack-management > /dev/null 2>&1 || true
 +/usr/bin/systemctl enable --now rngd > /dev/null 2>&1 || true
 +
 +grep -s -q "db.cloud.driver=jdbc:mysql" 
"%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e 
"\$adb.cloud.driver=jdbc:mysql" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "db.usage.driver=jdbc:mysql" 
"%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e 
"\$adb.usage.driver=jdbc:mysql"  
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "db.simulator.driver=jdbc:mysql" 
"%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e 
"\$adb.simulator.driver=jdbc:mysql" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +
 +# Update DB properties having master and slave(s), with source and replica(s) 
respectively (for inclusiveness)
 +grep -s -q "^db.cloud.slaves=" 
"%{_sysconfdir}/%{name}/management/db.properties" && sed -i 
"s/^db.cloud.slaves=/db.cloud.replicas=/g" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "^db.cloud.secondsBeforeRetryMaster=" 
"%{_sysconfdir}/%{name}/management/db.properties" && sed -i 
"s/^db.cloud.secondsBeforeRetryMaster=/db.cloud.secondsBeforeRetrySource=/g" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "^db.cloud.queriesBeforeRetryMaster=" 
"%{_sysconfdir}/%{name}/management/db.properties" && sed -i 
"s/^db.cloud.queriesBeforeRetryMaster=/db.cloud.queriesBeforeRetrySource=/g" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "^db.usage.slaves=" 
"%{_sysconfdir}/%{name}/management/db.properties" && sed -i 
"s/^db.usage.slaves=/db.usage.replicas=/g" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "^db.usage.secondsBeforeRetryMaster=" 
"%{_sysconfdir}/%{name}/management/db.properties" && sed -i 
"s/^db.usage.secondsBeforeRetryMaster=/db.usage.secondsBeforeRetrySource=/g" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +grep -s -q "^db.usage.queriesBeforeRetryMaster=" 
"%{_sysconfdir}/%{name}/management/db.properties" && sed -i 
"s/^db.usage.queriesBeforeRetryMaster=/db.usage.queriesBeforeRetrySource=/g" 
"%{_sysconfdir}/%{name}/management/db.properties"
 +
 +if [ ! -f 
%{_datadir}/cloudstack-common/scripts/vm/hypervisor/xenserver/vhd-util ] ; then
 +    echo Please download vhd-util from 
http://download.cloudstack.org/tools/vhd-util and put it in
 +    echo %{_datadir}/cloudstack-common/scripts/vm/hypervisor/xenserver/
 +fi
 +
 +if [ -f %{_sysconfdir}/sysconfig/%{name}-management ] ; then
 +    rm -f %{_sysconfdir}/sysconfig/%{name}-management
 +fi
 +
 +chown -R cloud:cloud /var/log/cloudstack/management
 +
 +systemctl daemon-reload
 +
 +%posttrans management
 +# Print help message
 +if [ -f "/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text" 
];then
 +    sed -i "s,^ACS_VERSION=.*,ACS_VERSION=%{_maventag},g" 
/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text
 +    /usr/share/cloudstack-common/scripts/installer/cloudstack-help-text 
management
 +fi
 +
 +%preun agent
 +/sbin/service cloudstack-agent stop || true
 +if [ "$1" == "0" ] ; then
 +    /sbin/chkconfig --del cloudstack-agent > /dev/null 2>&1 || true
 +fi
 +
 +%pre agent
 +
 +# save old configs if they exist (for upgrade). Otherwise we may lose them
 +# when the old packages are erased. There are a lot of properties files here.
 +if [ -d "%{_sysconfdir}/cloud" ] ; then
 +    mv %{_sysconfdir}/cloud %{_sysconfdir}/cloud.rpmsave
 +fi
 +
 +%posttrans agent
 +
 +if [ "$1" == "2" ] ; then
 +    echo "Running %{_bindir}/%{name}-agent-upgrade to update bridge name for 
upgrade from CloudStack 4.0.x (and before) to CloudStack 4.1 (and later)"
 +    %{_bindir}/%{name}-agent-upgrade
 +fi
 +if [ ! -d %{_sysconfdir}/libvirt/hooks ] ; then
 +    mkdir %{_sysconfdir}/libvirt/hooks
 +fi
 +cp -a ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib/libvirtqemuhook 
%{_sysconfdir}/libvirt/hooks/qemu
 +mkdir -m 0755 -p /usr/share/cloudstack-agent/tmp
 +/usr/bin/systemctl restart libvirtd
 +/usr/bin/systemctl enable cloudstack-agent > /dev/null 2>&1 || true
 +/usr/bin/systemctl enable cloudstack-rolling-maintenance@p > /dev/null 2>&1 
|| true
 +/usr/bin/systemctl enable --now rngd > /dev/null 2>&1 || true
 +
 +# if saved configs from upgrade exist, copy them over
 +if [ -f "%{_sysconfdir}/cloud.rpmsave/agent/agent.properties" ]; then
 +    mv %{_sysconfdir}/%{name}/agent/agent.properties  
%{_sysconfdir}/%{name}/agent/agent.properties.rpmnew
 +    cp -p %{_sysconfdir}/cloud.rpmsave/agent/agent.properties 
%{_sysconfdir}/%{name}/agent
 +    # make sure we only do this on the first install of this RPM, don't want 
to overwrite on a reinstall
 +    mv %{_sysconfdir}/cloud.rpmsave/agent/agent.properties 
%{_sysconfdir}/cloud.rpmsave/agent/agent.properties.rpmsave
 +fi
 +
 +systemctl daemon-reload
 +
 +# Print help message
 +if [ -f "/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text" 
];then
 +    sed -i "s,^ACS_VERSION=.*,ACS_VERSION=%{_maventag},g" 
/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text
 +    /usr/share/cloudstack-common/scripts/installer/cloudstack-help-text agent
 +fi
 +
 +%pre usage
 +id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack 
unprivileged user" \
 +     -r -s /bin/sh -d %{_localstatedir}/cloudstack/management cloud|| true
 +
 +%preun usage
 +/sbin/service cloudstack-usage stop || true
 +if [ "$1" == "0" ] ; then
 +    /sbin/chkconfig --del cloudstack-usage > /dev/null 2>&1 || true
 +fi
 +
 +%post usage
 +if [ -f "%{_sysconfdir}/%{name}/management/db.properties" ]; then
 +    echo "Replacing usage server's db.properties with a link to the 
management server's db.properties"
 +    rm -f %{_sysconfdir}/%{name}/usage/db.properties
 +    ln -s %{_sysconfdir}/%{name}/management/db.properties 
%{_sysconfdir}/%{name}/usage/db.properties
 +    /usr/bin/systemctl enable cloudstack-usage > /dev/null 2>&1 || true
 +fi
 +
 +if [ -f "%{_sysconfdir}/%{name}/management/key" ]; then
 +    echo "Replacing usage server's key with a link to the management server's 
key"
 +    rm -f %{_sysconfdir}/%{name}/usage/key
 +    ln -s %{_sysconfdir}/%{name}/management/key 
%{_sysconfdir}/%{name}/usage/key
 +fi
 +
 +if [ ! -f "%{_sysconfdir}/%{name}/usage/key" ]; then
 +    ln -s %{_sysconfdir}/%{name}/management/key 
%{_sysconfdir}/%{name}/usage/key
 +fi
 +
 +mkdir -p /usr/local/libexec
 +if [ ! -f "/usr/local/libexec/sanity-check-last-id" ]; then
 +    echo 1 > /usr/local/libexec/sanity-check-last-id
 +fi
 +chown cloud:cloud /usr/local/libexec/sanity-check-last-id
 +
 +%posttrans usage
 +# Print help message
 +if [ -f "/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text" 
];then
 +    sed -i "s,^ACS_VERSION=.*,ACS_VERSION=%{_maventag},g" 
/usr/share/cloudstack-common/scripts/installer/cloudstack-help-text
 +    /usr/share/cloudstack-common/scripts/installer/cloudstack-help-text usage
 +fi
 +
 +%post marvin
 +pip3 install --upgrade 
https://files.pythonhosted.org/packages/08/1f/42d74bae9dd6dcfec67c9ed0f3fa482b1ae5ac5f117ca82ab589ecb3ca19/mysql_connector_python-8.0.31-py2.py3-none-any.whl
 +pip3 install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz
 +
 +#No default permission as the permission setup is complex
 +%files management
 +%defattr(-,root,root,-)
 +%dir %{_datadir}/%{name}-management
 +%dir %attr(0770,root,cloud) %{_localstatedir}/%{name}/mnt
 +%dir %attr(0770,cloud,cloud) %{_localstatedir}/%{name}/management
 +%dir %attr(0770,root,cloud) %{_localstatedir}/cache/%{name}/management
 +%dir %attr(0770,root,cloud) %{_localstatedir}/log/%{name}/management
 +%config(noreplace) %{_sysconfdir}/default/%{name}-management
 +%config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management
 +%config(noreplace) %{_sysconfdir}/security/limits.d/cloud
 +%config(noreplace) %{_sysconfdir}/systemd/system/%{name}-management.service.d
 +%config(noreplace) %attr(0640,root,cloud) 
%{_sysconfdir}/%{name}/management/db.properties
 +%config(noreplace) %attr(0640,root,cloud) 
%{_sysconfdir}/%{name}/management/server.properties
 +%config(noreplace) %attr(0640,root,cloud) 
%{_sysconfdir}/%{name}/management/config.json
 +%config(noreplace) %{_sysconfdir}/%{name}/management/log4j-cloud.xml
 +%config(noreplace) %{_sysconfdir}/%{name}/management/log4j2.xml
 +%config(noreplace) %{_sysconfdir}/%{name}/management/environment.properties
 +%config(noreplace) %{_sysconfdir}/%{name}/management/java.security.ciphers
 +%config(noreplace) %attr(0644,root,root) 
%{_sysconfdir}/logrotate.d/%{name}-management
 +%attr(0644,root,root) %{_unitdir}/%{name}-management.service
 +%attr(0755,cloud,cloud) %{_localstatedir}/run/%{name}-management.pid
 +%attr(0755,root,root) %{_bindir}/%{name}-setup-management
 +%attr(0755,root,root) %{_bindir}/%{name}-update-xenserver-licenses
 +%{_datadir}/%{name}-management/conf
 +%{_datadir}/%{name}-management/lib/*.jar
 +%{_datadir}/%{name}-management/logs
 +%{_datadir}/%{name}-management/templates
 +%attr(0755,root,root) %{_bindir}/%{name}-setup-databases
 +%attr(0755,root,root) %{_bindir}/%{name}-migrate-databases
 +%attr(0755,root,root) %{_bindir}/%{name}-set-guest-password
 +%attr(0755,root,root) %{_bindir}/%{name}-set-guest-sshkey
 +%attr(0755,root,root) %{_bindir}/%{name}-sysvmadm
 +%attr(0755,root,root) %{_bindir}/%{name}-setup-encryption
 +%attr(0755,root,root) %{_bindir}/cmk
 +%{_datadir}/%{name}-management/setup/*.sql
 +%{_datadir}/%{name}-management/setup/*.sh
 +%{_datadir}/%{name}-management/setup/server-setup.xml
 +%{_datadir}/%{name}-management/webapp/*
 +%attr(0755,root,root) %{_bindir}/%{name}-external-ipallocator.py
 +%attr(0755,root,root) %{_initrddir}/%{name}-ipallocator
 +%dir %attr(0770,root,root) %{_localstatedir}/log/%{name}/ipallocator
 +%{_defaultdocdir}/%{name}-management-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-management-%{version}/NOTICE
 +%{_datadir}/%{name}-management/setup/wheel/*.whl
 +
 +%files agent
 +%attr(0755,root,root) %{_bindir}/%{name}-setup-agent
 +%attr(0755,root,root) %{_bindir}/%{name}-agent-upgrade
 +%attr(0755,root,root) %{_bindir}/%{name}-guest-tool
 +%attr(0755,root,root) %{_bindir}/%{name}-ssh
 +%attr(0644,root,root) %{_unitdir}/%{name}-agent.service
 +%attr(0644,root,root) %{_unitdir}/%{name}[email protected]
 +%config(noreplace) %{_sysconfdir}/default/%{name}-agent
 +%attr(0644,root,root) %{_sysconfdir}/profile.d/%{name}-agent-profile.sh
 +%config(noreplace) %attr(0644,root,root) 
%{_sysconfdir}/logrotate.d/%{name}-agent
 +%attr(0755,root,root) %{_datadir}/%{name}-common/scripts/network/cisco
 +%config(noreplace) %{_sysconfdir}/%{name}/agent
 +%dir %{_localstatedir}/log/%{name}/agent
 +%attr(0644,root,root) %{_datadir}/%{name}-agent/lib/*.jar
 +%attr(0755,root,root) %{_datadir}/%{name}-agent/lib/libvirtqemuhook
 +%attr(0755,root,root) %{_datadir}/%{name}-agent/lib/rolling-maintenance
 +%dir %{_datadir}/%{name}-agent/plugins
 +%{_defaultdocdir}/%{name}-agent-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-agent-%{version}/NOTICE
 +
 +%files common
 +%dir %attr(0755,root,root) %{_datadir}/%{name}-common/python-site/cloudutils
 +%dir %attr(0755,root,root) %{_datadir}/%{name}-common/vms
 +%attr(0755,root,root) %{_datadir}/%{name}-common/scripts
 +%attr(0755,root,root) /usr/bin/cloudstack-sccs
 +%attr(0644, root, root) %{_datadir}/%{name}-common/vms/agent.zip
 +%attr(0644, root, root) %{_datadir}/%{name}-common/vms/cloud-scripts.tgz
 +%attr(0644, root, root) %{_datadir}/%{name}-common/vms/patch-sysvms.sh
 +%attr(0644,root,root) %{_datadir}/%{name}-common/python-site/cloud_utils.py
 +%attr(0644,root,root) %{_datadir}/%{name}-common/python-site/__pycache__/*
 +%attr(0644,root,root) %{_datadir}/%{name}-common/python-site/cloudutils/*
 +%attr(0644, root, root) %{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
 +%attr(0644, root, root) %{_datadir}/%{name}-common/lib/%{name}-utils.jar
 +%{_defaultdocdir}/%{name}-common-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-common-%{version}/NOTICE
 +
 +%files ui
 +%config(noreplace) %attr(0640,root,cloud) 
%{_sysconfdir}/%{name}/ui/config.json
 +%{_datadir}/%{name}-ui/*
 +%{_defaultdocdir}/%{name}-ui-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-ui-%{version}/NOTICE
 +
 +%files usage
 +%attr(0644,root,root) %{_unitdir}/%{name}-usage.service
 +%config(noreplace) %{_sysconfdir}/default/%{name}-usage
 +%config(noreplace) %attr(0644,root,root) 
%{_sysconfdir}/logrotate.d/%{name}-usage
 +%attr(0644,root,root) %{_datadir}/%{name}-usage/*.jar
 +%attr(0644,root,root) %{_datadir}/%{name}-usage/lib/*.jar
 +%dir %attr(0770,root,cloud) %{_localstatedir}/log/%{name}/usage
 +%attr(0644,root,root) %{_sysconfdir}/%{name}/usage/db.properties
 +%attr(0644,root,root) %{_sysconfdir}/%{name}/usage/log4j-cloud.xml
 +%{_defaultdocdir}/%{name}-usage-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-usage-%{version}/NOTICE
 +
 +%files marvin
 +%attr(0644,root,root) %{_datadir}/%{name}-marvin/Marvin*.tar.gz
 +%{_defaultdocdir}/%{name}-marvin-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-marvin-%{version}/NOTICE
 +
 +%files integration-tests
 +%attr(0755,root,root) %{_datadir}/%{name}-integration-tests/*
 +%{_defaultdocdir}/%{name}-integration-tests-%{version}/LICENSE
 +%{_defaultdocdir}/%{name}-integration-tests-%{version}/NOTICE
 +
 +%if "%{_ossnoss}" == "noredist"
 +%files mysql-ha
 +%defattr(0644,cloud,cloud,0755)
 +%attr(0644,root,root) %{_datadir}/%{name}-mysql-ha/lib/*
 +%endif
 +
 +%files baremetal-agent
 +%attr(0755,root,root) %{_bindir}/cloudstack-setup-baremetal
 +
 +%changelog
 +* Thu Dec 22 2022 Rohit Yadav <[email protected]> 4.18.0
 +- Add support for EL9
 +
 +* Fri Oct 14 2022 Daan Hoogland <[email protected]> 4.18.0
 +- initialising sanity check pointer file
 +
 +* Tue Jun 29 2021 David Jumani <[email protected]> 4.16.0
 +- Adding SUSE 15 support
 +
 +* Thu Apr 30 2015 Rohit Yadav <[email protected]> 4.6.0
 +- Remove awsapi package
 +
 +* Wed Nov 19 2014 Hugo Trippaers <[email protected]> 4.6.0
 +- Create a specific spec for CentOS 7
 +
 +* Fri Jul 4 2014 Hugo Trippaers <[email protected]> 4.5.0
 +- Add a package for the mysql ha module
 +
 +* Fri Oct 5 2012 Hugo Trippaers <[email protected]> 4.1.0
 +- new style spec file
diff --cc 
plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
index de85e6231f2,d666293d02a..4d50ef7e1f8
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java
@@@ -203,10 -203,10 +203,10 @@@ public class KubernetesClusterScaleWork
              retryCounter++;
              try {
                  Pair<Boolean, String> result = 
SshHelper.sshExecute(ipAddress, port, getControlNodeLoginUser(),
-                         pkFile, null, String.format("sudo /opt/bin/kubectl 
drain %s --ignore-daemonsets --delete-local-data", hostName),
+                         pkFile, null, String.format("sudo /opt/bin/kubectl 
drain %s --ignore-daemonsets --delete-emptydir-data", hostName),
                          10000, 10000, 60000);
                  if (!result.first()) {
 -                    LOGGER.warn(String.format("Draining node: %s on VM : %s 
in Kubernetes cluster : %s unsuccessful", hostName, userVm.getDisplayName(), 
kubernetesCluster.getName()));
 +                    logger.warn("Draining node: {} on VM: {} in Kubernetes 
cluster: {} unsuccessful", hostName, userVm, kubernetesCluster);
                  } else {
                      result = SshHelper.sshExecute(ipAddress, port, 
getControlNodeLoginUser(),
                              pkFile, null, String.format("sudo 
/opt/bin/kubectl delete node %s", hostName),
diff --cc 
plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
index 7d27e6b77ce,a63c5b68e57..12741e0996a
--- 
a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
+++ 
b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java
@@@ -520,13 -517,8 +520,17 @@@ public class MockAccountManager extend
          return null;
      }
  
 +    public void validateUserPasswordAndUpdateIfNeeded(String newPassword, 
UserVO user,
 +                                               String currentPassword,
 +                                               boolean 
skipCurrentPassValidation) {
 +    }
 +
 +    @Override
 +    public void checkApiAccess(Account account, String command) throws 
PermissionDeniedException {
++    }
 +
+     @Override
+     public UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
+         return null;
      }
  }
diff --cc server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index 4ca5bf2eb92,3a6d804a762..8e99e3429ba
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@@ -62,8 -62,8 +62,8 @@@ import org.apache.cloudstack.api.comman
  import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd;
  import 
org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd;
  import org.apache.cloudstack.api.command.admin.storage.SyncStoragePoolCmd;
--import 
org.apache.cloudstack.api.command.admin.storage.UpdateObjectStoragePoolCmd;
  import org.apache.cloudstack.api.command.admin.storage.UpdateImageStoreCmd;
++import 
org.apache.cloudstack.api.command.admin.storage.UpdateObjectStoragePoolCmd;
  import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
  import 
org.apache.cloudstack.api.command.admin.storage.heuristics.CreateSecondaryStorageSelectorCmd;
  import 
org.apache.cloudstack.api.command.admin.storage.heuristics.RemoveSecondaryStorageSelectorCmd;
@@@ -234,8 -235,8 +234,8 @@@ import com.cloud.user.dao.UserDao
  import com.cloud.utils.DateUtil;
  import com.cloud.utils.NumbersUtil;
  import com.cloud.utils.Pair;
--import com.cloud.utils.UriUtils;
  import com.cloud.utils.StringUtils;
++import com.cloud.utils.UriUtils;
  import com.cloud.utils.component.ComponentContext;
  import com.cloud.utils.component.ManagerBase;
  import com.cloud.utils.concurrency.NamedThreadFactory;
diff --cc server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 2b6c7a06709,68546f185b7..a371a064701
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@@ -346,7 -352,7 +351,9 @@@ public class VolumeApiServiceImpl exten
      @Inject
      protected StoragePoolDetailsDao storagePoolDetailsDao;
      @Inject
 +    private BackupDao backupDao;
++    @Inject
+     HostPodDao podDao;
  
  
      protected Gson _gson;
@@@ -2433,20 -2384,13 +2440,13 @@@
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription 
= "attaching volume", async = true)
      public Volume attachVolumeToVM(AttachVolumeCmd command) {
 -        return attachVolumeToVM(command.getVirtualMachineId(), 
command.getId(), command.getDeviceId());
 +        return attachVolumeToVM(command.getVirtualMachineId(), 
command.getId(), command.getDeviceId(), false);
      }
  
-     private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long 
deviceId) {
-         VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
- 
-         if (volumeToAttach.isAttachedVM()) {
-             throw new CloudRuntimeException("This volume is already attached 
to a VM.");
-         }
- 
-         UserVmVO vm = _userVmDao.findById(vmId);
+     protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, 
VolumeInfo volumeToAttach) {
          VolumeVO existingVolumeOfVm = null;
          VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
-         List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, 
Volume.Type.ROOT);
+         List<VolumeVO> rootVolumesOfVm = 
_volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
          if (rootVolumesOfVm.size() > 1 && template != null && 
!template.isDeployAsIs()) {
              throw new CloudRuntimeException("The VM " + vm.getHostName() + " 
has more than one ROOT volume and is in an invalid state.");
          } else {
@@@ -2463,41 -2407,89 +2463,89 @@@
                  }
              }
          }
-         if (logger.isTraceEnabled()) {
-             if (existingVolumeOfVm != null) {
-                 logger.trace("attaching volume {} to a VM {} with an existing 
volume {} on primary storage {}",
-                         volumeToAttach, vm, existingVolumeOfVm, 
_storagePoolDao.findById(existingVolumeOfVm.getPoolId()));
+         if (existingVolumeOfVm == null) {
 -            if (s_logger.isTraceEnabled()) {
 -                s_logger.trace(String.format("No existing volume found for VM 
(%s/%s) to attach volume %s/%s",
++            if (logger.isTraceEnabled()) {
++                logger.trace(String.format("No existing volume found for VM 
(%s/%s) to attach volume %s/%s",
+                         vm.getName(), vm.getUuid(),
+                         volumeToAttach.getName(), volumeToAttach.getUuid()));
              }
+             return null;
          }
- 
-         HypervisorType rootDiskHyperType = vm.getHypervisorType();
-         HypervisorType volumeToAttachHyperType = 
_volsDao.getHypervisorType(volumeToAttach.getId());
- 
 -        if (s_logger.isTraceEnabled()) {
++        if (logger.isTraceEnabled()) {
+             String msg = "attaching volume %s/%s to a VM (%s/%s) with an 
existing volume %s/%s on primary storage %s";
 -            s_logger.trace(String.format(msg,
++            logger.trace(String.format(msg,
+                     volumeToAttach.getName(), volumeToAttach.getUuid(),
+                     vm.getName(), vm.getUuid(),
+                     existingVolumeOfVm.getName(), 
existingVolumeOfVm.getUuid(),
+                     existingVolumeOfVm.getPoolId()));
+         }
+         return existingVolumeOfVm;
+     }
+ 
+     protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final 
VolumeInfo volumeToAttach, final UserVmVO vm) {
+         DataCenter zone = _dcDao.findById(vm.getDataCenterId());
+         Pair<Long, Long> clusterHostId = 
virtualMachineManager.findClusterAndHostIdForVm(vm, false);
+         long podId = vm.getPodIdToDeployIn();
+         if (clusterHostId.first() != null) {
+             Cluster cluster = clusterDao.findById(clusterHostId.first());
+             podId = cluster.getPodId();
+         }
+         Pod pod = podDao.findById(podId);
+         DiskOfferingVO offering = 
_diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
+         DiskProfile diskProfile =  new DiskProfile(volumeToAttach.getId(), 
volumeToAttach.getVolumeType(),
+                 volumeToAttach.getName(), volumeToAttach.getId(), 
volumeToAttach.getSize(), offering.getTagsArray(),
+                 offering.isUseLocalStorage(), offering.isRecreatable(),
+                 volumeToAttach.getTemplateId());
+         diskProfile.setHyperType(vm.getHypervisorType());
+         StoragePool pool = _volumeMgr.findStoragePool(diskProfile, zone, pod, 
clusterHostId.first(),
+                 clusterHostId.second(), vm, Collections.emptySet());
+         if (pool == null) {
+             throw new CloudRuntimeException(String.format("Failed to find a 
primary storage for volume in state: %s", volumeToAttach.getState()));
+         }
+         return pool;
+     }
+ 
+     protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final 
VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
          VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
- 
+         boolean volumeOnSecondary = volumeToAttach.getState() == 
Volume.State.Uploaded;
+         if (!Arrays.asList(Volume.State.Allocated, 
Volume.State.Uploaded).contains(volumeToAttach.getState())) {
+             return newVolumeOnPrimaryStorage;
+         }
          //don't create volume on primary storage if its being attached to the 
vm which Root's volume hasn't been created yet
-         StoragePoolVO destPrimaryStorage = null;
+         StoragePool destPrimaryStorage = null;
          if (existingVolumeOfVm != null && 
!existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
              destPrimaryStorage = 
_storagePoolDao.findById(existingVolumeOfVm.getPoolId());
 -            if (s_logger.isTraceEnabled() && destPrimaryStorage != null) {
 -                s_logger.trace(String.format("decided on target storage: 
%s/%s", destPrimaryStorage.getName(), destPrimaryStorage.getUuid()));
 +            if (logger.isTraceEnabled() && destPrimaryStorage != null) {
 +                logger.trace("decided on target storage: {}", 
destPrimaryStorage);
              }
          }
+         if (destPrimaryStorage == null) {
+             destPrimaryStorage = 
getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
+         }
+         try {
+             if (volumeOnSecondary && 
Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) {
+                 throw new InvalidParameterValueException("Cannot attach 
uploaded volume, this operation is unsupported on storage pool type " + 
destPrimaryStorage.getPoolType());
+             }
+             newVolumeOnPrimaryStorage = 
_volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
+                     vm.getHypervisorType(), destPrimaryStorage);
+         } catch (NoTransitionException e) {
 -            s_logger.debug("Failed to create volume on primary storage", e);
++            logger.debug("Failed to create volume on primary storage", e);
+             throw new CloudRuntimeException("Failed to create volume on 
primary storage", e);
+         }
+         return newVolumeOnPrimaryStorage;
+     }
  
-         boolean volumeOnSecondary = volumeToAttach.getState() == 
Volume.State.Uploaded;
+     private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long 
deviceId) {
+         VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
  
-         if (destPrimaryStorage != null && (volumeToAttach.getState() == 
Volume.State.Allocated || volumeOnSecondary)) {
-             try {
-                 if (volumeOnSecondary && destPrimaryStorage.getPoolType() == 
Storage.StoragePoolType.PowerFlex) {
-                     throw new InvalidParameterValueException("Cannot attach 
uploaded volume, this operation is unsupported on storage pool type " + 
destPrimaryStorage.getPoolType());
-                 }
-                 newVolumeOnPrimaryStorage = 
_volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, 
destPrimaryStorage);
-             } catch (NoTransitionException e) {
-                 logger.debug("Failed to create volume on primary storage", e);
-                 throw new CloudRuntimeException("Failed to create volume on 
primary storage", e);
-             }
+         if (volumeToAttach.isAttachedVM()) {
+             throw new CloudRuntimeException("This volume is already attached 
to a VM.");
          }
  
+         UserVmVO vm = _userVmDao.findById(vmId);
+         VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, 
volumeToAttach);
+         VolumeInfo newVolumeOnPrimaryStorage = 
createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);
+ 
          // reload the volume from db
          newVolumeOnPrimaryStorage = 
volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
          boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, 
newVolumeOnPrimaryStorage);
@@@ -2515,13 -2507,11 +2563,11 @@@
              StoragePoolVO vmRootVolumePool = 
_storagePoolDao.findById(existingVolumeOfVm.getPoolId());
  
              try {
+                 HypervisorType volumeToAttachHyperType = 
_volsDao.getHypervisorType(volumeToAttach.getId());
                  newVolumeOnPrimaryStorage = 
_volumeMgr.moveVolume(newVolumeOnPrimaryStorage, 
vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), 
vmRootVolumePool.getClusterId(),
                          volumeToAttachHyperType);
-             } catch (ConcurrentOperationException e) {
-                 logger.debug("move volume failed", e);
-                 throw new CloudRuntimeException("move volume failed", e);
-             } catch (StorageUnavailableException e) {
+             } catch (ConcurrentOperationException | 
StorageUnavailableException e) {
 -                s_logger.debug("move volume failed", e);
 +                logger.debug("move volume failed", e);
                  throw new CloudRuntimeException("move volume failed", e);
              }
          }
diff --cc server/src/main/java/com/cloud/user/AccountManager.java
index 1e5526688b7,1d7611d5b54..640eaa00076
--- a/server/src/main/java/com/cloud/user/AccountManager.java
+++ b/server/src/main/java/com/cloud/user/AccountManager.java
@@@ -20,7 -20,7 +20,6 @@@ import java.net.InetAddress
  import java.util.List;
  import java.util.Map;
  
--import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
  import org.apache.cloudstack.acl.ControlledEntity;
  import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
  import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
@@@ -31,6 -31,6 +30,7 @@@ import org.apache.cloudstack.auth.UserT
  import org.apache.cloudstack.framework.config.ConfigKey;
  import org.apache.cloudstack.framework.config.Configurable;
  
++import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
  import com.cloud.api.query.vo.ControlledViewEntity;
  import com.cloud.exception.ConcurrentOperationException;
  import com.cloud.exception.ResourceUnavailableException;
@@@ -198,9 -198,6 +198,12 @@@ public interface AccountManager extend
  
      UserTwoFactorAuthenticationSetupResponse 
setupUserTwoFactorAuthentication(SetupUserTwoFactorAuthenticationCmd cmd);
  
 +    List<String> getApiNameList();
 +
 +    void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO 
user, String currentPassword, boolean skipCurrentPassValidation);
 +
-   void checkApiAccess(Account caller, String command);
++    void checkApiAccess(Account caller, String command);
++
+     UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
+ 
  }
diff --cc server/src/main/java/com/cloud/user/AccountManagerImpl.java
index ea2df5d1530,2358830e9a3..1422f39f1fe
--- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
@@@ -120,6 -117,6 +119,7 @@@ import com.cloud.exception.InvalidParam
  import com.cloud.exception.OperationTimedoutException;
  import com.cloud.exception.PermissionDeniedException;
  import com.cloud.exception.ResourceUnavailableException;
++import com.cloud.host.dao.HostDao;
  import com.cloud.network.IpAddress;
  import com.cloud.network.IpAddressManager;
  import com.cloud.network.Network;
@@@ -1377,8 -1340,8 +1377,8 @@@ public class AccountManagerImpl extend
       * if there is any permission under the requested role that is not 
permitted for the caller, refuse
       */
      private void checkRoleEscalation(Account caller, Account requested) {
 -        if (s_logger.isDebugEnabled()) {
 -            s_logger.debug(String.format("Checking if user of account %s [%s] 
with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
 +        if (logger.isDebugEnabled()) {
-             logger.debug(String.format("checking if user of account %s [%s] 
with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
++            logger.debug(String.format("Checking if user of account %s [%s] 
with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
                      caller.getAccountName(),
                      caller.getUuid(),
                      caller.getRoleId(),
@@@ -1391,13 -1354,14 +1391,14 @@@
              try {
                  checkApiAccess(apiCheckers, requested, command);
              } catch (PermissionDeniedException pde) {
 -                if (s_logger.isTraceEnabled()) {
 -                    s_logger.trace(String.format(
 +                if (logger.isTraceEnabled()) {
-                     logger.trace(String.format("checking for permission to 
\"%s\" is irrelevant as it is not requested for %s [%s]",
++                    logger.trace(String.format(
+                             "Checking for permission to \"%s\" is irrelevant 
as it is not requested for %s [%s]",
                              command,
-                             pde.getAccount().getAccountName(),
-                             pde.getAccount().getUuid(),
-                             pde.getEntitiesInViolation()
-                             ));
+                             requested.getAccountName(),
+                             requested.getUuid()
+                         )
+                     );
                  }
                  continue;
              }
@@@ -3566,4 -3476,26 +3567,26 @@@
          return 
userTwoFactorAuthenticationProvidersMap.get(name.toLowerCase());
      }
  
+     @Override
+     public UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
+         return Transaction.execute((TransactionCallback<UserAccount>) status 
-> {
+             if (!user.isUser2faEnabled() && 
StringUtils.isBlank(user.getUser2faProvider())) {
+                 return user;
+             }
+             UserDetailVO userDetailVO = 
_userDetailsDao.findDetail(user.getId(), UserDetailVO.Setup2FADetail);
+             if (userDetailVO != null && 
UserAccountVO.Setup2FAstatus.VERIFIED.name().equals(userDetailVO.getValue())) {
+                 return user;
+             }
 -            s_logger.info(String.format("Clearing 2FA configurations for %s 
as it is still in setup on a new login request", user));
++            logger.info("Clearing 2FA configurations for {} as it is still in 
setup on a new login request", user);
+             if (userDetailVO != null) {
+                 _userDetailsDao.remove(userDetailVO.getId());
+             }
+             UserAccountVO userAccountVO = 
_userAccountDao.findById(user.getId());
+             userAccountVO.setUser2faEnabled(false);
+             userAccountVO.setUser2faProvider(null);
+             userAccountVO.setKeyFor2fa(null);
+             _userAccountDao.update(user.getId(), userAccountVO);
+             return userAccountVO;
+         });
+     }
  }
diff --cc server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
index 9630b341bc9,3e6f9ec63f9..23b79e4ab03
--- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
+++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
@@@ -91,11 -85,14 +90,15 @@@ import org.mockito.junit.MockitoJUnitRu
  import org.springframework.test.util.ReflectionTestUtils;
  
  import com.cloud.api.query.dao.ServiceOfferingJoinDao;
 +import com.cloud.configuration.ConfigurationManager;
  import com.cloud.configuration.Resource;
  import com.cloud.configuration.Resource.ResourceType;
+ import com.cloud.dc.ClusterVO;
  import com.cloud.dc.DataCenterVO;
+ import com.cloud.dc.HostPodVO;
+ import com.cloud.dc.dao.ClusterDao;
  import com.cloud.dc.dao.DataCenterDao;
+ import com.cloud.dc.dao.HostPodDao;
  import com.cloud.event.EventTypes;
  import com.cloud.event.UsageEventUtils;
  import com.cloud.exception.InvalidParameterValueException;
@@@ -108,6 -105,6 +111,7 @@@ import com.cloud.org.Grouping
  import com.cloud.projects.Project;
  import com.cloud.projects.ProjectManager;
  import com.cloud.serializer.GsonHelper;
++import com.cloud.server.ManagementService;
  import com.cloud.server.TaggedResourceService;
  import com.cloud.service.ServiceOfferingVO;
  import com.cloud.service.dao.ServiceOfferingDao;
@@@ -247,15 -251,6 +262,12 @@@ public class VolumeApiServiceImplTest 
      @Mock
      private StorageManager storageMgr;
  
 +    @Mock
 +    private ConfigurationManager _configMgr;
 +
-     @Mock
-     private VolumeOrchestrationService _volumeMgr;
- 
 +    @Mock
 +    private ManagementService managementService;
 +
      private long accountMockId = 456l;
      private long volumeMockId = 12313l;
      private long vmInstanceMockId = 1123l;
@@@ -1838,91 -1837,236 +1850,323 @@@
          volumeApiServiceImpl.validationsForCheckVolumeOperation(volume);
      }
  
 +    private void testResizeVolumeSetup() throws ExecutionException, 
InterruptedException {
 +        Long poolId = 11L;
 +
 +        when(volumeDaoMock.findById(volumeMockId)).thenReturn(volumeVoMock);
 +        when(volumeVoMock.getId()).thenReturn(volumeMockId);
 +        
when(volumeDaoMock.getHypervisorType(volumeMockId)).thenReturn(HypervisorType.KVM);
 +        when(volumeVoMock.getState()).thenReturn(Volume.State.Ready);
 +        when(volumeVoMock.getDiskOfferingId()).thenReturn(diskOfferingMockId);
 +        
when(_diskOfferingDao.findById(diskOfferingMockId)).thenReturn(diskOfferingMock);
 +        
when(_diskOfferingDao.findById(newDiskOfferingMockId)).thenReturn(newDiskOfferingMock);
 +        when(newDiskOfferingMock.getRemoved()).thenReturn(null);
 +        when(diskOfferingMock.getDiskSizeStrictness()).thenReturn(false);
 +        when(newDiskOfferingMock.getDiskSizeStrictness()).thenReturn(false);
 +        when(volumeVoMock.getInstanceId()).thenReturn(null);
 +        when(volumeVoMock.getVolumeType()).thenReturn(Type.DATADISK);
 +        when(newDiskOfferingMock.getDiskSize()).thenReturn(newVolumeSizeMock);
 +
 +        VolumeInfo volInfo = Mockito.mock(VolumeInfo.class);
 +        
when(volumeDataFactoryMock.getVolume(volumeMockId)).thenReturn(volInfo);
 +        DataStore dataStore = Mockito.mock(DataStore.class);
 +        when((volInfo.getDataStore())).thenReturn(dataStore);
 +
 +        when(volumeVoMock.getPoolId()).thenReturn(poolId);
 +        StoragePoolVO storagePool = Mockito.mock(StoragePoolVO.class);
 +        
when(primaryDataStoreDaoMock.findById(poolId)).thenReturn(storagePool);
 +
 +        
Mockito.lenient().doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).resize(any(VolumeInfo.class));
 +        
Mockito.doReturn(Mockito.mock(VolumeApiResult.class)).when(asyncCallFutureVolumeapiResultMock).get();
 +    }
 +
 +    @Test
 +    public void testResizeVolumeWithEnoughCapacity() throws 
ResourceAllocationException, ExecutionException, InterruptedException {
 +        ResizeVolumeCmd cmd = new ResizeVolumeCmd();
 +        ReflectionTestUtils.setField(cmd, "id", volumeMockId);
 +        ReflectionTestUtils.setField(cmd, "newDiskOfferingId", 
newDiskOfferingMockId);
 +
 +        testResizeVolumeSetup();
 +
 +        when(storageMgr.storagePoolHasEnoughSpaceForResize(any(), 
nullable(Long.class), nullable(Long.class))).thenReturn(true);
 +
 +        try (MockedStatic<UsageEventUtils> ignored = 
Mockito.mockStatic(UsageEventUtils.class)) {
 +            volumeApiServiceImpl.resizeVolume(cmd);
 +
 +            verify(volumeServiceMock).resize(any(VolumeInfo.class));
 +        }
 +    }
 +
 +    @Test
 +    public void testResizeVolumeWithoutEnoughCapacity() throws 
ResourceAllocationException, ExecutionException, InterruptedException {
 +        ResizeVolumeCmd cmd = new ResizeVolumeCmd();
 +        ReflectionTestUtils.setField(cmd, "id", volumeMockId);
 +        ReflectionTestUtils.setField(cmd, "newDiskOfferingId", 
newDiskOfferingMockId);
 +        ReflectionTestUtils.setField(cmd, "autoMigrate", true);
 +
 +        testResizeVolumeSetup();
 +
 +        when(storageMgr.storagePoolHasEnoughSpaceForResize(any(), 
nullable(Long.class), nullable(Long.class))).thenReturn(false).thenReturn(true);
 +        StoragePoolVO suitableStoragePool = Mockito.mock(StoragePoolVO.class);
 +        Pair<List<? extends StoragePool>, List<? extends StoragePool>> 
poolsPair = new Pair<>(Arrays.asList(suitableStoragePool), 
Arrays.asList(suitableStoragePool));
 +        
when(managementService.listStoragePoolsForSystemMigrationOfVolume(anyLong(), 
anyLong(), anyLong(), anyLong(), anyLong(), anyBoolean(), 
anyBoolean())).thenReturn(poolsPair);
 +        
doReturn(volumeInfoMock).when(volumeApiServiceImpl).migrateVolume(any());
 +        when(volumeInfoMock.getId()).thenReturn(volumeMockId);
 +
 +        try (MockedStatic<UsageEventUtils> ignored = 
Mockito.mockStatic(UsageEventUtils.class)) {
 +            volumeApiServiceImpl.resizeVolume(cmd);
 +
 +            verify(volumeApiServiceImpl).migrateVolume(any());
 +            verify(volumeServiceMock).resize(any(VolumeInfo.class));
 +        }
 +    }
 +
 +    @Test
 +    public void testResizeVolumeInAllocateState() throws 
ResourceAllocationException, ExecutionException, InterruptedException {
 +        ResizeVolumeCmd cmd = new ResizeVolumeCmd();
 +        ReflectionTestUtils.setField(cmd, "id", volumeMockId);
 +        ReflectionTestUtils.setField(cmd, "newDiskOfferingId", 
newDiskOfferingMockId);
 +
 +        testResizeVolumeSetup();
 +
 +        when(volumeVoMock.getState()).thenReturn(Volume.State.Allocated);
 +
 +        try (MockedStatic<UsageEventUtils> ignored = 
Mockito.mockStatic(UsageEventUtils.class)) {
 +            volumeApiServiceImpl.resizeVolume(cmd);
 +
 +            verify(volumeServiceMock, times(0)).resize(any(VolumeInfo.class));
 +        }
 +    }
+     private UserVmVO getMockedVm() {
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         Mockito.when(vm.getId()).thenReturn(1L);
+         Mockito.when(vm.getTemplateId()).thenReturn(10L);
+         Mockito.when(vm.getHostName()).thenReturn("test-vm");
+         return vm;
+     }
+ 
+     private VMTemplateVO getMockedTemplate() {
+         VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
+         Mockito.when(template.isDeployAsIs()).thenReturn(false);
+         return template;
+     }
+ 
+     @Test(expected = CloudRuntimeException.class)
+     public void 
testGetVmExistingVolumeForVolumeAttach_MultipleRootVolumes_ThrowsException() {
+         UserVmVO vm = getMockedVm();
+         VMTemplateVO template = getMockedTemplate();
+         when(templateDao.findById(10L)).thenReturn(template);
+         when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT))
+                 .thenReturn(Arrays.asList(Mockito.mock(VolumeVO.class), 
Mockito.mock(VolumeVO.class)));
+         volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, 
Mockito.mock(VolumeInfo.class));
+     }
+ 
+     @Test
+     public void testGetVmExistingVolumeForVolumeAttach_SingleRootVolume() {
+         UserVmVO vm = getMockedVm();
+         VMTemplateVO template = getMockedTemplate();
+         VolumeVO rootVolume = Mockito.mock(VolumeVO.class);
+         Mockito.when(rootVolume.getId()).thenReturn(20L);
+         Mockito.when(templateDao.findById(10L)).thenReturn(template);
+         Mockito.when(volumeDaoMock.findByInstanceAndType(1L, 
Volume.Type.ROOT))
+                 .thenReturn(Collections.singletonList(rootVolume));
+         VolumeVO result = 
volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, 
Mockito.mock(VolumeInfo.class));
+         Assert.assertNotNull(result);
+         Assert.assertEquals(20L, result.getId());
+     }
+ 
+     private VolumeVO getMockedDataVolume() {
+         VolumeVO volume = Mockito.mock(VolumeVO.class);
+         Mockito.when(volume.getId()).thenReturn(30L);
+         Mockito.when(volume.getState()).thenReturn(Volume.State.Ready);
+         return volume;
+     }
+ 
+     @Test
+     public void 
testGetVmExistingVolumeForVolumeAttach_NoRootVolume_DataDiskAvailable() {
+         UserVmVO vm = getMockedVm();
+         VMTemplateVO template = getMockedTemplate();
+         VolumeVO dataDisk = getMockedDataVolume();
+         List<VolumeVO> rootVolumes = Collections.emptyList();
+         List<VolumeVO> dataVolumes = Collections.singletonList(dataDisk);
+         Mockito.when(templateDao.findById(10L)).thenReturn(template);
+         Mockito.when(volumeDaoMock.findByInstanceAndType(1L, 
Volume.Type.ROOT)).thenReturn(rootVolumes);
+         Mockito.when(volumeDaoMock.findByInstanceAndType(1L, 
Volume.Type.DATADISK)).thenReturn(dataVolumes);
+         VolumeVO result = 
volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, 
Mockito.mock(VolumeInfo.class));
+         Assert.assertNotNull(result);
+         Assert.assertEquals(30L, result.getId());
+     }
+ 
+     @Test
+     public void testGetVmExistingVolumeForVolumeAttach_NoVolumesAtAll() {
+         UserVmVO vm = getMockedVm();
+         VMTemplateVO template = getMockedTemplate();
+         Mockito.when(templateDao.findById(10L)).thenReturn(template);
+         Mockito.when(volumeDaoMock.findByInstanceAndType(1L, 
Volume.Type.ROOT)).thenReturn(Collections.emptyList());
+         Mockito.when(volumeDaoMock.findByInstanceAndType(1L, 
Volume.Type.DATADISK)).thenReturn(Collections.emptyList());
+         VolumeVO result = 
volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, 
Mockito.mock(VolumeInfo.class));
+         Assert.assertNull(result);
+     }
+ 
+     private void mockDiskOffering() {
+         DiskOfferingVO offering = Mockito.mock(DiskOfferingVO.class);
+         Mockito.when(_diskOfferingDao.findById(1L)).thenReturn(offering);
+         Mockito.when(offering.isUseLocalStorage()).thenReturn(true);
+         Mockito.when(offering.isRecreatable()).thenReturn(false);
+     }
+ 
+     private DataCenterVO mockZone() {
+         DataCenterVO zone = Mockito.mock(DataCenterVO.class);
+         Mockito.when(_dcDao.findById(1L)).thenReturn(zone);
+         return zone;
+     }
+ 
+     @Test
+     public void testGetPoolForAllocatedOrUploadedVolumeForAttach_Success() {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         ClusterVO cluster = Mockito.mock(ClusterVO.class);
+         HostPodVO pod = Mockito.mock(HostPodVO.class);
+         DataCenterVO zone = mockZone();
+         mockDiskOffering();
+         StoragePool pool = Mockito.mock(StoragePool.class);
+         when(vm.getDataCenterId()).thenReturn(1L);
+         when(virtualMachineManager.findClusterAndHostIdForVm(vm, 
false)).thenReturn(new Pair<>(1L, 2L));
+         when(clusterDao.findById(1L)).thenReturn(cluster);
+         when(cluster.getPodId()).thenReturn(1L);
+         when(podDao.findById(1L)).thenReturn(pod);
+         when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
+         
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), 
eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet())))
+                 .thenReturn(pool);
+         StoragePool result = 
volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach,
 vm);
+         Assert.assertNotNull(result);
+         Assert.assertEquals(pool, result);
+     }
+ 
+     @Test(expected = CloudRuntimeException.class)
+     public void 
testGetPoolForAllocatedOrUploadedVolumeForAttach_NoPoolFound_ThrowsException() {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         DataCenterVO zone = mockZone();
+         Pair<Long, Long> clusterHostId = new Pair<>(1L, 2L);
+         ClusterVO cluster = Mockito.mock(ClusterVO.class);
+         HostPodVO pod = Mockito.mock(HostPodVO.class);
+         mockDiskOffering();
+         when(vm.getDataCenterId()).thenReturn(1L);
+         when(clusterDao.findById(1L)).thenReturn(cluster);
+         when(virtualMachineManager.findClusterAndHostIdForVm(vm, 
false)).thenReturn(clusterHostId);
+         when(podDao.findById(anyLong())).thenReturn(pod);
+         when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
+         
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), 
eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet())))
+                 .thenReturn(null);
+         
volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach,
 vm);
+     }
+ 
+     @Test
+     public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoCluster() {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         DataCenterVO zone = mockZone();
+         HostPodVO pod = Mockito.mock(HostPodVO.class);
+         mockDiskOffering();
+         StoragePool pool = Mockito.mock(StoragePool.class);
+         when(vm.getDataCenterId()).thenReturn(1L);
+         when(vm.getPodIdToDeployIn()).thenReturn(2L);
+         when(virtualMachineManager.findClusterAndHostIdForVm(vm, 
false)).thenReturn(new Pair<>(null, 2L));
+         when(podDao.findById(2L)).thenReturn(pod);
+         when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
+         
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), 
eq(zone), eq(pod), eq(null), eq(2L), eq(vm), eq(Collections.emptySet())))
+                 .thenReturn(pool);
+         StoragePool result = 
volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach,
 vm);
+         Assert.assertNotNull(result);
+         Assert.assertEquals(pool, result);
+     }
+ 
+ 
+     @Test
+     public void 
testCreateVolumeOnSecondaryForAttachIfNeeded_VolumeNotAllocatedOrUploaded() {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Ready);
+         VolumeInfo result = 
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(
+                 volumeToAttach, Mockito.mock(UserVmVO.class), null);
+         Assert.assertSame(volumeToAttach, result);
+         Mockito.verifyNoInteractions(primaryDataStoreDaoMock, 
volumeOrchestrationService);
+     }
+ 
+     @Test
+     public void 
testCreateVolumeOnSecondaryForAttachIfNeeded_ExistingVolumeDeterminesStoragePool()
 {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         VolumeVO existingVolume = Mockito.mock(VolumeVO.class);
+         
Mockito.when(existingVolume.getState()).thenReturn(Volume.State.Ready);
+         when(existingVolume.getPoolId()).thenReturn(1L);
+         StoragePoolVO destPrimaryStorage = Mockito.mock(StoragePoolVO.class);
+         
Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
+         
Mockito.when(primaryDataStoreDaoMock.findById(1L)).thenReturn(destPrimaryStorage);
+         VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class);
+         try {
+             
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, 
volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
+                     .thenReturn(newVolumeOnPrimaryStorage);
+         } catch (NoTransitionException nte) {
+             Assert.fail(nte.getMessage());
+         }
+         VolumeInfo result = 
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, 
existingVolume);
+         Assert.assertSame(newVolumeOnPrimaryStorage, result);
+         Mockito.verify(primaryDataStoreDaoMock).findById(1L);
+     }
+ 
+     @Test
+     public void 
testCreateVolumeOnPrimaryForAttachIfNeeded_UsesGetPoolForAttach() {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Allocated);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
+         Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
+                 .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, 
vm);
+         VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class);
+         try {
+             
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(
+                     vm, volumeToAttach, vm.getHypervisorType(), 
destPrimaryStorage))
+                 .thenReturn(newVolumeOnPrimaryStorage);
+         } catch (NoTransitionException nte) {
+             Assert.fail(nte.getMessage());
+         }
+         VolumeInfo result = 
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, 
null);
+         Assert.assertSame(newVolumeOnPrimaryStorage, result);
+         
verify(volumeApiServiceImpl).getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach,
 vm);
+     }
+ 
+     @Test(expected = InvalidParameterValueException.class)
+     public void 
testCreateVolumeOnPrimaryForAttachIfNeeded_UnsupportedPoolType_ThrowsException()
 {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
+         
when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex);
+         Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
+                 .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, 
vm);
+         
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, 
null);
+     }
+ 
+     @Test
+     public void 
testCreateVolumeOnSecondaryForAttachIfNeeded_CreateVolumeFails_ThrowsException()
 {
+         VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
+         
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
+         UserVmVO vm = Mockito.mock(UserVmVO.class);
+         StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
+         
Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
+         Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
+                 .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, 
vm);
+         try {
+             
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, 
volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
+                     .thenThrow(new NoTransitionException("Mocked exception"));
+         } catch (NoTransitionException nte) {
+             Assert.fail(nte.getMessage());
+         }
+         CloudRuntimeException exception = 
Assert.assertThrows(CloudRuntimeException.class, () ->
+                 
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, 
null)
+         );
+         Assert.assertTrue(exception.getMessage().contains("Failed to create 
volume on primary storage"));
+     }
  }
diff --cc server/src/test/java/com/cloud/user/AccountManagerImplTest.java
index 0f53b907251,e68de194f01..61a273c8946
--- a/server/src/test/java/com/cloud/user/AccountManagerImplTest.java
+++ b/server/src/test/java/com/cloud/user/AccountManagerImplTest.java
@@@ -43,23 -39,21 +41,25 @@@ import org.apache.cloudstack.auth.UserA
  import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.framework.config.ConfigKey;
+ import org.apache.cloudstack.resourcedetail.UserDetailVO;
 +import org.apache.cloudstack.webhook.WebhookHelper;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
+ import org.mockito.ArgumentCaptor;
  import org.mockito.InOrder;
  import org.mockito.Mock;
 +import org.mockito.MockedStatic;
  import org.mockito.Mockito;
  import org.mockito.junit.MockitoJUnitRunner;
- 
 +import org.springframework.beans.factory.NoSuchBeanDefinitionException;
  
  import com.cloud.acl.DomainChecker;
  import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
  import com.cloud.domain.Domain;
  import com.cloud.domain.DomainVO;
++import com.cloud.event.ActionEventUtils;
  import com.cloud.exception.ConcurrentOperationException;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.exception.PermissionDeniedException;
diff --cc server/src/test/java/com/cloud/user/MockAccountManagerImpl.java
index 30324b41986,96b73cc63dd..62a95380a15
--- a/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java
+++ b/server/src/test/java/com/cloud/user/MockAccountManagerImpl.java
@@@ -16,6 -16,6 +16,27 @@@
  // under the License.
  package com.cloud.user;
  
++import java.net.InetAddress;
++import java.util.List;
++import java.util.Map;
++
++import javax.naming.ConfigurationException;
++
++import org.apache.cloudstack.acl.ControlledEntity;
++import org.apache.cloudstack.acl.RoleType;
++import org.apache.cloudstack.acl.SecurityChecker.AccessType;
++import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
++import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
++import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
++import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
++import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
++import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
++import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
++import 
org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
++import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
++import org.apache.cloudstack.framework.config.ConfigKey;
++import org.springframework.stereotype.Component;
++
  import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
  import com.cloud.api.query.vo.ControlledViewEntity;
  import com.cloud.dc.DataCenter;
@@@ -34,25 -34,25 +55,6 @@@ import com.cloud.utils.component.Manage
  import com.cloud.utils.component.ManagerBase;
  import com.cloud.utils.db.SearchBuilder;
  import com.cloud.utils.db.SearchCriteria;
--import org.apache.cloudstack.acl.ControlledEntity;
--import org.apache.cloudstack.acl.RoleType;
--import org.apache.cloudstack.acl.SecurityChecker.AccessType;
--import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
--import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
--import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
--import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
--import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
--import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
--import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
--import 
org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
--import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
--import org.apache.cloudstack.framework.config.ConfigKey;
--import org.springframework.stereotype.Component;
--
--import javax.naming.ConfigurationException;
--import java.net.InetAddress;
--import java.util.List;
--import java.util.Map;
  
  @Component
  public class MockAccountManagerImpl extends ManagerBase implements Manager, 
AccountManager {
@@@ -488,12 -484,8 +490,17 @@@
          return null;
      }
  
 +    @Override
 +    public List<String> getApiNameList() {
 +        return null;
 +    }
 +
 +    @Override
 +    public void validateUserPasswordAndUpdateIfNeeded(String newPassword, 
UserVO user, String currentPassword, boolean skipCurrentPassValidation) {
 +    }
++
+     @Override
+     public UserAccount 
clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
+         return null;
+     }
  }
diff --cc ui/src/components/view/ListView.vue
index 37c22edba77,0c19ea8edab..4541e1687ea
--- a/ui/src/components/view/ListView.vue
+++ b/ui/src/components/view/ListView.vue
@@@ -999,22 -911,19 +999,22 @@@ export default 
        return host.state
      },
      getColumnKey (name) {
-       if (typeof name === 'object') {
-         name = Object.keys(name).includes('field') ? name.field : 
name.customTitle
+       if (typeof name !== 'object' || name === null) {
+         return name
        }
-       return name
+       return name.field ?? name.customTitle ?? Object.keys(name)[0]
      },
-     getColumTitle (name) {
-       if (typeof name === 'object') {
-         name = Object.keys(name).includes('customTitle') ? name.customTitle : 
name.field
+     getColumnTitle (name) {
+       if (typeof name !== 'object' || name === null) {
+         return name
        }
-       return name
+       return name.customTitle ?? name.field ?? Object.keys(name)[0]
      },
 +    handleResizeColumn (w, col) {
 +      col.width = w
 +    },
      updateSelectedColumns (name) {
 -      this.$emit('update-selected-columns', name)
 +      this.$emit('update-selected-columns', this.getColumnKey(name))
      },
      getVmRouteUsingType (record) {
        switch (record.virtualmachinetype) {


Reply via email to