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

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


The following commit(s) were added to refs/heads/main by this push:
     new 8c8d115a1e1 feature: Support Multi-arch Zones (#9619)
8c8d115a1e1 is described below

commit 8c8d115a1e130305eda9b7742bc4f2e58a9c33af
Author: Nicolas Vazquez <[email protected]>
AuthorDate: Fri Sep 6 03:44:54 2024 -0300

    feature: Support Multi-arch Zones (#9619)
    
    This introduces the multi-arch zones, allowing users to select the VM arch 
upon deployment.
    
    Multi-arch zone support in CloudStack can allow admins to mix x86_64 & 
arm64 hosts within the same zone with the following changes proposed:
    - All hosts in a clusters need to be homogenous, wrt host CPU type (amd64 
vs arm64) and hypevisor
    - Arch-aware templates & ISOs:
       -  Add support for a new arch field (default set of: amd64 and arm64), 
when unspecified defaults to amd64 and for existing templates & iso
       -  Allow admins to edit the arch type of the registered template & iso
    - Arch-aware clusters and host:
       - Add new attribute field for cluster and hosts (kvm host agents can 
automatically report this, arch of the first host of the cluster is cluster's 
architecture), defaults to amd64 when not specified
       - Allow admins to edit the arch of an existing cluster
    - VM deployment form (UI):
       - In a multi-arch zone/env, the VM deployment form can allow some kind 
of template/iso filtration in the UI
       - Users should be able to select arch: amd64 & arm64; but this is shown 
only in a multi-arch zone (env)
    - VM orchestration and lifecycle operations:
       - Use of VM/template's arch to correctly decide where to provision the 
VM (on the correct strictly arch-matching host/clusters) & other lifecycle 
operations (such as migration from/to arch-matching hosts)
    
    Co-authored-by: Rohit Yadav <[email protected]>
---
 agent/src/main/java/com/cloud/agent/Agent.java     | 17 ++++++
 api/src/main/java/com/cloud/cpu/CPU.java           | 67 ++++++++++++++++++++++
 api/src/main/java/com/cloud/host/Host.java         |  3 +
 api/src/main/java/com/cloud/org/Cluster.java       |  3 +
 .../com/cloud/template/VirtualMachineTemplate.java |  3 +
 .../org/apache/cloudstack/api/ApiConstants.java    |  3 +
 .../cloudstack/api/BaseUpdateTemplateOrIsoCmd.java | 14 +++++
 .../api/command/admin/cluster/AddClusterCmd.java   | 10 ++++
 .../command/admin/cluster/UpdateClusterCmd.java    | 14 +++++
 .../api/command/user/iso/ListIsosCmd.java          | 14 +++++
 .../api/command/user/iso/RegisterIsoCmd.java       | 14 +++++
 .../template/GetUploadParamsForTemplateCmd.java    | 10 ++++
 .../command/user/template/ListTemplatesCmd.java    | 14 +++++
 .../command/user/template/RegisterTemplateCmd.java | 10 ++++
 .../cloudstack/api/response/ClusterResponse.java   | 12 ++++
 .../cloudstack/api/response/HostResponse.java      | 12 ++++
 .../cloudstack/api/response/TemplateResponse.java  |  8 +++
 .../cloudstack/api/response/ZoneResponse.java      |  8 +++
 .../java/com/cloud/agent/api/ReadyCommand.java     |  9 +++
 .../java/com/cloud/agent/api/StartupCommand.java   |  9 +++
 .../com/cloud/agent/api/StartupRoutingCommand.java |  9 +++
 .../api/agent/test/CheckOnHostCommandTest.java     |  8 ++-
 .../com/cloud/agent/manager/AgentManagerImpl.java  | 13 +++++
 .../datacenter/entity/api/db/EngineClusterVO.java  | 15 +++++
 .../datacenter/entity/api/db/EngineHostVO.java     | 15 +++++
 .../src/main/java/com/cloud/dc/ClusterVO.java      | 15 +++++
 .../src/main/java/com/cloud/dc/dao/ClusterDao.java |  5 ++
 .../main/java/com/cloud/dc/dao/ClusterDaoImpl.java | 30 ++++++++++
 .../src/main/java/com/cloud/host/HostVO.java       | 15 +++++
 .../main/java/com/cloud/storage/VMTemplateVO.java  | 18 +++++-
 .../apache/cloudstack/util/CPUArchConverter.java   | 33 +++++------
 .../resources/META-INF/db/schema-41910to42000.sql  |  5 ++
 .../META-INF/db/views/cloud.host_view.sql          |  1 +
 .../META-INF/db/views/cloud.template_view.sql      |  1 +
 .../storage/image/TemplateServiceImpl.java         |  2 +-
 .../storage/image/store/TemplateObject.java        |  6 ++
 .../kvm/resource/LibvirtComputingResource.java     |  4 +-
 .../apache/cloudstack/utils/linux/KVMHostInfo.java | 12 ++++
 .../java/com/cloud/hypervisor/guru/VMwareGuru.java |  3 +-
 .../version/KubernetesVersionManagerImpl.java      |  8 ++-
 .../version/AddKubernetesSupportedVersionCmd.java  | 10 ++++
 .../version/KubernetesVersionServiceTest.java      |  2 +
 server/src/main/java/com/cloud/api/ApiDBUtils.java |  5 ++
 .../main/java/com/cloud/api/ApiResponseHelper.java |  3 +
 .../java/com/cloud/api/query/QueryManagerImpl.java | 11 +++-
 .../cloud/api/query/dao/DataCenterJoinDaoImpl.java |  5 ++
 .../com/cloud/api/query/dao/HostJoinDaoImpl.java   |  3 +
 .../cloud/api/query/dao/TemplateJoinDaoImpl.java   |  6 ++
 .../java/com/cloud/api/query/vo/HostJoinVO.java    | 10 ++++
 .../com/cloud/api/query/vo/TemplateJoinVO.java     | 10 ++++
 .../deploy/DeploymentPlanningManagerImpl.java      | 21 +++++++
 .../com/cloud/resource/ResourceManagerImpl.java    | 18 ++++++
 .../java/com/cloud/storage/TemplateProfile.java    | 18 ++++--
 .../upload/params/TemplateUploadParams.java        |  5 +-
 .../cloud/storage/upload/params/UploadParams.java  |  2 +
 .../storage/upload/params/UploadParamsBase.java    | 14 ++++-
 .../java/com/cloud/template/TemplateAdapter.java   |  5 +-
 .../com/cloud/template/TemplateAdapterBase.java    | 29 +++++-----
 .../com/cloud/template/TemplateManagerImpl.java    | 13 ++++-
 .../deploy/DeploymentPlanningManagerImplTest.java  | 13 +++++
 ui/public/locales/en.json                          |  3 +
 ui/src/components/view/InfoCard.vue                |  3 +
 ui/src/config/section/image.js                     |  4 +-
 ui/src/config/section/infra/clusters.js            |  9 ++-
 ui/src/config/section/infra/hosts.js               |  2 +-
 ui/src/views/compute/DeployVM.vue                  | 57 +++++++++++++++++-
 .../views/image/AddKubernetesSupportedVersion.vue  | 36 +++++++++++-
 ui/src/views/image/RegisterOrUploadIso.vue         | 37 +++++++++++-
 ui/src/views/image/RegisterOrUploadTemplate.vue    | 36 +++++++++++-
 ui/src/views/image/UpdateISO.vue                   | 37 +++++++++++-
 ui/src/views/image/UpdateTemplate.vue              | 38 +++++++++++-
 ui/src/views/infra/ClusterAdd.vue                  | 33 +++++++++++
 72 files changed, 904 insertions(+), 66 deletions(-)

diff --git a/agent/src/main/java/com/cloud/agent/Agent.java 
b/agent/src/main/java/com/cloud/agent/Agent.java
index d2d4f165979..15f010808ac 100644
--- a/agent/src/main/java/com/cloud/agent/Agent.java
+++ b/agent/src/main/java/com/cloud/agent/Agent.java
@@ -504,6 +504,13 @@ public class Agent implements HandlerFactory, 
IAgentControl, AgentStatusUpdater
         startup.setGuid(getResourceGuid());
         startup.setResourceName(getResourceName());
         startup.setVersion(getVersion());
+        startup.setArch(getAgentArch());
+    }
+
+    protected String getAgentArch() {
+        final Script command = new Script("/usr/bin/arch", 500, logger);
+        final OutputInterpreter.OneLineParser parser = new 
OutputInterpreter.OneLineParser();
+        return command.execute(parser);
     }
 
     @Override
@@ -858,11 +865,21 @@ public class Agent implements HandlerFactory, 
IAgentControl, AgentStatusUpdater
             setId(ready.getHostId());
         }
 
+        verifyAgentArch(ready.getArch());
         processManagementServerList(ready.getMsHostList(), 
ready.getLbAlgorithm(), ready.getLbCheckInterval());
 
         logger.info("Ready command is processed for agent id = {}", getId());
     }
 
+    private void verifyAgentArch(String arch) {
+        if (StringUtils.isNotBlank(arch)) {
+            String agentArch = getAgentArch();
+            if (!arch.equals(agentArch)) {
+                logger.error("Unexpected arch {}, expected {}", agentArch, 
arch);
+            }
+        }
+    }
+
     public void processOtherTask(final Task task) {
         final Object obj = task.get();
         if (obj instanceof Response) {
diff --git a/api/src/main/java/com/cloud/cpu/CPU.java 
b/api/src/main/java/com/cloud/cpu/CPU.java
new file mode 100644
index 00000000000..4e1b9f5a501
--- /dev/null
+++ b/api/src/main/java/com/cloud/cpu/CPU.java
@@ -0,0 +1,67 @@
+// 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.
+package com.cloud.cpu;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class CPU {
+
+    public static final String archX86Identifier = "i686";
+    public static final String archX86_64Identifier = "x86_64";
+    public static final String archARM64Identifier = "aarch64";
+
+    public static class CPUArch {
+        private static final Map<String, CPUArch> cpuArchMap = new 
LinkedHashMap<>();
+
+        public static final CPUArch archX86 = new CPUArch(archX86Identifier, 
32);
+        public static final CPUArch amd64 = new CPUArch(archX86_64Identifier, 
64);
+        public static final CPUArch arm64 = new CPUArch(archARM64Identifier, 
64);
+
+        private String type;
+        private int bits;
+
+        public CPUArch(String type, int bits) {
+            this.type = type;
+            this.bits = bits;
+            cpuArchMap.put(type, this);
+        }
+
+        public String getType() {
+            return this.type;
+        }
+
+        public int getBits() {
+            return this.bits;
+        }
+
+        public static CPUArch fromType(String type) {
+            if (StringUtils.isBlank(type)) {
+                return amd64;
+            }
+            switch (type) {
+                case archX86Identifier: return archX86;
+                case archX86_64Identifier: return amd64;
+                case archARM64Identifier: return arm64;
+                default: throw new 
CloudRuntimeException(String.format("Unsupported arch type: %s", type));
+            }
+        }
+    }
+}
diff --git a/api/src/main/java/com/cloud/host/Host.java 
b/api/src/main/java/com/cloud/host/Host.java
index 4a3b914364f..56b4ed75a31 100644
--- a/api/src/main/java/com/cloud/host/Host.java
+++ b/api/src/main/java/com/cloud/host/Host.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.host;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.resource.ResourceState;
 import com.cloud.utils.fsm.StateObject;
@@ -208,4 +209,6 @@ public interface Host extends StateObject<Status>, 
Identity, Partition, HAResour
     boolean isDisabled();
 
     ResourceState getResourceState();
+
+    CPU.CPUArch getArch();
 }
diff --git a/api/src/main/java/com/cloud/org/Cluster.java 
b/api/src/main/java/com/cloud/org/Cluster.java
index 4079c88dfde..5124168084c 100644
--- a/api/src/main/java/com/cloud/org/Cluster.java
+++ b/api/src/main/java/com/cloud/org/Cluster.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.org;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.org.Managed.ManagedState;
 import org.apache.cloudstack.kernel.Partition;
@@ -38,4 +39,6 @@ public interface Cluster extends Grouping, Partition {
     AllocationState getAllocationState();
 
     ManagedState getManagedState();
+
+    CPU.CPUArch getArch();
 }
diff --git a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java 
b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java
index 1f8cef0365b..d8872d5fe72 100644
--- a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java
+++ b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java
@@ -19,6 +19,7 @@ package com.cloud.template;
 import java.util.Date;
 import java.util.Map;
 
+import com.cloud.cpu.CPU;
 import com.cloud.user.UserData;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.Identity;
@@ -148,4 +149,6 @@ public interface VirtualMachineTemplate extends 
ControlledEntity, Identity, Inte
 
     UserData.UserDataOverridePolicy getUserDataOverridePolicy();
 
+    CPU.CPUArch getArch();
+
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 07bb0183f65..ad953e97867 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -36,6 +36,7 @@ public class ApiConstants {
     public static final String ANNOTATION = "annotation";
     public static final String API_KEY = "apikey";
     public static final String ARCHIVED = "archived";
+    public static final String ARCH = "arch";
     public static final String AS_NUMBER = "asnumber";
     public static final String AS_NUMBER_ID = "asnumberid";
     public static final String ASN_RANGE = "asnrange";
@@ -326,6 +327,8 @@ public class ApiConstants {
     public static final String MIGRATIONS = "migrations";
     public static final String MEMORY = "memory";
     public static final String MODE = "mode";
+    public static final String MULTI_ARCH = "ismultiarch";
+    public static final String NSX_MODE = "nsxmode";
     public static final String NETWORK_MODE = "networkmode";
     public static final String NSX_ENABLED = "isnsxenabled";
     public static final String NAME = "name";
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java
index e3aead6881b..9a8282df112 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java
@@ -16,8 +16,10 @@
 // under the License.
 package org.apache.cloudstack.api;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.response.GuestOSResponse;
 import org.apache.cloudstack.api.response.TemplateResponse;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.Collection;
 import java.util.Map;
@@ -77,6 +79,11 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends 
BaseCmd {
             description = "optional boolean field, which indicates if details 
should be cleaned up or not (if set to true, details removed for this resource, 
details field ignored; if false or not set, no action)")
     private Boolean cleanupDetails;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the template/ISO. Valid options 
are: x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -141,4 +148,11 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends 
BaseCmd {
     public boolean isCleanupDetails(){
         return cleanupDetails == null ? false : cleanupDetails.booleanValue();
     }
+
+    public CPU.CPUArch getCPUArch() {
+        if (StringUtils.isBlank(arch)) {
+            return null;
+        }
+        return CPU.CPUArch.fromType(arch);
+    }
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java
index 184a443d9db..69cb43ce40e 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java
@@ -20,6 +20,7 @@ package org.apache.cloudstack.api.command.admin.cluster;
 import java.util.ArrayList;
 import java.util.List;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 
 import org.apache.cloudstack.api.APICommand;
@@ -67,6 +68,11 @@ public class AddClusterCmd extends BaseCmd {
                description = "hypervisor type of the cluster: 
XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3")
     private String hypervisor;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the cluster. Valid options are: 
x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, 
required = true, description = "type of the cluster: CloudManaged, 
ExternalManaged")
     private String clusterType;
 
@@ -204,6 +210,10 @@ public class AddClusterCmd extends BaseCmd {
         return ApiCommandResourceType.Cluster;
     }
 
+    public CPU.CPUArch getArch() {
+        return CPU.CPUArch.fromType(arch);
+    }
+
     @Override
     public void execute() {
         try {
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java
index 77bb97fd39d..c4ee87380ed 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.admin.cluster;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 
 import org.apache.cloudstack.api.APICommand;
@@ -29,6 +30,7 @@ import org.apache.cloudstack.api.response.ClusterResponse;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.org.Cluster;
 import com.cloud.user.Account;
+import org.apache.commons.lang3.StringUtils;
 
 @APICommand(name = "updateCluster", description = "Updates an existing 
cluster", responseObject = ClusterResponse.class,
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -53,6 +55,11 @@ public class UpdateClusterCmd extends BaseCmd {
     @Parameter(name = ApiConstants.MANAGED_STATE, type = CommandType.STRING, 
description = "whether this cluster is managed by cloudstack")
     private String managedState;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the cluster. Valid options are: 
x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     public String getClusterName() {
         return clusterName;
     }
@@ -108,6 +115,13 @@ public class UpdateClusterCmd extends BaseCmd {
         return ApiCommandResourceType.Cluster;
     }
 
+    public CPU.CPUArch getArch() {
+        if (StringUtils.isBlank(arch)) {
+            return null;
+        }
+        return CPU.CPUArch.fromType(arch);
+    }
+
     @Override
     public void execute() {
         Cluster cluster = _resourceService.getCluster(getId());
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java 
b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java
index 04dcbf8ca96..5c4d606a93c 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.iso;
 
+import com.cloud.cpu.CPU;
 import com.cloud.server.ResourceIcon;
 import com.cloud.server.ResourceTag;
 import org.apache.cloudstack.api.response.ResourceIconResponse;
@@ -34,6 +35,7 @@ import org.apache.cloudstack.context.CallContext;
 
 import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
 import com.cloud.user.Account;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 
@@ -88,6 +90,11 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd 
implements UserCmd {
     @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = 
CommandType.BOOLEAN, description = "flag to display the resource image for the 
isos")
     private Boolean showIcon;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the ISO. Valid options are: x86_64, 
aarch64",
+            since = "4.20")
+    private String arch;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -159,6 +166,13 @@ public class ListIsosCmd extends 
BaseListTaggedResourcesCmd implements UserCmd {
         return onlyReady;
     }
 
+    public CPU.CPUArch getArch() {
+        if (StringUtils.isBlank(arch)) {
+            return null;
+        }
+        return CPU.CPUArch.fromType(arch);
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
index becfdcd653d..81f52552289 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java
@@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.iso;
 
 import java.util.List;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
@@ -118,6 +119,11 @@ public class RegisterIsoCmd extends BaseCmd implements 
UserCmd {
             description = "true if password reset feature is supported; 
default is false")
     private Boolean passwordEnabled;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the ISO. Valid options are: x86_64, 
aarch64",
+            since = "4.20")
+    private String arch;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -229,6 +235,14 @@ public class RegisterIsoCmd extends BaseCmd implements 
UserCmd {
         return passwordEnabled == null ? false : passwordEnabled;
     }
 
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
+
+    public CPU.CPUArch getArch() {
+        return CPU.CPUArch.fromType(arch);
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java
index c878fda8240..8fa1a5d53eb 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java
@@ -22,6 +22,7 @@ import java.net.MalformedURLException;
 import java.util.Collection;
 import java.util.Map;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor;
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
@@ -55,6 +56,11 @@ public class GetUploadParamsForTemplateCmd extends 
AbstractGetUploadParamsCmd {
             description = "the ID of the OS Type that best represents the OS 
of this template. Not required for VMware as the guest OS is obtained from the 
OVF file.")
     private Long osTypeId;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the template. Valid options are: 
x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     @Parameter(name = ApiConstants.BITS, type = CommandType.INTEGER, 
description = "32 or 64 bits support. 64 by default")
     private Integer bits;
 
@@ -162,6 +168,10 @@ public class GetUploadParamsForTemplateCmd extends 
AbstractGetUploadParamsCmd {
                 Boolean.TRUE.equals(deployAsIs);
     }
 
+    public CPU.CPUArch getArch() {
+        return CPU.CPUArch.fromType(arch);
+    }
+
     @Override
     public void execute() throws ServerApiException {
         validateRequest();
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
index 113080257d0..bff65ef70a9 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.template;
 
+import com.cloud.cpu.CPU;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.server.ResourceIcon;
 import com.cloud.server.ResourceTag;
@@ -41,6 +42,7 @@ import org.apache.cloudstack.context.CallContext;
 import com.cloud.template.VirtualMachineTemplate;
 import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
 import com.cloud.user.Account;
+import org.apache.commons.lang3.StringUtils;
 
 @APICommand(name = "listTemplates", description = "List all public, private, 
and privileged templates.", responseObject = TemplateResponse.class, entityType 
= {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted,
         requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -104,6 +106,11 @@ public class ListTemplatesCmd extends 
BaseListTaggedResourcesCmd implements User
             since = "4.19.0")
     private Boolean isVnf;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the template. Valid options are: 
x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -191,6 +198,13 @@ public class ListTemplatesCmd extends 
BaseListTaggedResourcesCmd implements User
         return isVnf;
     }
 
+    public CPU.CPUArch getArch() {
+        if (StringUtils.isBlank(arch)) {
+            return null;
+        }
+        return CPU.CPUArch.fromType(arch);
+    }
+
     @Override
     public String getCommandName() {
         return s_name;
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
index 1e5c4af9c15..a7826dedcd0 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.api.command.user.template;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
@@ -172,6 +173,11 @@ public class RegisterTemplateCmd extends BaseCmd 
implements UserCmd {
             since = "4.19.0")
     private String templateType;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the template. Valid options are: 
x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -293,6 +299,10 @@ public class RegisterTemplateCmd extends BaseCmd 
implements UserCmd {
         return templateType;
     }
 
+    public CPU.CPUArch getArch() {
+        return CPU.CPUArch.fromType(arch);
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java
index ca01a2012f6..1c69849239f 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java
@@ -91,6 +91,10 @@ public class ClusterResponse extends 
BaseResponseWithAnnotations {
     @Param(description = "Meta data associated with the zone (key/value 
pairs)")
     private Map<String, String> resourceDetails;
 
+    @SerializedName(ApiConstants.ARCH)
+    @Param(description = "CPU Arch of the hosts in the cluster", since = 
"4.20")
+    private String arch;
+
     public String getId() {
         return id;
     }
@@ -247,4 +251,12 @@ public class ClusterResponse extends 
BaseResponseWithAnnotations {
     public void setCapacities(List<CapacityResponse> capacities) {
         this.capacities = capacities;
     }
+
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
+
+    public String getArch() {
+        return arch;
+    }
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java
index 274c1fd43c1..62bcc07b16d 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java
@@ -290,6 +290,10 @@ public class HostResponse extends 
BaseResponseWithAnnotations {
     @Param(description = "true if the host supports instance conversion (using 
virt-v2v)", since = "4.19.1")
     private Boolean instanceConversionSupported;
 
+    @SerializedName(ApiConstants.ARCH)
+    @Param(description = "CPU Arch of the host", since = "4.20")
+    private String arch;
+
     @Override
     public String getObjectId() {
         return this.getId();
@@ -787,6 +791,14 @@ public class HostResponse extends 
BaseResponseWithAnnotations {
         isTagARule = tagARule;
     }
 
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
+
+    public String getArch() {
+        return arch;
+    }
+
     public Long getCpuAllocatedValue() {
         return cpuAllocatedValue;
     }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java
index 604c9f0955f..98e96091d8c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java
@@ -183,6 +183,10 @@ public class TemplateResponse extends 
BaseResponseWithTagInformation implements
     @Param(description = "Lists the download progress of a template across all 
secondary storages")
     private List<Map<String, String>> downloadDetails;
 
+    @SerializedName(ApiConstants.ARCH)
+    @Param(description = "CPU Arch of the template", since = "4.20")
+    private String arch;
+
     @SerializedName(ApiConstants.BITS)
     @Param(description = "the processor bit size", since = "4.10")
     private int bits;
@@ -520,4 +524,8 @@ public class TemplateResponse extends 
BaseResponseWithTagInformation implements
     public void setUserDataParams(String userDataParams) {
         this.userDataParams = userDataParams;
     }
+
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
index 8a4c41f2957..143dfad0eaf 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
@@ -149,6 +149,10 @@ public class ZoneResponse extends 
BaseResponseWithAnnotations implements SetReso
     @Param(description = "true, if zone is NSX enabled", since = "4.20.0")
     private boolean nsxEnabled = false;
 
+    @SerializedName(ApiConstants.MULTI_ARCH)
+    @Param(description = "true, if zone contains clusters and hosts from 
different CPU architectures", since = "4.20")
+    private boolean multiArch;
+
     @SerializedName(ApiConstants.ASN_RANGE)
     @Param(description = "AS Number Range")
     private String asnRange;
@@ -397,6 +401,10 @@ public class ZoneResponse extends 
BaseResponseWithAnnotations implements SetReso
         this.nsxEnabled = nsxEnabled;
     }
 
+    public void setMultiArch(boolean multiArch) {
+        this.multiArch = multiArch;
+    }
+
     public void setAsnRange(String asnRange) {
         this.asnRange = asnRange;
     }
diff --git a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java 
b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java
index 637e4f54da0..42f1d264a50 100644
--- a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java
@@ -34,6 +34,7 @@ public class ReadyCommand extends Command {
     private String lbAlgorithm;
     private Long lbCheckInterval;
     private Boolean enableHumanReadableSizes;
+    private String arch;
 
     public ReadyCommand(Long dcId) {
         super();
@@ -94,4 +95,12 @@ public class ReadyCommand extends Command {
     public Boolean getEnableHumanReadableSizes() {
         return enableHumanReadableSizes;
     }
+
+    public String getArch() {
+        return arch;
+    }
+
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
 }
diff --git a/core/src/main/java/com/cloud/agent/api/StartupCommand.java 
b/core/src/main/java/com/cloud/agent/api/StartupCommand.java
index 5f2c00d0be6..cca5e16b585 100644
--- a/core/src/main/java/com/cloud/agent/api/StartupCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/StartupCommand.java
@@ -47,6 +47,7 @@ public class StartupCommand extends Command {
     String resourceName;
     String gatewayIpAddress;
     String msHostList;
+    String arch;
 
     public StartupCommand(Host.Type type) {
         this.type = type;
@@ -290,6 +291,14 @@ public class StartupCommand extends Command {
         this.msHostList = msHostList;
     }
 
+    public String getArch() {
+        return arch;
+    }
+
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
+
     @Override
     public boolean executeInSequence() {
         return false;
diff --git a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java 
b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java
index 2d4ed8c9cc4..286fced0c58 100644
--- a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java
@@ -32,6 +32,7 @@ public class StartupRoutingCommand extends StartupCommand {
     Integer cpuSockets;
     int cpus;
     long speed;
+    String cpuArch;
     long memory;
     long dom0MinMemory;
     boolean poolSync;
@@ -201,4 +202,12 @@ public class StartupRoutingCommand extends StartupCommand {
     public void setHostHealthCheckResult(Boolean hostHealthCheckResult) {
         this.hostHealthCheckResult = hostHealthCheckResult;
     }
+
+    public String getCpuArch() {
+        return cpuArch;
+    }
+
+    public void setCpuArch(String cpuArch) {
+        this.cpuArch = cpuArch;
+    }
 }
diff --git 
a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java
 
b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java
index f7d756268f4..287769d6a76 100644
--- 
a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java
+++ 
b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java
@@ -27,6 +27,7 @@ import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 
+import com.cloud.cpu.CPU;
 import org.junit.Test;
 
 import com.cloud.agent.api.CheckOnHostCommand;
@@ -272,7 +273,12 @@ public class CheckOnHostCommandTest {
         @Override
         public ResourceState getResourceState() {
             return ResourceState.Enabled;
-        };
+        }
+
+        @Override
+        public CPU.CPUArch getArch() {
+            return CPU.CPUArch.amd64;
+        }
     };
 
     CheckOnHostCommand cohc = new CheckOnHostCommand(host);
diff --git 
a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
 
b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
index b693729f841..27b3ac2d751 100644
--- 
a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
@@ -39,6 +39,7 @@ import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
 import com.cloud.configuration.Config;
+import com.cloud.org.Cluster;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.db.GlobalLock;
 import org.apache.cloudstack.agent.lb.IndirectAgentLB;
@@ -1097,6 +1098,7 @@ public class AgentManagerImpl extends ManagerBase 
implements AgentManager, Handl
                 agentMSHostList.addAll(Arrays.asList(msHosts[0].split(",")));
             }
         }
+        ready.setArch(host.getArch().getType());
         AgentAttache attache = null;
         GlobalLock joinLock = getHostJoinLock(host.getId());
         if (joinLock.lock(60)) {
@@ -1128,6 +1130,7 @@ public class AgentManagerImpl extends ManagerBase 
implements AgentManager, Handl
         try {
             final HostVO host = 
_resourceMgr.createHostVOForConnectedAgent(startup);
             if (host != null) {
+                checkHostArchOnCluster(host);
                 ready = new ReadyCommand(host.getDataCenterId(), host.getId(), 
NumbersUtil.enableHumanReadableSizes);
                 attache = sendReadyAndGetAttache(host, ready, link, startup);
             }
@@ -1154,6 +1157,16 @@ public class AgentManagerImpl extends ManagerBase 
implements AgentManager, Handl
         return attache;
     }
 
+    private void checkHostArchOnCluster(HostVO host) {
+        Cluster cluster = _resourceMgr.getCluster(host.getClusterId());
+        if (cluster != null && !cluster.getArch().equals(host.getArch())) {
+            String msg = String.format("The host %s has arch %s and cannot be 
added to the %s cluster %s",
+                    host.getName(), host.getArch().getType(), 
cluster.getArch().getType(), cluster.getName());
+            logger.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
     protected class SimulateStartTask extends ManagedContextRunnable {
         ServerResource resource;
         Map<String, String> details;
diff --git 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java
 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java
index be35cea3c07..c00d939b3df 100644
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java
@@ -16,6 +16,7 @@
 // under the License.
 package org.apache.cloudstack.engine.datacenter.entity.api.db;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.org.Cluster;
 import com.cloud.org.Grouping;
@@ -26,6 +27,7 @@ import com.cloud.utils.db.StateMachine;
 import org.apache.cloudstack.api.Identity;
 import 
org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State;
 import 
org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State.Event;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 
 import javax.persistence.Column;
@@ -75,6 +77,10 @@ public class EngineClusterVO implements EngineCluster, 
Identity {
     @Enumerated(value = EnumType.STRING)
     AllocationState allocationState;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private CPU.CPUArch arch;
+
     @Column(name = "managed_state")
     @Enumerated(value = EnumType.STRING)
     ManagedState managedState;
@@ -245,6 +251,15 @@ public class EngineClusterVO implements EngineCluster, 
Identity {
         return state;
     }
 
+    @Override
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
+
+    public void setArch(CPU.CPUArch arch) {
+        this.arch = arch;
+    }
+
     @Override
     public PartitionType partitionType() {
         return PartitionType.Cluster;
diff --git 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java
 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java
index f8535b6b6cc..d804f079e17 100644
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java
@@ -38,6 +38,7 @@ import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.Identity;
 import 
org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State;
 import 
org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State.Event;
@@ -50,6 +51,7 @@ import com.cloud.util.StoragePoolTypeConverter;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.db.GenericDao;
 import com.cloud.utils.db.StateMachine;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 
 @Entity
@@ -122,6 +124,10 @@ public class EngineHostVO implements EngineHost, Identity {
     @Convert(converter = HypervisorTypeConverter.class)
     private HypervisorType hypervisorType;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private CPU.CPUArch arch;
+
     @Column(name = "proxy_port")
     private Integer proxyPort;
 
@@ -725,6 +731,15 @@ public class EngineHostVO implements EngineHost, Identity {
         return resourceState;
     }
 
+    @Override
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
+
+    public void setArch(CPU.CPUArch arch) {
+        this.arch = arch;
+    }
+
     public void setResourceState(ResourceState state) {
         resourceState = state;
     }
diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java 
b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java
index 90591690eb0..434901ef5b3 100644
--- a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java
+++ b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java
@@ -16,12 +16,14 @@
 // under the License.
 package com.cloud.dc;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.org.Cluster;
 import com.cloud.org.Grouping;
 import com.cloud.org.Managed.ManagedState;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 
 import javax.persistence.Column;
@@ -69,6 +71,10 @@ public class ClusterVO implements Cluster {
     @Enumerated(value = EnumType.STRING)
     AllocationState allocationState;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private String arch;
+
     @Column(name = "managed_state")
     @Enumerated(value = EnumType.STRING)
     ManagedState managedState;
@@ -200,6 +206,15 @@ public class ClusterVO implements Cluster {
         return PartitionType.Cluster;
     }
 
+    @Override
+    public CPU.CPUArch getArch() {
+        return CPU.CPUArch.fromType(arch);
+    }
+
+    public void setArch(String arch) {
+        this.arch = arch;
+    }
+
     @Override
     public String toString() {
         return String.format("Cluster {id: \"%s\", name: \"%s\", uuid: 
\"%s\"}", id, name, uuid);
diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java 
b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java
index ab9c5cab8c4..6ecfdaeb058 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.dc.dao;
 
+import com.cloud.cpu.CPU;
 import com.cloud.dc.ClusterVO;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.utils.db.GenericDao;
@@ -50,4 +51,8 @@ public interface ClusterDao extends GenericDao<ClusterVO, 
Long> {
     List<Long> listAllClusters(Long zoneId);
 
     boolean getSupportsResigning(long clusterId);
+
+    List<CPU.CPUArch> getClustersArchsByZone(long zoneId);
+
+    List<ClusterVO> listClustersByArchAndZoneId(long zoneId, CPU.CPUArch arch);
 }
diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java
index 4d9bedba966..9a56f0f2d94 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.dc.dao;
 
+import com.cloud.cpu.CPU;
 import com.cloud.dc.ClusterDetailsDao;
 import com.cloud.dc.ClusterDetailsVO;
 import com.cloud.dc.ClusterVO;
@@ -43,6 +44,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 @Component
 public class ClusterDaoImpl extends GenericDaoBase<ClusterVO, Long> implements 
ClusterDao {
@@ -54,6 +56,8 @@ public class ClusterDaoImpl extends GenericDaoBase<ClusterVO, 
Long> implements C
     protected final SearchBuilder<ClusterVO> ZoneHyTypeSearch;
     protected final SearchBuilder<ClusterVO> ZoneClusterSearch;
     protected final SearchBuilder<ClusterVO> ClusterSearch;
+    protected final SearchBuilder<ClusterVO> ClusterDistinctArchSearch;
+    protected final SearchBuilder<ClusterVO> ClusterArchSearch;
 
     protected GenericSearchBuilder<ClusterVO, Long> ClusterIdSearch;
 
@@ -104,6 +108,16 @@ public class ClusterDaoImpl extends 
GenericDaoBase<ClusterVO, Long> implements C
         ClusterSearch = createSearchBuilder();
         ClusterSearch.select(null, Func.DISTINCT, 
ClusterSearch.entity().getHypervisorType());
         ClusterIdSearch.done();
+
+        ClusterDistinctArchSearch = createSearchBuilder();
+        ClusterDistinctArchSearch.and("dataCenterId", 
ClusterDistinctArchSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
+        ClusterDistinctArchSearch.select(null, Func.DISTINCT, 
ClusterDistinctArchSearch.entity().getArch());
+        ClusterDistinctArchSearch.done();
+
+        ClusterArchSearch = createSearchBuilder();
+        ClusterArchSearch.and("dataCenterId", 
ClusterArchSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
+        ClusterArchSearch.and("arch", ClusterArchSearch.entity().getArch(), 
SearchCriteria.Op.EQ);
+        ClusterArchSearch.done();
     }
 
     @Override
@@ -301,4 +315,20 @@ public class ClusterDaoImpl extends 
GenericDaoBase<ClusterVO, Long> implements C
 
         return false;
     }
+
+    @Override
+    public List<CPU.CPUArch> getClustersArchsByZone(long zoneId) {
+        SearchCriteria<ClusterVO> sc = ClusterDistinctArchSearch.create();
+        sc.setParameters("dataCenterId", zoneId);
+        List<ClusterVO> clusters = listBy(sc);
+        return 
clusters.stream().map(ClusterVO::getArch).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<ClusterVO> listClustersByArchAndZoneId(long zoneId, 
CPU.CPUArch arch) {
+        SearchCriteria<ClusterVO> sc = ClusterArchSearch.create();
+        sc.setParameters("dataCenterId", zoneId);
+        sc.setParameters("arch", arch);
+        return listBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/host/HostVO.java 
b/engine/schema/src/main/java/com/cloud/host/HostVO.java
index 1a507da7957..b5b634a73a7 100644
--- a/engine/schema/src/main/java/com/cloud/host/HostVO.java
+++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java
@@ -42,6 +42,8 @@ import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
 
+import com.cloud.cpu.CPU;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
 import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
@@ -153,6 +155,10 @@ public class HostVO implements Host {
     @Column(name = "hypervisor_version")
     private String hypervisorVersion;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private CPU.CPUArch arch;
+
     @Column(name = "update_count", updatable = true, nullable = false)
     protected long updated;    // This field should be updated everytime the 
state is updated.  There's no set method in the vo object because it is done 
with in the dao code.
 
@@ -738,6 +744,15 @@ public class HostVO implements Host {
         return resourceState;
     }
 
+    @Override
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
+
+    public void setArch(CPU.CPUArch arch) {
+        this.arch = arch;
+    }
+
     public void setResourceState(ResourceState state) {
         resourceState = state;
     }
diff --git a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java 
b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java
index 25b02db6479..9dc9734f8ab 100644
--- a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java
+++ b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java
@@ -32,7 +32,9 @@ import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
 
+import com.cloud.cpu.CPU;
 import com.cloud.user.UserData;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
 
@@ -167,6 +169,10 @@ public class VMTemplateVO implements 
VirtualMachineTemplate {
     @Enumerated(value = EnumType.STRING)
     UserData.UserDataOverridePolicy userDataLinkPolicy;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private CPU.CPUArch arch;
+
     @Override
     public String getUniqueName() {
         return uniqueName;
@@ -209,7 +215,7 @@ public class VMTemplateVO implements VirtualMachineTemplate 
{
 
     public VMTemplateVO(long id, String name, ImageFormat format, boolean 
isPublic, boolean featured, boolean isExtractable, TemplateType type, String 
url, boolean requiresHvm, int bits, long accountId, String cksum, String 
displayText, boolean enablePassword, long guestOSId, boolean bootable,
                         HypervisorType hyperType, String templateTag, 
Map<String, String> details, boolean sshKeyEnabled, boolean 
isDynamicallyScalable, boolean directDownload,
-                        boolean deployAsIs) {
+                        boolean deployAsIs, CPU.CPUArch arch) {
         this(id,
             name,
             format,
@@ -235,6 +241,7 @@ public class VMTemplateVO implements VirtualMachineTemplate 
{
         state = State.Active;
         this.directDownload = directDownload;
         this.deployAsIs = deployAsIs;
+        this.arch = arch;
     }
 
     public static VMTemplateVO createPreHostIso(Long id, String uniqueName, 
String name, ImageFormat format, boolean isPublic, boolean featured, 
TemplateType type,
@@ -673,4 +680,13 @@ public class VMTemplateVO implements 
VirtualMachineTemplate {
         this.userDataLinkPolicy = userDataLinkPolicy;
     }
 
+    @Override
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
+
+    public void setArch(CPU.CPUArch arch) {
+        this.arch = arch;
+    }
+
 }
diff --git a/api/src/main/java/com/cloud/org/Cluster.java 
b/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java
similarity index 60%
copy from api/src/main/java/com/cloud/org/Cluster.java
copy to 
engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java
index 4079c88dfde..e278809fb96 100644
--- a/api/src/main/java/com/cloud/org/Cluster.java
+++ 
b/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java
@@ -14,28 +14,23 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.org;
+package org.apache.cloudstack.util;
 
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.org.Managed.ManagedState;
-import org.apache.cloudstack.kernel.Partition;
+import com.cloud.cpu.CPU;
 
-public interface Cluster extends Grouping, Partition {
-    public static enum ClusterType {
-        CloudManaged, ExternalManaged;
-    };
+import javax.persistence.AttributeConverter;
+import javax.persistence.Converter;
 
-    String getName();
+@Converter
+public class CPUArchConverter implements AttributeConverter<CPU.CPUArch, 
String> {
 
-    long getDataCenterId();
+    @Override
+    public String convertToDatabaseColumn(CPU.CPUArch cpuArch) {
+        return cpuArch == null ? CPU.CPUArch.amd64.getType() : 
cpuArch.getType();
+    }
 
-    long getPodId();
-
-    HypervisorType getHypervisorType();
-
-    ClusterType getClusterType();
-
-    AllocationState getAllocationState();
-
-    ManagedState getManagedState();
+    @Override
+    public CPU.CPUArch convertToEntityAttribute(String attribute) {
+        return CPU.CPUArch.fromType(attribute);
+    }
 }
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
index 582460cb10b..ac7d7936833 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
@@ -581,6 +581,11 @@ CALL 
`cloud`.`IDEMPOTENT_MODIFY_COLUMN_CHAR_SET`('vpc_offerings', 'display_text'
 
 CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.roles','state', 'varchar(10) NOT 
NULL default "enabled" COMMENT "role state"');
 
+-- Multi-Arch Zones
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.cluster', 'arch', 'varchar(8) 
DEFAULT "x86_64" COMMENT "the CPU architecture of the hosts in the cluster"');
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'arch', 'varchar(8) DEFAULT 
"x86_64" COMMENT "the CPU architecture of the host"');
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template', 'arch', 'varchar(8) 
DEFAULT "x86_64" COMMENT "the CPU architecture of the template/ISO"');
+
 -- NAS B&R Plugin Backup Repository
 DROP TABLE IF EXISTS `cloud`.`backup_repository`;
 CREATE TABLE `cloud`.`backup_repository` (
diff --git 
a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql 
b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql
index 7bd4b3cc4a9..6fc8fb80386 100644
--- a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql
+++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql
@@ -41,6 +41,7 @@ SELECT
     host.cpus,
     host.speed,
     host.ram,
+    host.arch,
     cluster.id cluster_id,
     cluster.uuid cluster_uuid,
     cluster.name cluster_name,
diff --git 
a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql 
b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql
index 40b416b16de..339e43860d8 100644
--- a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql
+++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql
@@ -50,6 +50,7 @@ SELECT
     `vm_template`.`sort_key` AS `sort_key`,
     `vm_template`.`removed` AS `removed`,
     `vm_template`.`enable_sshkey` AS `enable_sshkey`,
+    `vm_template`.`arch` AS `arch`,
     `parent_template`.`id` AS `parent_template_id`,
     `parent_template`.`uuid` AS `parent_template_uuid`,
     `source_template`.`id` AS `source_template_id`,
diff --git 
a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
 
b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
index 518a3869833..5e21f37f4d5 100644
--- 
a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
+++ 
b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
@@ -814,7 +814,7 @@ public class TemplateServiceImpl implements TemplateService 
{
         String templateName = dataDiskTemplate.isIso() ? 
dataDiskTemplate.getPath().substring(dataDiskTemplate.getPath().lastIndexOf(File.separator)
 + 1) : template.getName() + suffix + diskCount;
         VMTemplateVO templateVO = new VMTemplateVO(templateId, templateName, 
format, false, false, false, ttype, template.getUrl(),
                 template.requiresHvm(), template.getBits(), 
template.getAccountId(), null, templateName, false, guestOsId, false, 
template.getHypervisorType(), null,
-                null, false, false, false, false);
+                null, false, false, false, false, template.getArch());
         if (dataDiskTemplate.isIso()){
             templateVO.setUniqueName(templateName);
         }
diff --git 
a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
 
b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
index 37ddc3573c1..b7d83c70223 100644
--- 
a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ 
b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import com.cloud.storage.StorageManager;
 import com.cloud.user.UserData;
 import org.apache.logging.log4j.Logger;
@@ -350,6 +351,11 @@ public class TemplateObject implements TemplateInfo {
         return imageVO.getUserDataOverridePolicy();
     }
 
+    @Override
+    public CPU.CPUArch getArch() {
+        return imageVO.getArch();
+    }
+
     @Override
     public DataTO getTO() {
         DataTO to = null;
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index f3e379f2c84..12eef2f9598 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -3655,7 +3655,6 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         if (dpdkSupport) {
             capabilities += ",dpdk";
         }
-
         final StartupRoutingCommand cmd =
                 new StartupRoutingCommand(info.getAllocatableCpus(), 
info.getCpuSpeed(), info.getTotalMemory(), info.getReservedMemory(), 
capabilities, hypervisorType,
                         RouterPrivateIpStrategy.HostLocal);
@@ -3675,6 +3674,9 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         if (healthCheckResult != HealthCheckResult.IGNORE) {
             cmd.setHostHealthCheckResult(healthCheckResult == 
HealthCheckResult.SUCCESS);
         }
+        if (StringUtils.isNotBlank(info.getCpuArch())) {
+            cmd.setCpuArch(info.getCpuArch());
+        }
 
         if (cmd.getHostDetails().containsKey("Host.OS")) {
             hostDistro = cmd.getHostDetails().get("Host.OS");
diff --git 
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
 
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
index 4293ee75f7e..c0b416410cb 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
@@ -53,12 +53,14 @@ public class KVMHostInfo {
     private int allocatableCpus;
     private int cpusockets;
     private long cpuSpeed;
+    private String cpuArch;
     private long totalMemory;
     private long reservedMemory;
     private long overCommitMemory;
     private List<String> capabilities = new ArrayList<>();
 
     private static String cpuInfoFreqFileName = 
"/sys/devices/system/cpu/cpu0/cpufreq/base_frequency";
+    private static String cpuArchCommand = "/usr/bin/arch";
 
     public KVMHostInfo(long reservedMemory, long overCommitMemory, long 
manualSpeed, int reservedCpus) {
         this.cpuSpeed = manualSpeed;
@@ -105,6 +107,10 @@ public class KVMHostInfo {
         return this.capabilities;
     }
 
+    public String getCpuArch() {
+        return cpuArch;
+    }
+
     protected static long getCpuSpeed(final String cpabilities, final NodeInfo 
nodeInfo) {
         long speed = 0L;
         speed = getCpuSpeedFromCommandLscpu();
@@ -201,6 +207,7 @@ public class KVMHostInfo {
                 this.cpusockets = hosts.sockets * hosts.nodes;
             }
             this.totalCpus = hosts.cpus;
+            this.cpuArch = getCPUArchFromCommand();
 
             final LibvirtCapXMLParser parser = new LibvirtCapXMLParser();
             parser.parseCapabilitiesXML(capabilities);
@@ -227,4 +234,9 @@ public class KVMHostInfo {
             LOGGER.error("Caught libvirt exception while fetching host 
information", e);
         }
     }
+
+    private String getCPUArchFromCommand() {
+        LOGGER.info("Fetching host CPU arch");
+        return Script.runSimpleBashScript(cpuArchCommand);
+    }
 }
diff --git 
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
 
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index 3ccf3bf8c62..b8bf08cd01a 100644
--- 
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++ 
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -30,6 +30,7 @@ import java.util.UUID;
 import javax.inject.Inject;
 
 import com.cloud.agent.api.to.NfsTO;
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.vmware.mo.DatastoreMO;
 import com.cloud.hypervisor.vmware.mo.HostMO;
 import com.cloud.hypervisor.vmware.util.VmwareClient;
@@ -642,7 +643,7 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
     private VMTemplateVO createVMTemplateRecord(String vmInternalName, long 
guestOsId, long accountId) {
         Long nextTemplateId = vmTemplateDao.getNextInSequence(Long.class, 
"id");
         VMTemplateVO templateVO = new VMTemplateVO(nextTemplateId, 
"Imported-from-" + vmInternalName, Storage.ImageFormat.OVA, false, false, 
false, Storage.TemplateType.USER, null,
-                false, 64, accountId, null, "Template imported from VM " + 
vmInternalName, false, guestOsId, false, HypervisorType.VMware, null, null, 
false, false, false, false);
+                false, 64, accountId, null, "Template imported from VM " + 
vmInternalName, false, guestOsId, false, HypervisorType.VMware, null, null, 
false, false, false, false, CPU.CPUArch.amd64);
         return vmTemplateDao.persist(templateVO);
     }
 
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
index ba2b4288d48..93e1ae2810a 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java
@@ -22,6 +22,7 @@ import java.util.List;
 
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.ApiCommandResourceType;
 import org.apache.cloudstack.api.ApiConstants;
 import 
org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd;
@@ -159,7 +160,7 @@ public class KubernetesVersionManagerImpl extends 
ManagerBase implements Kuberne
         return versions;
     }
 
-    private VirtualMachineTemplate registerKubernetesVersionIso(final Long 
zoneId, final String versionName, final String isoUrl, final String 
isoChecksum, final boolean directDownload) throws IllegalAccessException, 
NoSuchFieldException,
+    private VirtualMachineTemplate registerKubernetesVersionIso(final Long 
zoneId, final String versionName, final String isoUrl, final String 
isoChecksum, final boolean directDownload, CPU.CPUArch arch) throws 
IllegalAccessException, NoSuchFieldException,
             IllegalArgumentException, ResourceAllocationException {
         CallContext.register(CallContext.current(), 
ApiCommandResourceType.Iso);
         String isoName = String.format("%s-Kubernetes-Binaries-ISO", 
versionName);
@@ -177,6 +178,7 @@ public class KubernetesVersionManagerImpl extends 
ManagerBase implements Kuberne
             registerIsoCmd.setChecksum(isoChecksum);
         }
         registerIsoCmd.setDirectDownload(directDownload);
+        registerIsoCmd.setArch(arch.getType());
         
registerIsoCmd.setAccountName(accountManager.getSystemAccount().getAccountName());
         
registerIsoCmd.setDomainId(accountManager.getSystemAccount().getDomainId());
         try {
@@ -316,6 +318,8 @@ public class KubernetesVersionManagerImpl extends 
ManagerBase implements Kuberne
         final Integer minimumCpu = cmd.getMinimumCpu();
         final Integer minimumRamSize = cmd.getMinimumRamSize();
         final boolean isDirectDownload = cmd.isDirectDownload();
+        CPU.CPUArch arch = cmd.getArch();
+
         if (minimumCpu == null || minimumCpu < 
KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU) {
             throw new InvalidParameterValueException(String.format("Invalid 
value for %s parameter. Minimum %d vCPUs required.", 
ApiConstants.MIN_CPU_NUMBER, 
KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU));
         }
@@ -346,7 +350,7 @@ public class KubernetesVersionManagerImpl extends 
ManagerBase implements Kuberne
 
         VMTemplateVO template = null;
         try {
-            VirtualMachineTemplate vmTemplate = 
registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, 
isDirectDownload);
+            VirtualMachineTemplate vmTemplate = 
registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, 
isDirectDownload, arch);
             template = templateDao.findById(vmTemplate.getId());
         } catch (IllegalAccessException | NoSuchFieldException | 
IllegalArgumentException | ResourceAllocationException ex) {
             logger.error(String.format("Unable to register binaries ISO for 
supported kubernetes version, %s, with url: %s", name, isoUrl), ex);
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
 
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
index 139b79a182a..592ca6b0c25 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java
@@ -19,6 +19,7 @@ package 
org.apache.cloudstack.api.command.admin.kubernetes.version;
 
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiCommandResourceType;
@@ -87,6 +88,11 @@ public class AddKubernetesSupportedVersionCmd extends 
BaseCmd implements AdminCm
             description = "If set to true the Kubernetes supported version ISO 
will bypass Secondary Storage and be downloaded to Primary Storage on 
deployment. Default is false")
     private Boolean directDownload;
 
+    @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING,
+            description = "the CPU arch of the Kubernetes ISO. Valid options 
are: x86_64, aarch64",
+            since = "4.20")
+    private String arch;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -130,6 +136,10 @@ public class AddKubernetesSupportedVersionCmd extends 
BaseCmd implements AdminCm
         return (directDownload != null) ? directDownload : false;
     }
 
+    public CPU.CPUArch getArch() {
+        return CPU.CPUArch.fromType(arch);
+    }
+
     @Override
     public long getEntityOwnerId() {
         return CallContext.current().getCallingAccountId();
diff --git 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
index f7e816596c2..e6d25df10ef 100644
--- 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
+++ 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
+import com.cloud.cpu.CPU;
 import 
org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd;
 import 
org.apache.cloudstack.api.command.admin.kubernetes.version.DeleteKubernetesSupportedVersionCmd;
 import 
org.apache.cloudstack.api.command.admin.kubernetes.version.UpdateKubernetesSupportedVersionCmd;
@@ -213,6 +214,7 @@ public class KubernetesVersionServiceTest {
         when(cmd.getChecksum()).thenReturn(null);
         
when(cmd.getMinimumCpu()).thenReturn(KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU);
         
when(cmd.getMinimumRamSize()).thenReturn(KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE);
+        when(cmd.getArch()).thenReturn(CPU.CPUArch.amd64);
         Account systemAccount =  new AccountVO("system", 1L, "", 
Account.Type.ADMIN, "uuid");
         when(accountManager.getSystemAccount()).thenReturn(systemAccount);
         CallContext callContext = Mockito.mock(CallContext.class);
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java 
b/server/src/main/java/com/cloud/api/ApiDBUtils.java
index a3609e81b37..a169ebc0f19 100644
--- a/server/src/main/java/com/cloud/api/ApiDBUtils.java
+++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.acl.Role;
 import org.apache.cloudstack.acl.RoleService;
 import org.apache.cloudstack.affinity.AffinityGroup;
@@ -2286,4 +2287,8 @@ public class ApiDBUtils {
     public static SharedFSJoinVO newSharedFSView(SharedFS sharedFS) {
         return s_sharedFSJoinDao.newSharedFSView(sharedFS);
     }
+
+    public static List<CPU.CPUArch> listZoneClustersArchs(long zoneId) {
+        return s_clusterDao.getClustersArchsByZone(zoneId);
+    }
 }
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java 
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index b563641745f..810f0abd7e0 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -1530,6 +1530,9 @@ public class ApiResponseHelper implements 
ResponseGenerator {
         clusterResponse.setCpuOvercommitRatio(cpuOvercommitRatio);
         clusterResponse.setMemoryOvercommitRatio(memoryOvercommitRatio);
         
clusterResponse.setResourceDetails(_clusterDetailsDao.findDetails(cluster.getId()));
+        if (cluster.getArch() != null) {
+            clusterResponse.setArch(cluster.getArch().getType());
+        }
 
         if (showCapacities != null && showCapacities) {
             List<SummedCapacity> capacities = 
ApiDBUtils.getCapacityByClusterPodZone(null, null, cluster.getId());
diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java 
b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index 04b32505625..f4c9b19c192 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -36,6 +36,7 @@ import java.util.stream.Stream;
 
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker;
@@ -4476,7 +4477,7 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
                 null, cmd.getPageSizeVal(), cmd.getStartIndex(), 
cmd.getZoneId(), cmd.getStoragePoolId(),
                 cmd.getImageStoreId(), hypervisorType, showDomr, 
cmd.listInReadyState(), permittedAccounts, caller,
                 listProjectResourcesCriteria, tags, showRemovedTmpl, 
cmd.getIds(), parentTemplateId, cmd.getShowUnique(),
-                templateType, isVnf);
+                templateType, isVnf, cmd.getArch());
     }
 
     private Pair<List<TemplateJoinVO>, Integer> 
searchForTemplatesInternal(Long templateId, String name, String keyword,
@@ -4485,7 +4486,7 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
             boolean showDomr, boolean onlyReady, List<Account> 
permittedAccounts, Account caller,
             ListProjectResourcesCriteria listProjectResourcesCriteria, 
Map<String, String> tags,
             boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, 
Boolean showUnique, String templateType,
-            Boolean isVnf) {
+            Boolean isVnf, CPU.CPUArch arch) {
 
         // check if zone is configured, if not, just return empty list
         List<HypervisorType> hypers = null;
@@ -4523,6 +4524,10 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
             sc.addAnd("dataStoreId", SearchCriteria.Op.EQ, imageStoreId);
         }
 
+        if (arch != null) {
+            sc.addAnd("arch", SearchCriteria.Op.EQ, arch);
+        }
+
         if (storagePoolId != null) {
             sc.setJoinParameters("storagePool", "pool_id", storagePoolId);
         }
@@ -4912,7 +4917,7 @@ public class QueryManagerImpl extends 
MutualExclusiveIdsManagerBase implements Q
         return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), 
cmd.getKeyword(), isoFilter, true, cmd.isBootable(),
                 cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), 
cmd.getStoragePoolId(), cmd.getImageStoreId(),
                 hypervisorType, true, cmd.listInReadyState(), 
permittedAccounts, caller, listProjectResourcesCriteria,
-                tags, showRemovedISO, null, null, cmd.getShowUnique(), null, 
null);
+                tags, showRemovedISO, null, null, cmd.getShowUnique(), null, 
null, cmd.getArch());
     }
 
     @Override
diff --git 
a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java 
b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
index 68e7ba7cc7c..2b3be728bd3 100644
--- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import com.cloud.dc.ASNumberRangeVO;
 import com.cloud.dc.dao.ASNumberRangeDao;
 import com.cloud.network.dao.NsxProviderDao;
@@ -33,6 +34,7 @@ import 
org.apache.cloudstack.api.response.ResourceIconResponse;
 import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.stereotype.Component;
 
@@ -132,6 +134,9 @@ public class DataCenterJoinDaoImpl extends 
GenericDaoBase<DataCenterJoinVO, Long
             zoneResponse.setNsxEnabled(true);
         }
 
+        List<CPU.CPUArch> clusterArchs = 
ApiDBUtils.listZoneClustersArchs(dataCenter.getId());
+        zoneResponse.setMultiArch(CollectionUtils.isNotEmpty(clusterArchs) && 
clusterArchs.size() > 1);
+
         List<ASNumberRangeVO> asNumberRange = 
asNumberRangeDao.listByZoneId(dataCenter.getId());
         String asRange = asNumberRange.stream().map(range -> 
range.getStartASNumber() + "-" + 
range.getEndASNumber()).collect(Collectors.joining(", "));
         zoneResponse.setAsnRange(asRange);
diff --git a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java 
b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
index 97ab3b4f07f..59de277aa4b 100644
--- a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
@@ -209,6 +209,9 @@ public class HostJoinDaoImpl extends 
GenericDaoBase<HostJoinVO, Long> implements
                 hostResponse.setImplicitHostTags(host.getImplicitTag());
 
                 hostResponse.setHypervisorVersion(host.getHypervisorVersion());
+                if (host.getArch() != null) {
+                    hostResponse.setArch(host.getArch().getType());
+                }
 
                 float cpuWithOverprovisioning = host.getCpus() * 
host.getSpeed() * cpuOverprovisioningFactor;
                 hostResponse.setCpuAllocatedValue(cpu);
diff --git 
a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java 
b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
index cb0f0126ab8..0bdf5040c82 100644
--- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
@@ -332,6 +332,9 @@ public class TemplateJoinDaoImpl extends 
GenericDaoBaseWithTagInformation<Templa
         templateResponse.setDirectDownload(template.isDirectDownload());
         templateResponse.setDeployAsIs(template.isDeployAsIs());
         templateResponse.setRequiresHvm(template.isRequiresHvm());
+        if (template.getArch() != null) {
+            templateResponse.setArch(template.getArch().getType());
+        }
 
         //set template children disks
         Set<ChildTemplateResponse> childTemplatesSet = new 
HashSet<ChildTemplateResponse>();
@@ -600,6 +603,9 @@ public class TemplateJoinDaoImpl extends 
GenericDaoBaseWithTagInformation<Templa
                 
_accountService.isRootAdmin(CallContext.current().getCallingAccount().getId())));
 
         isoResponse.setDirectDownload(iso.isDirectDownload());
+        if (iso.getArch() != null) {
+            isoResponse.setArch(iso.getArch().getType());
+        }
 
         isoResponse.setObjectName("iso");
         return isoResponse;
diff --git a/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java 
b/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java
index 4c5fa20f822..72918c3fa27 100644
--- a/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java
@@ -29,6 +29,7 @@ import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 
+import com.cloud.cpu.CPU;
 import com.cloud.host.Host.Type;
 import com.cloud.host.Status;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
@@ -39,6 +40,7 @@ import org.apache.cloudstack.api.Identity;
 import org.apache.cloudstack.api.InternalIdentity;
 import org.apache.cloudstack.ha.HAConfig;
 import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 import org.apache.commons.lang3.StringUtils;
 
@@ -213,6 +215,10 @@ public class HostJoinVO extends BaseViewVO implements 
InternalIdentity, Identity
     @Column(name = "username")
     private String username;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private CPU.CPUArch arch;
+
     @Override
     public long getId() {
         return this.id;
@@ -432,4 +438,8 @@ public class HostJoinVO extends BaseViewVO implements 
InternalIdentity, Identity
                     ResourceState.Maintenance, 
ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance)
                 .contains(getResourceState());
     }
+
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
 }
diff --git a/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java 
b/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java
index babc5ac5567..cd1496f65b1 100644
--- a/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java
@@ -27,6 +27,7 @@ import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 
+import com.cloud.cpu.CPU;
 import com.cloud.user.Account;
 import 
org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 
@@ -37,6 +38,7 @@ import 
com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
 import com.cloud.template.VirtualMachineTemplate;
 import com.cloud.template.VirtualMachineTemplate.State;
 import com.cloud.utils.db.GenericDao;
+import org.apache.cloudstack.util.CPUArchConverter;
 import org.apache.cloudstack.util.HypervisorTypeConverter;
 
 @Entity
@@ -70,6 +72,10 @@ public class TemplateJoinVO extends 
BaseViewWithTagInformationVO implements Cont
     @Column(name = "hvm")
     private boolean requiresHvm;
 
+    @Column(name = "arch")
+    @Convert(converter = CPUArchConverter.class)
+    private CPU.CPUArch arch;
+
     @Column(name = "bits")
     private int bits;
 
@@ -543,4 +549,8 @@ public class TemplateJoinVO extends 
BaseViewWithTagInformationVO implements Cont
     public String getUserDataParams() {
         return userDataParams;
     }
+
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
 }
diff --git 
a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java 
b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index dc302bacae7..19760e6d025 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -19,6 +19,7 @@ package com.cloud.deploy;
 import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -35,6 +36,7 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.cpu.CPU;
 import com.cloud.vm.UserVmManager;
 import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
 import com.cloud.storage.VMTemplateVO;
@@ -271,6 +273,8 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, 
Configurable {
         _affinityProcessors = affinityProcessors;
     }
 
+    private static final List<CPU.CPUArch> clusterArchTypes = 
Arrays.asList(CPU.CPUArch.amd64, CPU.CPUArch.arm64);
+
     protected void 
avoidOtherClustersForDeploymentIfMigrationDisabled(VirtualMachine vm, Host 
lastHost, ExcludeList avoids) {
         if (lastHost == null || lastHost.getClusterId() == null ||
                 
ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS.valueIn(vm.getDataCenterId()))
 {
@@ -329,6 +333,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, 
Configurable {
         logger.debug("ROOT volume [{}] {} to deploy VM [{}].", () -> 
getRootVolumeUuid(_volsDao.findByInstance(vm.getId())), () -> plan.getPoolId() 
!= null ? "is ready" : "is not ready", vm::getUuid);
 
         avoidDisabledResources(vmProfile, dc, avoids);
+        avoidDifferentArchResources(vmProfile, dc, avoids);
 
         String haVmTag = 
(String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
         String uefiFlag = 
(String)vmProfile.getParameter(VirtualMachineProfile.Param.UefiFlag);
@@ -455,6 +460,22 @@ StateListener<State, VirtualMachine.Event, 
VirtualMachine>, Configurable {
         return dest;
     }
 
+    private void avoidDifferentArchResources(VirtualMachineProfile vmProfile, 
DataCenter dc, ExcludeList avoids) {
+        VirtualMachineTemplate template = vmProfile.getTemplate();
+        for (CPU.CPUArch arch : clusterArchTypes) {
+            if (arch.equals(template.getArch())) {
+                continue;
+            }
+            List<ClusterVO> avoidClusters = 
_clusterDao.listClustersByArchAndZoneId(dc.getId(), arch);
+            if (CollectionUtils.isNotEmpty(avoidClusters)) {
+                logger.debug("Excluding {} clusters as they are {} arch, 
conflicting with the requested arch {}",
+                        avoidClusters.size(), arch.getType(), 
template.getArch().getType());
+                List<Long> clusterIds = avoidClusters.stream().map(x -> 
x.getId()).collect(Collectors.toList());
+                avoids.addClusterList(clusterIds);
+            }
+        }
+    }
+
     private DeployDestination deployInVmLastHost(VirtualMachineProfile 
vmProfile, DeploymentPlan plan, ExcludeList avoids,
             DeploymentPlanner planner, VirtualMachine vm, DataCenter dc, 
ServiceOffering offering, int cpuRequested, long ramRequested,
             boolean volumesRequireEncryption) throws 
InsufficientServerCapacityException {
diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java 
b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 4fe519cfd04..22837389620 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -38,6 +38,7 @@ import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
 import com.cloud.alert.AlertManager;
+import com.cloud.cpu.CPU;
 import com.cloud.exception.StorageConflictException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.host.HostTagVO;
@@ -426,6 +427,7 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
         String url = cmd.getUrl();
         final String username = cmd.getUsername();
         final String password = cmd.getPassword();
+        CPU.CPUArch arch = cmd.getArch();
 
         if (url != null) {
             url = URLDecoder.decode(url);
@@ -525,6 +527,7 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
 
         cluster.setClusterType(clusterType);
         cluster.setAllocationState(allocationState);
+        cluster.setArch(arch.getType());
         try {
             cluster = _clusterDao.persist(cluster);
         } catch (final Exception e) {
@@ -1141,6 +1144,7 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
         String allocationState = cmd.getAllocationState();
         String managedstate = cmd.getManagedstate();
         String name = cmd.getClusterName();
+        CPU.CPUArch arch = cmd.getArch();
 
         // Verify cluster information and update the cluster if needed
         boolean doUpdate = false;
@@ -1213,6 +1217,11 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
             }
         }
 
+        if (arch != null) {
+            cluster.setArch(arch.getType());
+            doUpdate = true;
+        }
+
         if (doUpdate) {
             _clusterDao.update(cluster.getId(), cluster);
         }
@@ -2353,6 +2362,7 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
         host.setLastPinged(System.currentTimeMillis() >> 10);
         host.setHostTags(hostTags, false);
         host.setDetails(details);
+        host.setArch(CPU.CPUArch.fromType(startup.getArch()));
         if (startup.getStorageIpAddressDeux() != null) {
             host.setStorageIpAddressDeux(startup.getStorageIpAddressDeux());
             host.setStorageMacAddressDeux(startup.getStorageMacAddressDeux());
@@ -2746,6 +2756,13 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
             throw new IllegalArgumentException("Can't add host whose 
hypervisor type is: " + hyType + " into cluster: " + clusterVO.getId() +
                     " whose hypervisor type is: " + 
clusterVO.getHypervisorType());
         }
+        CPU.CPUArch hostCpuArch = CPU.CPUArch.fromType(ssCmd.getCpuArch());
+        if (hostCpuArch != null && clusterVO.getArch() != null && hostCpuArch 
!= clusterVO.getArch()) {
+            String msg = String.format("Can't add a host whose arch is: %s 
into cluster of arch type: %s",
+                    hostCpuArch.getType(), clusterVO.getArch().getType());
+            logger.error(msg);
+            throw new IllegalArgumentException(msg);
+        }
 
         final Map<String, String> hostDetails = ssCmd.getHostDetails();
         if (hostDetails != null) {
@@ -2764,6 +2781,7 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
         host.setCaps(ssCmd.getCapabilities());
         host.setCpuSockets(ssCmd.getCpuSockets());
         host.setCpus(ssCmd.getCpus());
+        host.setArch(hostCpuArch);
         host.setTotalMemory(ssCmd.getMemory());
         host.setSpeed(ssCmd.getSpeed());
         host.setHypervisorType(hyType);
diff --git a/server/src/main/java/com/cloud/storage/TemplateProfile.java 
b/server/src/main/java/com/cloud/storage/TemplateProfile.java
index b90409480bc..49fc6836d73 100644
--- a/server/src/main/java/com/cloud/storage/TemplateProfile.java
+++ b/server/src/main/java/com/cloud/storage/TemplateProfile.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Storage.TemplateType;
@@ -28,6 +29,7 @@ public class TemplateProfile {
     Long userId;
     String name;
     String displayText;
+    CPU.CPUArch arch;
     Integer bits;
     Boolean passwordEnabled;
     Boolean sshKeyEnbaled;
@@ -55,13 +57,14 @@ public class TemplateProfile {
     Boolean deployAsIs;
     Long size;
 
-    public TemplateProfile(Long templateId, Long userId, String name, String 
displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHvm, String 
url,
+    public TemplateProfile(Long templateId, Long userId, String name, String 
displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean 
requiresHvm, String url,
                            Boolean isPublic, Boolean featured, Boolean 
isExtractable, ImageFormat format, Long guestOsId, List<Long> zoneIdList, 
HypervisorType hypervisorType,
                            String accountName, Long domainId, Long accountId, 
String chksum, Boolean bootable, Map details, Boolean sshKeyEnabled) {
         this.templateId = templateId;
         this.userId = userId;
         this.name = name;
         this.displayText = displayText;
+        this.arch = arch;
         this.bits = bits;
         this.passwordEnabled = passwordEnabled;
         this.requiresHvm = requiresHvm;
@@ -92,15 +95,16 @@ public class TemplateProfile {
         else this.zoneIdList = null;
     }
 
-    public TemplateProfile(Long templateId, Long userId, String name, String 
displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHvm, String 
url,
-            Boolean isPublic, Boolean featured, Boolean isExtractable, 
ImageFormat format, Long guestOsId, List<Long> zoneId,
+    public TemplateProfile(Long templateId, Long userId, String name, String 
displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean 
requiresHvm, String url,
+                           Boolean isPublic, Boolean featured, Boolean 
isExtractable, ImageFormat format, Long guestOsId, List<Long> zoneId,
 
-            HypervisorType hypervisorType, String accountName, Long domainId, 
Long accountId, String chksum, Boolean bootable, String templateTag, Map 
details,
-            Boolean sshKeyEnabled, Long imageStoreId, Boolean 
isDynamicallyScalable, TemplateType templateType, Boolean directDownload, 
Boolean deployAsIs) {
+                           HypervisorType hypervisorType, String accountName, 
Long domainId, Long accountId, String chksum, Boolean bootable, String 
templateTag, Map details,
+                           Boolean sshKeyEnabled, Long imageStoreId, Boolean 
isDynamicallyScalable, TemplateType templateType, Boolean directDownload, 
Boolean deployAsIs) {
         this(templateId,
             userId,
             name,
             displayText,
+            arch,
             bits,
             passwordEnabled,
             requiresHvm,
@@ -337,4 +341,8 @@ public class TemplateProfile {
     public boolean isDeployAsIs() {
         return this.deployAsIs;
     }
+
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
 }
diff --git 
a/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java
 
b/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java
index 31206ca31b3..d11edce14c2 100644
--- 
a/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java
+++ 
b/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java
@@ -16,13 +16,14 @@
 // under the License.
 package com.cloud.storage.upload.params;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor;
 
 import java.util.Map;
 
 public class TemplateUploadParams extends UploadParamsBase {
 
-    public TemplateUploadParams(long userId, String name, String displayText,
+    public TemplateUploadParams(long userId, String name, String displayText, 
CPU.CPUArch arch,
                                 Integer bits, Boolean passwordEnabled, Boolean 
requiresHVM,
                                 Boolean isPublic, Boolean featured,
                                 Boolean isExtractable, String format, Long 
guestOSId,
@@ -30,7 +31,7 @@ public class TemplateUploadParams extends UploadParamsBase {
                                 String templateTag, long templateOwnerId,
                                 Map details, Boolean sshkeyEnabled,
                                 Boolean isDynamicallyScalable, Boolean 
isRoutingType, boolean deployAsIs) {
-        super(userId, name, displayText, bits, passwordEnabled, requiresHVM, 
isPublic, featured, isExtractable,
+        super(userId, name, displayText, arch, bits, passwordEnabled, 
requiresHVM, isPublic, featured, isExtractable,
                 format, guestOSId, zoneId, hypervisorType, chksum, 
templateTag, templateOwnerId, details,
                 sshkeyEnabled, isDynamicallyScalable, isRoutingType, 
deployAsIs);
         setBootable(true);
diff --git 
a/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java 
b/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java
index be8319c9e57..7be526d780d 100644
--- a/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java
+++ b/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.storage.upload.params;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor;
 
 import java.util.Map;
@@ -47,4 +48,5 @@ public interface UploadParams {
     boolean isRoutingType();
     boolean isDirectDownload();
     boolean isDeployAsIs();
+    CPU.CPUArch getArch();
 }
diff --git 
a/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java 
b/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java
index 60ccc27a48f..3bf3e77fe1d 100644
--- a/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java
+++ b/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.storage.upload.params;
 
+import com.cloud.cpu.CPU;
 import com.cloud.hypervisor.Hypervisor;
 
 import java.util.Map;
@@ -45,8 +46,9 @@ public abstract class UploadParamsBase implements 
UploadParams {
     private boolean isDynamicallyScalable;
     private boolean isRoutingType;
     private boolean deployAsIs;
+    private CPU.CPUArch arch;
 
-    UploadParamsBase(long userId, String name, String displayText,
+    UploadParamsBase(long userId, String name, String displayText, CPU.CPUArch 
arch,
                                Integer bits, boolean passwordEnabled, boolean 
requiresHVM,
                                boolean isPublic, boolean featured,
                                boolean isExtractable, String format, Long 
guestOSId,
@@ -57,6 +59,7 @@ public abstract class UploadParamsBase implements 
UploadParams {
         this.userId = userId;
         this.name = name;
         this.displayText = displayText;
+        this.arch = arch;
         this.bits = bits;
         this.passwordEnabled = passwordEnabled;
         this.requiresHVM = requiresHVM;
@@ -244,4 +247,13 @@ public abstract class UploadParamsBase implements 
UploadParams {
     void setHypervisorType(Hypervisor.HypervisorType hypervisorType) {
         this.hypervisorType = hypervisorType;
     }
+
+    @Override
+    public CPU.CPUArch getArch() {
+        return arch;
+    }
+
+    public void setArch(CPU.CPUArch arch) {
+        this.arch = arch;
+    }
 }
diff --git a/server/src/main/java/com/cloud/template/TemplateAdapter.java 
b/server/src/main/java/com/cloud/template/TemplateAdapter.java
index 86dd0d3cad5..27ff563655d 100644
--- a/server/src/main/java/com/cloud/template/TemplateAdapter.java
+++ b/server/src/main/java/com/cloud/template/TemplateAdapter.java
@@ -19,6 +19,7 @@ package com.cloud.template;
 import java.util.List;
 import java.util.Map;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
 import org.apache.cloudstack.api.command.user.iso.GetUploadParamsForIsoCmd;
 import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
@@ -72,11 +73,11 @@ public interface TemplateAdapter extends Adapter {
 
     boolean delete(TemplateProfile profile);
 
-    TemplateProfile prepare(boolean isIso, Long userId, String name, String 
displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String 
url, Boolean isPublic,
+    TemplateProfile prepare(boolean isIso, Long userId, String name, String 
displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean 
requiresHVM, String url, Boolean isPublic,
                             Boolean featured, Boolean isExtractable, String 
format, Long guestOSId, List<Long> zoneId, HypervisorType hypervisorType, 
String accountName, Long domainId, String chksum, Boolean bootable, Map 
details, boolean directDownload,
                             boolean deployAsIs) throws 
ResourceAllocationException;
 
-    TemplateProfile prepare(boolean isIso, long userId, String name, String 
displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String 
url, Boolean isPublic,
+    TemplateProfile prepare(boolean isIso, long userId, String name, String 
displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean 
requiresHVM, String url, Boolean isPublic,
                             Boolean featured, Boolean isExtractable, String 
format, Long guestOSId, List<Long> zoneId, HypervisorType hypervisorType, 
String chksum, Boolean bootable, String templateTag, Account templateOwner, Map 
details, Boolean sshKeyEnabled, String imageStoreUuid, Boolean 
isDynamicallyScalable,
                             TemplateType templateType, boolean directDownload, 
boolean deployAsIs) throws ResourceAllocationException;
 
diff --git a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java 
b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java
index d663a9ae0b7..119589dcc65 100644
--- a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java
+++ b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java
@@ -24,6 +24,7 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import com.cloud.cpu.CPU;
 import com.cloud.deployasis.DeployAsIsConstants;
 import com.cloud.storage.upload.params.IsoUploadParams;
 import com.cloud.storage.upload.params.TemplateUploadParams;
@@ -130,18 +131,18 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
     }
 
     @Override
-    public TemplateProfile prepare(boolean isIso, Long userId, String name, 
String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, 
String url,
-        Boolean isPublic, Boolean featured, Boolean isExtractable, String 
format, Long guestOSId, List<Long> zoneId, HypervisorType hypervisorType, 
String accountName,
-        Long domainId, String chksum, Boolean bootable, Map details, boolean 
directDownload, boolean deployAsIs) throws ResourceAllocationException {
-        return prepare(isIso, userId, name, displayText, bits, 
passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, format, 
guestOSId, zoneId,
+    public TemplateProfile prepare(boolean isIso, Long userId, String name, 
String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, 
Boolean requiresHVM, String url,
+                                   Boolean isPublic, Boolean featured, Boolean 
isExtractable, String format, Long guestOSId, List<Long> zoneId, HypervisorType 
hypervisorType, String accountName,
+                                   Long domainId, String chksum, Boolean 
bootable, Map details, boolean directDownload, boolean deployAsIs) throws 
ResourceAllocationException {
+        return prepare(isIso, userId, name, displayText, arch, bits, 
passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, format, 
guestOSId, zoneId,
             hypervisorType, chksum, bootable, null, null, details, false, 
null, false, TemplateType.USER, directDownload, deployAsIs);
     }
 
     @Override
-    public TemplateProfile prepare(boolean isIso, long userId, String name, 
String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, 
String url,
-        Boolean isPublic, Boolean featured, Boolean isExtractable, String 
format, Long guestOSId, List<Long> zoneIdList, HypervisorType hypervisorType, 
String chksum,
-        Boolean bootable, String templateTag, Account templateOwner, Map 
details, Boolean sshkeyEnabled, String imageStoreUuid, Boolean 
isDynamicallyScalable,
-        TemplateType templateType, boolean directDownload, boolean deployAsIs) 
throws ResourceAllocationException {
+    public TemplateProfile prepare(boolean isIso, long userId, String name, 
String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, 
Boolean requiresHVM, String url,
+                                   Boolean isPublic, Boolean featured, Boolean 
isExtractable, String format, Long guestOSId, List<Long> zoneIdList, 
HypervisorType hypervisorType, String chksum,
+                                   Boolean bootable, String templateTag, 
Account templateOwner, Map details, Boolean sshkeyEnabled, String 
imageStoreUuid, Boolean isDynamicallyScalable,
+                                   TemplateType templateType, boolean 
directDownload, boolean deployAsIs) throws ResourceAllocationException {
         //Long accountId = null;
         // parameters verification
 
@@ -262,7 +263,7 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
 
         Long id = _tmpltDao.getNextInSequence(Long.class, "id");
         CallContext.current().setEventDetails("Id: " + id + " name: " + name);
-        return new TemplateProfile(id, userId, name, displayText, bits, 
passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, imgfmt, 
guestOSId, zoneIdList,
+        return new TemplateProfile(id, userId, name, displayText, arch, bits, 
passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, imgfmt, 
guestOSId, zoneIdList,
             hypervisorType, templateOwner.getAccountName(), 
templateOwner.getDomainId(), templateOwner.getAccountId(), chksum, bootable, 
templateTag, details,
             sshkeyEnabled, null, isDynamicallyScalable, templateType, 
directDownload, deployAsIs);
 
@@ -306,7 +307,7 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
                 }
             }
         }
-        return prepare(false, CallContext.current().getCallingUserId(), 
cmd.getTemplateName(), cmd.getDisplayText(), cmd.getBits(), 
cmd.isPasswordEnabled(), cmd.getRequiresHvm(),
+        return prepare(false, CallContext.current().getCallingUserId(), 
cmd.getTemplateName(), cmd.getDisplayText(), cmd.getArch(), cmd.getBits(), 
cmd.isPasswordEnabled(), cmd.getRequiresHvm(),
                 cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), 
cmd.isExtractable(), cmd.getFormat(), cmd.getOsTypeId(), zoneId, 
hypervisorType, cmd.getChecksum(), true,
                 cmd.getTemplateTag(), owner, details, cmd.isSshKeyEnabled(), 
null, cmd.isDynamicallyScalable(), templateType,
                 cmd.isDirectDownload(), cmd.isDeployAsIs());
@@ -337,7 +338,7 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
                     
StringUtils.join(Arrays.stream(HypervisorType.values()).filter(h -> h != 
HypervisorType.None).map(HypervisorType::name).toArray(), ", ")));
         }
 
-        return prepare(params.isIso(), params.getUserId(), params.getName(), 
params.getDisplayText(), params.getBits(),
+        return prepare(params.isIso(), params.getUserId(), params.getName(), 
params.getDisplayText(), params.getArch(), params.getBits(),
                 params.isPasswordEnabled(), params.requiresHVM(), 
params.getUrl(), params.isPublic(), params.isFeatured(),
                 params.isExtractable(), params.getFormat(), 
params.getGuestOSId(), zoneList,
                 params.getHypervisorType(), params.getChecksum(), 
params.isBootable(), params.getTemplateTag(), owner,
@@ -358,7 +359,7 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
             osTypeId = getDefaultDeployAsIsGuestOsId();
         }
         UploadParams params = new 
TemplateUploadParams(CallContext.current().getCallingUserId(), cmd.getName(),
-                cmd.getDisplayText(), cmd.getBits(), 
BooleanUtils.toBoolean(cmd.isPasswordEnabled()),
+                cmd.getDisplayText(), cmd.getArch(), cmd.getBits(), 
BooleanUtils.toBoolean(cmd.isPasswordEnabled()),
                 BooleanUtils.toBoolean(cmd.getRequiresHvm()), 
BooleanUtils.toBoolean(cmd.isPublic()),
                 BooleanUtils.toBoolean(cmd.isFeatured()), 
BooleanUtils.toBoolean(cmd.isExtractable()), cmd.getFormat(), osTypeId,
                 cmd.getZoneId(), HypervisorType.getType(cmd.getHypervisor()), 
cmd.getChecksum(),
@@ -392,7 +393,7 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
             zoneList.add(zoneId);
         }
 
-        return prepare(true, CallContext.current().getCallingUserId(), 
cmd.getIsoName(), cmd.getDisplayText(), 64, cmd.isPasswordEnabled(), true, 
cmd.getUrl(), cmd.isPublic(),
+        return prepare(true, CallContext.current().getCallingUserId(), 
cmd.getIsoName(), cmd.getDisplayText(), cmd.getArch(), 64, 
cmd.isPasswordEnabled(), true, cmd.getUrl(), cmd.isPublic(),
             cmd.isFeatured(), cmd.isExtractable(), ImageFormat.ISO.toString(), 
cmd.getOsTypeId(), zoneList, HypervisorType.None, cmd.getChecksum(), 
cmd.isBootable(), null,
             owner, null, false, cmd.getImageStoreUuid(), 
cmd.isDynamicallyScalable(), TemplateType.USER, cmd.isDirectDownload(), false);
     }
@@ -403,7 +404,7 @@ public abstract class TemplateAdapterBase extends 
AdapterBase implements Templat
             new VMTemplateVO(profile.getTemplateId(), profile.getName(), 
profile.getFormat(), profile.isPublic(), profile.isFeatured(), 
profile.isExtractable(),
                 profile.getTemplateType(), profile.getUrl(), 
profile.isRequiresHVM(), profile.getBits(), profile.getAccountId(), 
profile.getCheckSum(),
                 profile.getDisplayText(), profile.isPasswordEnabled(), 
profile.getGuestOsId(), profile.isBootable(), profile.getHypervisorType(),
-                profile.getTemplateTag(), profile.getDetails(), 
profile.isSshKeyEnabled(), profile.IsDynamicallyScalable(), 
profile.isDirectDownload(), profile.isDeployAsIs());
+                profile.getTemplateTag(), profile.getDetails(), 
profile.isSshKeyEnabled(), profile.IsDynamicallyScalable(), 
profile.isDirectDownload(), profile.isDeployAsIs(), profile.getArch());
         template.setState(initialState);
 
         if (profile.isDirectDownload()) {
diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java 
b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
index 8752abbca34..f58e5bf9aab 100755
--- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
@@ -34,6 +34,7 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.cpu.CPU;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.BaseCmd;
@@ -1918,9 +1919,13 @@ public class TemplateManagerImpl extends ManagerBase 
implements TemplateManager,
         String description = cmd.getDisplayText();
         boolean isExtractable = false;
         Long sourceTemplateId = null;
+        CPU.CPUArch arch = CPU.CPUArch.amd64;
         if (volume != null) {
             VMTemplateVO template = 
ApiDBUtils.findTemplateById(volume.getTemplateId());
             isExtractable = template != null && template.isExtractable() && 
template.getTemplateType() != Storage.TemplateType.SYSTEM;
+            if (template != null) {
+                arch = template.getArch();
+            }
             if (volume.getIsoId() != null && volume.getIsoId() != 0) {
                 sourceTemplateId = volume.getIsoId();
             } else if (volume.getTemplateId() != null) {
@@ -1935,7 +1940,7 @@ public class TemplateManagerImpl extends ManagerBase 
implements TemplateManager,
         }
         privateTemplate = new VMTemplateVO(nextTemplateId, name, 
ImageFormat.RAW, isPublic, featured, isExtractable,
                 TemplateType.USER, null, requiresHvmValue, bitsValue, 
templateOwner.getId(), null, description,
-                passwordEnabledValue, guestOS.getId(), true, hyperType, 
templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, 
false, false);
+                passwordEnabledValue, guestOS.getId(), true, hyperType, 
templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, 
false, false, arch);
 
         if (sourceTemplateId != null) {
             if (logger.isDebugEnabled()) {
@@ -2113,6 +2118,7 @@ public class TemplateManagerImpl extends ManagerBase 
implements TemplateManager,
         Map details = cmd.getDetails();
         Account account = CallContext.current().getCallingAccount();
         boolean cleanupDetails = cmd.isCleanupDetails();
+        CPU.CPUArch arch = cmd.getCPUArch();
 
         // verify that template exists
         VMTemplateVO template = _tmpltDao.findById(id);
@@ -2161,6 +2167,7 @@ public class TemplateManagerImpl extends ManagerBase 
implements TemplateManager,
                   isRoutingTemplate == null &&
                   templateType == null &&
                   templateTag == null &&
+                  arch == null &&
                   (! cleanupDetails && details == null) //update details in 
every case except this one
                   );
         if (!updateNeeded) {
@@ -2235,6 +2242,10 @@ public class TemplateManagerImpl extends ManagerBase 
implements TemplateManager,
             template.setDynamicallyScalable(isDynamicallyScalable);
         }
 
+        if (arch != null) {
+            template.setArch(arch);
+        }
+
         if (isRoutingTemplate != null) {
             if (isRoutingTemplate) {
                 template.setTemplateType(TemplateType.ROUTING);
diff --git 
a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java 
b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
index 3afd3dc4a95..482d17908f4 100644
--- 
a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
+++ 
b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
@@ -21,6 +21,7 @@ import com.cloud.agent.AgentManager;
 import com.cloud.capacity.CapacityManager;
 import com.cloud.capacity.dao.CapacityDao;
 import com.cloud.configuration.ConfigurationManagerImpl;
+import com.cloud.cpu.CPU;
 import com.cloud.dc.ClusterDetailsDao;
 import com.cloud.dc.ClusterDetailsVO;
 import com.cloud.dc.ClusterVO;
@@ -295,6 +296,10 @@ public class DeploymentPlanningManagerImplTest {
         DataCenterDeployment plan = new DataCenterDeployment(dataCenterId);
 
         Mockito.when(avoids.shouldAvoid((DataCenterVO) 
ArgumentMatchers.any())).thenReturn(true);
+        VirtualMachineTemplate template = 
Mockito.mock(VirtualMachineTemplate.class);
+        Mockito.when(template.getArch()).thenReturn(CPU.CPUArch.amd64);
+        Mockito.when(vmProfile.getTemplate()).thenReturn(template);
+        Mockito.when(_clusterDao.listClustersByArchAndZoneId(dataCenterId, 
CPU.CPUArch.arm64)).thenReturn(null);
         DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, 
null);
         assertNull("DataCenter is in avoid set, destination should be null! ", 
dest);
     }
@@ -310,6 +315,10 @@ public class DeploymentPlanningManagerImplTest {
         Mockito.when(avoids.shouldAvoid((DataCenterVO) 
ArgumentMatchers.any())).thenReturn(false);
 
         Mockito.when(_planner.canHandle(vmProfile, plan, 
avoids)).thenReturn(false);
+        VirtualMachineTemplate template = 
Mockito.mock(VirtualMachineTemplate.class);
+        Mockito.when(template.getArch()).thenReturn(CPU.CPUArch.amd64);
+        Mockito.when(vmProfile.getTemplate()).thenReturn(template);
+        Mockito.when(_clusterDao.listClustersByArchAndZoneId(dataCenterId, 
CPU.CPUArch.arm64)).thenReturn(null);
         DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, 
null);
         assertNull("Planner cannot handle, destination should be null! ", 
dest);
     }
@@ -326,6 +335,10 @@ public class DeploymentPlanningManagerImplTest {
         Mockito.when(_planner.canHandle(vmProfile, plan, 
avoids)).thenReturn(true);
 
         Mockito.when(((DeploymentClusterPlanner) 
_planner).orderClusters(vmProfile, plan, avoids)).thenReturn(null);
+        VirtualMachineTemplate template = 
Mockito.mock(VirtualMachineTemplate.class);
+        Mockito.when(template.getArch()).thenReturn(CPU.CPUArch.amd64);
+        Mockito.when(vmProfile.getTemplate()).thenReturn(template);
+        Mockito.when(_clusterDao.listClustersByArchAndZoneId(dataCenterId, 
CPU.CPUArch.arm64)).thenReturn(null);
         DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, 
null);
         assertNull("Planner cannot handle, destination should be null! ", 
dest);
     }
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index ca38fd24fc9..b011ddaf6eb 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -381,6 +381,7 @@
 "label.apply.tungsten.firewall.policy": "Apply Firewall Policy",
 "label.apply.tungsten.network.policy": "Apply Network Policy",
 "label.apply.tungsten.tag": "Apply tag",
+"label.arch": "Arch",
 "label.archive": "Archive",
 "label.archived": "Archived",
 "label.archive.alerts": "Archive alerts",
@@ -3220,6 +3221,7 @@
 "message.ip.address": "IP address : ",
 "message.ip.address.changes.effect.after.vm.restart": "IP address changes 
takes effect only after Instance restart.",
 "message.ip.v6.prefix.delete": "IPv6 prefix deleted",
+"message.iso.arch": "Please select an ISO architecture",
 "message.iso.desc": "Disc image containing data or bootable media for OS.",
 "message.kubeconfig.cluster.not.available": "Kubernetes cluster kubeconfig not 
available currently.",
 "message.kubernetes.cluster.delete": "Please confirm that you want to destroy 
the cluster.",
@@ -3551,6 +3553,7 @@
 "message.suspend.project": "Are you sure you want to suspend this project?",
 "message.sussess.discovering.feature": "Discovered all available features!",
 "message.switch.to": "Switched to",
+"message.template.arch": "Please select a Template architecture.",
 "message.template.desc": "OS image that can be used to boot Instances.",
 "message.template.import.vm.temporary": "If a temporary Template is used, the 
reset Instance operation will not work after importing it.",
 "message.template.iso": "Please select a Template or ISO to continue.",
diff --git a/ui/src/components/view/InfoCard.vue 
b/ui/src/components/view/InfoCard.vue
index 24b7cce0feb..06775d8efaf 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -188,6 +188,9 @@
             <appstore-outlined />
             <span v-if="'cpunumber' in resource && 'cpuspeed' in resource">{{ 
resource.cpunumber }} CPU x {{ parseFloat(resource.cpuspeed / 
1000.0).toFixed(2) }} Ghz</span>
             <span v-else>{{ resource.cputotal }}</span>
+            <a-tag v-if="resource.arch" style="margin-left: 10px">
+              {{ resource.arch }}
+            </a-tag>
           </div>
           <div>
             <span v-if="resource.cpuused">
diff --git a/ui/src/config/section/image.js b/ui/src/config/section/image.js
index 72cde6afe9c..5b561ef877c 100644
--- a/ui/src/config/section/image.js
+++ b/ui/src/config/section/image.js
@@ -57,7 +57,7 @@ export default {
         return fields
       },
       details: () => {
-        var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 
'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled',
+        var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 
'arch', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 
'passwordenabled',
           'crossZones', 'templatetype', 'directdownload', 'deployasis', 
'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 
'crosszones', 'type',
           'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy']
         if (['Admin'].includes(store.getters.userInfo.roletype)) {
@@ -231,7 +231,7 @@ export default {
         }
         return fields
       },
-      details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 
'bootable', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 
'ispublic', 'isfeatured', 'isdynamicallyscalable', 'crosszones', 'account', 
'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url'],
+      details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 
'arch', 'bootable', 'isready', 'passwordenabled', 'directdownload', 
'isextractable', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 
'crosszones', 'account', 'domain', 'created', 'userdatadetails', 
'userdatapolicy', 'url'],
       searchFilters: () => {
         var filters = ['name', 'zoneid', 'tags']
         if (['Admin', 
'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
diff --git a/ui/src/config/section/infra/clusters.js 
b/ui/src/config/section/infra/clusters.js
index 8b2f37d5b7b..c7edc6560ef 100644
--- a/ui/src/config/section/infra/clusters.js
+++ b/ui/src/config/section/infra/clusters.js
@@ -35,7 +35,7 @@ export default {
     fields.push('zonename')
     return fields
   },
-  details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 
'hypervisortype', 'podname', 'zonename', 'drsimbalance'],
+  details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 
'arch', 'hypervisortype', 'podname', 'zonename', 'drsimbalance'],
   related: [{
     name: 'host',
     title: 'label.hosts',
@@ -83,7 +83,12 @@ export default {
       icon: 'edit-outlined',
       label: 'label.edit',
       dataView: true,
-      args: ['clustername']
+      args: ['clustername', 'arch'],
+      mapping: {
+        arch: {
+          options: ['x86_64', 'aarch64']
+        }
+      }
     },
     {
       api: 'updateCluster',
diff --git a/ui/src/config/section/infra/hosts.js 
b/ui/src/config/section/infra/hosts.js
index 92688cf9280..727da7242d7 100644
--- a/ui/src/config/section/infra/hosts.js
+++ b/ui/src/config/section/infra/hosts.js
@@ -41,7 +41,7 @@ export default {
     fields.push('zonename')
     return fields
   },
-  details: ['name', 'id', 'resourcestate', 'ipaddress', 'hypervisor', 'type', 
'clustername', 'podname', 'zonename', 'disconnected', 'created'],
+  details: ['name', 'id', 'resourcestate', 'ipaddress', 'hypervisor', 'arch', 
'type', 'clustername', 'podname', 'zonename', 'disconnected', 'created'],
   tabs: [{
     name: 'details',
     component: shallowRef(defineAsyncComponent(() => 
import('@/components/view/DetailsTab.vue')))
diff --git a/ui/src/views/compute/DeployVM.vue 
b/ui/src/views/compute/DeployVM.vue
index 7d661aa9dc3..3c4742d64b2 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -152,6 +152,18 @@
                       @tabChange="key => onTabChange(key, 'tabKey')">
                       <div v-if="tabKey === 'templateid'">
                         {{ $t('message.template.desc') }}
+                        <div v-if="isZoneSelectedMultiArch" style="width: 
100%; margin-top: 5px">
+                          {{ $t('message.template.arch') }}
+                          <a-select
+                            style="width: 100%"
+                            v-model:value="selectedArchitecture"
+                            :defaultValue="architectureTypes.opts[0].id"
+                            @change="arch => changeArchitecture(arch, true)">
+                            <a-select-option v-for="opt in 
architectureTypes.opts" :key="opt.id">
+                              {{ opt.name || opt.description }}
+                            </a-select-option>
+                          </a-select>
+                        </div>
                         <template-iso-selection
                           input-decorator="templateid"
                           :items="options.templates"
@@ -181,6 +193,18 @@
                       </div>
                       <div v-else>
                         {{ $t('message.iso.desc') }}
+                        <div v-if="isZoneSelectedMultiArch" style="width: 
100%; margin-top: 5px">
+                          {{ $t('message.iso.arch') }}
+                          <a-select
+                            style="width: 100%"
+                            v-model:value="selectedArchitecture"
+                            :defaultValue="architectureTypes.opts[0].id"
+                            @change="arch => changeArchitecture(arch, false)">
+                            <a-select-option v-for="opt in 
architectureTypes.opts" :key="opt.id">
+                              {{ opt.name || opt.description }}
+                            </a-select-option>
+                          </a-select>
+                        </div>
                         <template-iso-selection
                           input-decorator="isoid"
                           :items="options.isos"
@@ -914,6 +938,7 @@ export default {
       podId: null,
       clusterId: null,
       zoneSelected: false,
+      isZoneSelectedMultiArch: false,
       dynamicscalingenabled: true,
       templateKey: 0,
       showRegisteredUserdata: true,
@@ -1064,7 +1089,19 @@ export default {
       zones: [],
       selectedZone: '',
       formModel: {},
-      nicToNetworkSelection: []
+      nicToNetworkSelection: [],
+      selectedArchitecture: null,
+      architectureTypes: {
+        opts: [
+          {
+            id: 'x86_64',
+            description: 'AMD 64 bits (x86_64)'
+          }, {
+            id: 'aarch64',
+            description: 'ARM 64 bits (aarch64)'
+          }
+        ]
+      }
     }
   },
   computed: {
@@ -1957,6 +1994,14 @@ export default {
     getText (option) {
       return _.get(option, 'displaytext', _.get(option, 'name'))
     },
+    changeArchitecture (arch, isTemplate) {
+      this.selectedArchitecture = arch
+      if (isTemplate) {
+        this.fetchAllTemplates()
+      } else {
+        this.fetchAllIsos()
+      }
+    },
     handleSubmitAndStay (e) {
       this.form.stayonpage = true
       this.handleSubmit(e.domEvent)
@@ -2383,6 +2428,9 @@ export default {
         args.pageSize = args.pageSize || 10
       }
       args.zoneid = _.get(this.zone, 'id')
+      if (this.isZoneSelectedMultiArch) {
+        args.arch = this.selectedArchitecture
+      }
       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
@@ -2408,6 +2456,9 @@ export default {
         args.pageSize = args.pageSize || 10
       }
       args.zoneid = _.get(this.zone, 'id')
+      if (this.isZoneSelectedMultiArch) {
+        args.arch = this.selectedArchitecture
+      }
       args.isoFilter = isoFilter
       args.bootable = true
       args.showicon = 'true'
@@ -2471,6 +2522,10 @@ export default {
       this.podId = null
       this.clusterId = null
       this.zone = _.find(this.options.zones, (option) => option.id === value)
+      this.isZoneSelectedMultiArch = this.zone.ismultiarch
+      if (this.isZoneSelectedMultiArch) {
+        this.selectedArchitecture = this.architectureTypes.opts[0].id
+      }
       this.zoneSelected = true
       this.form.startvm = true
       this.selectedZone = this.zoneId
diff --git a/ui/src/views/image/AddKubernetesSupportedVersion.vue 
b/ui/src/views/image/AddKubernetesSupportedVersion.vue
index c88fc34695d..e0cd18c8eaa 100644
--- a/ui/src/views/image/AddKubernetesSupportedVersion.vue
+++ b/ui/src/views/image/AddKubernetesSupportedVersion.vue
@@ -106,6 +106,25 @@
             v-model:checked="form.directdownload"
             :placeholder="apiParams.directdownload.description"/>
         </a-form-item>
+        <a-form-item
+          name="arch"
+          ref="arch">
+          <template #label>
+            <tooltip-label :title="$t('label.arch')" 
:tooltip="apiParams.arch.description"/>
+          </template>
+          <a-select
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }"
+            v-model:value="form.arch"
+            :placeholder="apiParams.arch.description">
+            <a-select-option v-for="opt in architectureTypes.opts" 
:key="opt.id">
+              {{ opt.name || opt.description }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
 
         <div :span="24" class="action-button">
           <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@@ -135,7 +154,8 @@ export default {
       loading: false,
       selectedZone: {},
       directDownloadDisabled: false,
-      lastNonEdgeDirectDownloadUserSelection: false
+      lastNonEdgeDirectDownloadUserSelection: false,
+      architectureTypes: {}
     }
   },
   beforeCreate () {
@@ -196,6 +216,7 @@ export default {
       })
     },
     fetchData () {
+      this.fetchArchitectureTypes()
       this.fetchZoneData()
     },
     isValidValueForKey (obj, key) {
@@ -204,6 +225,19 @@ export default {
     arrayHasItems (array) {
       return array !== null && array !== undefined && Array.isArray(array) && 
array.length > 0
     },
+    fetchArchitectureTypes () {
+      this.architectureTypes.opts = []
+      const typesList = []
+      typesList.push({
+        id: 'x86_64',
+        description: 'AMD 64 bits (x86_64)'
+      })
+      typesList.push({
+        id: 'aarch64',
+        description: 'ARM 64 bits (aarch64)'
+      })
+      this.architectureTypes.opts = typesList
+    },
     fetchZoneData () {
       const params = {}
       params.showicon = true
diff --git a/ui/src/views/image/RegisterOrUploadIso.vue 
b/ui/src/views/image/RegisterOrUploadIso.vue
index edc6b82b92d..2c45a4dfc30 100644
--- a/ui/src/views/image/RegisterOrUploadIso.vue
+++ b/ui/src/views/image/RegisterOrUploadIso.vue
@@ -183,6 +183,26 @@
           </a-select>
         </a-form-item>
 
+        <a-form-item
+          name="arch"
+          ref="arch">
+          <template #label>
+            <tooltip-label :title="$t('label.arch')" 
:tooltip="apiParams.arch.description"/>
+          </template>
+          <a-select
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }"
+            v-model:value="form.arch"
+            :placeholder="apiParams.arch.description">
+            <a-select-option v-for="opt in architectureTypes.opts" 
:key="opt.id">
+              {{ opt.name || opt.description }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
+
         <a-row :gutter="12">
           <a-col :md="24" :lg="12">
             <a-form-item
@@ -322,7 +342,8 @@ export default {
       accounts: [],
       domainLoading: false,
       domainid: null,
-      account: null
+      account: null,
+      architectureTypes: {}
     }
   },
   beforeCreate () {
@@ -363,6 +384,7 @@ export default {
     fetchData () {
       this.fetchZoneData()
       this.fetchOsType()
+      this.fetchArchitectureTypes()
       this.fetchUserData()
       this.fetchUserdataPolicy()
       if ('listDomains' in this.$store.getters.apis) {
@@ -388,6 +410,19 @@ export default {
         this.form.zoneid = (this.zones[0].id ? this.zones[0].id : '')
       })
     },
+    fetchArchitectureTypes () {
+      this.architectureTypes.opts = []
+      const typesList = []
+      typesList.push({
+        id: 'x86_64',
+        description: 'AMD 64 bits (x86_64)'
+      })
+      typesList.push({
+        id: 'aarch64',
+        description: 'ARM 64 bits (aarch64)'
+      })
+      this.architectureTypes.opts = typesList
+    },
     fetchOsType () {
       this.osTypeLoading = true
 
diff --git a/ui/src/views/image/RegisterOrUploadTemplate.vue 
b/ui/src/views/image/RegisterOrUploadTemplate.vue
index 27f60eeeb5d..9523351ffd0 100644
--- a/ui/src/views/image/RegisterOrUploadTemplate.vue
+++ b/ui/src/views/image/RegisterOrUploadTemplate.vue
@@ -343,6 +343,25 @@
             </a-select-option>
           </a-select>
         </a-form-item>
+        <a-form-item
+          name="arch"
+          ref="arch">
+          <template #label>
+            <tooltip-label :title="$t('label.arch')" 
:tooltip="apiParams.arch.description"/>
+          </template>
+          <a-select
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }"
+            v-model:value="form.arch"
+            :placeholder="apiParams.arch.description">
+            <a-select-option v-for="opt in architectureTypes.opts" 
:key="opt.id">
+              {{ opt.name || opt.description }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
         <a-form-item ref="templatetag" name="templatetag" v-if="isAdminRole">
           <template #label>
             <tooltip-label :title="$t('label.templatetag')" 
:tooltip="apiParams.templatetag.description"/>
@@ -511,7 +530,8 @@ export default {
       domainLoading: false,
       domainid: null,
       account: null,
-      customHypervisorName: 'Custom'
+      customHypervisorName: 'Custom',
+      architectureTypes: {}
     }
   },
   beforeCreate () {
@@ -563,6 +583,7 @@ export default {
       this.fetchZone()
       this.fetchOsTypes()
       this.fetchTemplateTypes()
+      this.fetchArchitectureTypes()
       this.fetchUserData()
       this.fetchUserdataPolicy()
       if ('listDomains' in this.$store.getters.apis) {
@@ -732,6 +753,19 @@ export default {
       }
       this.templateTypes.opts = templatetypes
     },
+    fetchArchitectureTypes () {
+      this.architectureTypes.opts = []
+      const typesList = []
+      typesList.push({
+        id: 'x86_64',
+        description: 'AMD 64 bits (x86_64)'
+      })
+      typesList.push({
+        id: 'aarch64',
+        description: 'ARM 64 bits (aarch64)'
+      })
+      this.architectureTypes.opts = typesList
+    },
     fetchUserData () {
       const params = {}
       params.listAll = true
diff --git a/ui/src/views/image/UpdateISO.vue b/ui/src/views/image/UpdateISO.vue
index e3594a3c1e1..bd0122e59fe 100644
--- a/ui/src/views/image/UpdateISO.vue
+++ b/ui/src/views/image/UpdateISO.vue
@@ -119,6 +119,26 @@
           </a-col>
         </a-row>
 
+        <a-form-item
+          name="arch"
+          ref="arch">
+          <template #label>
+            <tooltip-label :title="$t('label.arch')" 
:tooltip="apiParams.arch.description"/>
+          </template>
+          <a-select
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }"
+            v-model:value="form.arch"
+            :placeholder="apiParams.arch.description">
+            <a-select-option v-for="opt in architectureTypes.opts" 
:key="opt.id">
+              {{ opt.name || opt.description }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
+
         <div :span="24" class="action-button">
           <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
           <a-button :loading="loading" ref="submit" type="primary" 
@click="handleSubmit">{{ $t('label.ok') }}</a-button>
@@ -151,7 +171,8 @@ export default {
       userdata: {},
       userdataid: null,
       userdatapolicy: null,
-      userdatapolicylist: {}
+      userdatapolicylist: {},
+      architectureTypes: {}
     }
   },
   beforeCreate () {
@@ -195,6 +216,7 @@ export default {
     },
     fetchData () {
       this.fetchOsTypes()
+      this.fetchArchitectureTypes()
       this.fetchUserdata()
       this.fetchUserdataPolicy()
     },
@@ -213,6 +235,19 @@ export default {
         this.osTypes.loading = false
       })
     },
+    fetchArchitectureTypes () {
+      this.architectureTypes.opts = []
+      const typesList = []
+      typesList.push({
+        id: 'x86_64',
+        description: 'AMD 64 bits (x86_64)'
+      })
+      typesList.push({
+        id: 'aarch64',
+        description: 'ARM 64 bits (aarch64)'
+      })
+      this.architectureTypes.opts = typesList
+    },
     fetchUserdataPolicy () {
       const userdataPolicy = []
       userdataPolicy.push({
diff --git a/ui/src/views/image/UpdateTemplate.vue 
b/ui/src/views/image/UpdateTemplate.vue
index 34050509425..4c540772f89 100644
--- a/ui/src/views/image/UpdateTemplate.vue
+++ b/ui/src/views/image/UpdateTemplate.vue
@@ -186,6 +186,25 @@
             :placeholder="apiParams.templatetag.description"
             v-focus="currentForm !== 'Create'"/>
         </a-form-item>
+        <a-form-item
+          name="arch"
+          ref="arch">
+          <template #label>
+            <tooltip-label :title="$t('label.arch')" 
:tooltip="apiParams.arch.description"/>
+          </template>
+          <a-select
+            showSearch
+            optionFilterProp="label"
+            :filterOption="(input, option) => {
+              return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }"
+            v-model:value="form.arch"
+            :placeholder="apiParams.arch.description">
+            <a-select-option v-for="opt in architectureTypes.opts" 
:key="opt.id">
+              {{ opt.name || opt.description }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
 
         <div :span="24" class="action-button">
           <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
@@ -225,7 +244,8 @@ export default {
       userdata: {},
       userdataid: null,
       userdatapolicy: null,
-      userdatapolicylist: {}
+      userdatapolicylist: {},
+      architectureTypes: {}
     }
   },
   beforeCreate () {
@@ -254,7 +274,7 @@ export default {
         displaytext: [{ required: true, message: 
this.$t('message.error.required.input') }],
         ostypeid: [{ required: true, message: this.$t('message.error.select') 
}]
       })
-      const resourceFields = ['name', 'displaytext', 'passwordenabled', 
'ostypeid', 'isdynamicallyscalable', 'userdataid', 'userdatapolicy']
+      const resourceFields = ['name', 'displaytext', 'architecture', 
'passwordenabled', 'ostypeid', 'isdynamicallyscalable', 'userdataid', 
'userdatapolicy']
       if (this.isAdmin) {
         resourceFields.push('templatetype')
         resourceFields.push('templatetag')
@@ -290,6 +310,7 @@ export default {
     },
     fetchData () {
       this.fetchOsTypes()
+      this.fetchArchitectureTypes()
       this.fetchRootDiskControllerTypes(this.resource.hypervisor)
       this.fetchNicAdapterTypes()
       this.fetchKeyboardTypes()
@@ -314,6 +335,19 @@ export default {
         this.osTypes.loading = false
       })
     },
+    fetchArchitectureTypes () {
+      this.architectureTypes.opts = []
+      const typesList = []
+      typesList.push({
+        id: 'x86_64',
+        description: 'AMD 64 bits (x86_64)'
+      })
+      typesList.push({
+        id: 'aarch64',
+        description: 'ARM 64 bits (aarch64)'
+      })
+      this.architectureTypes.opts = typesList
+    },
     fetchRootDiskControllerTypes (hyperVisor) {
       const controller = []
       this.rootDisk.opts = []
diff --git a/ui/src/views/infra/ClusterAdd.vue 
b/ui/src/views/infra/ClusterAdd.vue
index 862b458b388..924722d59bb 100644
--- a/ui/src/views/infra/ClusterAdd.vue
+++ b/ui/src/views/infra/ClusterAdd.vue
@@ -62,6 +62,21 @@
         </a-select>
       </div>
 
+      <div class="form__item">
+        <div class="form__label">{{ $t('label.arch') }}</div>
+        <a-select
+          showSearch
+          optionFilterProp="label"
+          :filterOption="(input, option) => {
+            return 
option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+          }"
+          v-model:value="selectedArchitecture">
+          <a-select-option v-for="opt in architectureTypes.opts" :key="opt.id">
+            {{ opt.name || opt.description }}
+          </a-select-option>
+        </a-select>
+      </div>
+
       <div class="form__item">
         <div class="form__label">{{ $t('label.podname') }}</div>
         <a-select
@@ -174,6 +189,8 @@ export default {
       dedicatedAccount: null,
       domainError: false,
       params: [],
+      architectureTypes: {},
+      selectedArchitecture: null,
       placeholder: {
         clustername: null
       }
@@ -186,6 +203,7 @@ export default {
     fetchData () {
       this.fetchZones()
       this.fetchHypervisors()
+      this.fetchArchitectureTypes()
       this.params = this.$store.getters.apis.addCluster.params
       Object.keys(this.placeholder).forEach(item => { 
this.returnPlaceholder(item) })
     },
@@ -212,6 +230,20 @@ export default {
         this.loading = false
       })
     },
+    fetchArchitectureTypes () {
+      this.architectureTypes.opts = []
+      const typesList = []
+      typesList.push({
+        id: 'x86_64',
+        description: 'AMD 64 bits (x86_64)'
+      })
+      typesList.push({
+        id: 'aarch64',
+        description: 'ARM 64 bits (aarch64)'
+      })
+      this.architectureTypes.opts = typesList
+      this.selectedArchitecture = this.architectureTypes.opts[0].id
+    },
     fetchPods () {
       this.loading = true
       api('listPods', {
@@ -285,6 +317,7 @@ export default {
         clustertype: this.clustertype,
         podId: this.podId,
         clustername: clustername,
+        arch: this.selectedArchitecture,
         url: this.url
       }
       if (this.ovm3pool) {


Reply via email to