diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index b3c40a32662..82ff9370969 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -286,6 +286,8 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, S public Date getCreated(); public long getServiceOfferingId(); + + public long getDiskOfferingId(); Type getType(); diff --git a/core/src/com/cloud/vm/VMInstanceVO.java b/core/src/com/cloud/vm/VMInstanceVO.java index 986c4218ccf..4f9717ecd80 100644 --- a/core/src/com/cloud/vm/VMInstanceVO.java +++ b/core/src/com/cloud/vm/VMInstanceVO.java @@ -475,4 +475,10 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject computeTags, @QueryParam("root-disk-tags") List rootDiskTags, - @QueryParam("networks") List networks - ); + @QueryParam("networks") List networks, + @QueryParam("deploymentplan") DeploymentPlan plan + ) throws InsufficientCapacityException; @POST VirtualMachineEntity createVirtualMachineFromScratch( @@ -81,9 +86,12 @@ public interface OrchestrationService { @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, + @QueryParam("disk-size") Long diskSize, @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, - @QueryParam("networks") List networks); + @QueryParam("networks") List networks, + @QueryParam("deploymentplan") DeploymentPlan plan + ) throws InsufficientCapacityException; @POST NetworkEntity createNetwork(String id, String name, String domainName, String cidr, String gateway); diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml index 8b9210cc360..5e2495ebf01 100755 --- a/engine/orchestration/pom.xml +++ b/engine/orchestration/pom.xml @@ -48,6 +48,11 @@ cloud-utils ${project.version} + + org.apache.cloudstack + cloud-server + ${project.version} + org.mockito mockito-all diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java new file mode 100644 index 00000000000..06496454f94 --- /dev/null +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java @@ -0,0 +1,10 @@ +package org.apache.cloudstack.engine.cloud.entity.api; + +import org.apache.cloudstack.engine.cloud.entity.api.db.VMEntityVO; + +public interface VMEntityManager { + + VMEntityVO loadVirtualMachine(String vmId); + + void saveVirtualMachine(VMEntityVO vmInstanceVO); +} diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java new file mode 100644 index 00000000000..70530dcc557 --- /dev/null +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java @@ -0,0 +1,223 @@ +package org.apache.cloudstack.engine.cloud.entity.api; + +import java.lang.reflect.Method; +import java.util.Date; +import java.util.List; +import java.util.Map; + + + +import org.apache.cloudstack.engine.cloud.entity.api.db.VMEntityVO; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; + +public class VirtualMachineEntityImpl implements VirtualMachineEntity { + + private VMEntityManager manager; + + private VMEntityVO vmEntityVO; + + public VirtualMachineEntityImpl(String vmId, VMEntityManager manager) { + this.manager = manager; + this.vmEntityVO = this.manager.loadVirtualMachine(vmId); + } + + public VirtualMachineEntityImpl(String vmId, String owner, String hostName, String displayName, int cpu, int speed, long memory, List computeTags, List rootDiskTags, List networks, VMEntityManager manager) { + this(vmId, manager); + this.vmEntityVO.setOwner(owner); + this.vmEntityVO.setHostname(hostName); + this.vmEntityVO.setDisplayname(displayName); + this.vmEntityVO.setSpeed(speed); + this.vmEntityVO.setComputeTags(computeTags); + this.vmEntityVO.setRootDiskTags(rootDiskTags); + this.vmEntityVO.setNetworkIds(networks); + + manager.saveVirtualMachine(vmEntityVO); + } + + @Override + public String getUuid() { + return vmEntityVO.getUuid(); + } + + @Override + public long getId() { + return vmEntityVO.getId(); + } + + @Override + public String getCurrentState() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getDesiredState() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Date getCreatedTime() { + return vmEntityVO.getCreated(); + } + + @Override + public Date getLastUpdatedTime() { + return vmEntityVO.getUpdateTime(); + } + + @Override + public String getOwner() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getDetails() { + return vmEntityVO.getDetails(); + } + + @Override + public void addDetail(String name, String value) { + vmEntityVO.setDetail(name, value); + } + + @Override + public void delDetail(String name, String value) { + // TODO Auto-generated method stub + } + + @Override + public void updateDetail(String name, String value) { + // TODO Auto-generated method stub + } + + @Override + public List getApplicableActions() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listVolumeIds() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listVolumes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listNicUuids() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listNics() { + // TODO Auto-generated method stub + return null; + } + + @Override + public TemplateEntity getTemplate() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listTags() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addTag() { + // TODO Auto-generated method stub + + } + + @Override + public void delTag() { + // TODO Auto-generated method stub + + } + + @Override + public String reserve(String plannerToUse, DeployDestination dest, + ExcludeList exclude) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void migrateTo(String reservationId) { + // TODO Auto-generated method stub + + } + + @Override + public void deploy(String reservationId) { + // TODO Auto-generated method stub + + } + + @Override + public void stop() { + // TODO Auto-generated method stub + + } + + @Override + public void cleanup() { + // TODO Auto-generated method stub + + } + + @Override + public void destroy() { + // TODO Auto-generated method stub + + } + + @Override + public VirtualMachineEntity duplicate(String externalId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SnapshotEntity takeSnapshotOf() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void attach(VolumeEntity volume, short deviceId) { + // TODO Auto-generated method stub + + } + + @Override + public void detach(VolumeEntity volume) { + // TODO Auto-generated method stub + + } + + @Override + public void connectTo(NetworkEntity network, short nicId) { + // TODO Auto-generated method stub + + } + + @Override + public void disconnectFrom(NetworkEntity netowrk, short nicId) { + // TODO Auto-generated method stub + + } + +} diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java new file mode 100644 index 00000000000..cebca33e214 --- /dev/null +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java @@ -0,0 +1,560 @@ +// 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 org.apache.cloudstack.engine.cloud.entity.api.db; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.db.Encrypt; +import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.StateMachine; +import com.cloud.utils.fsm.FiniteStateObject; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; + +@Entity +@Table(name="vm_instance") +@Inheritance(strategy=InheritanceType.JOINED) +@DiscriminatorColumn(name="type", discriminatorType=DiscriminatorType.STRING, length=32) +public class VMEntityVO implements VirtualMachine, FiniteStateObject { + @Id + @TableGenerator(name="vm_instance_sq", table="sequence", pkColumnName="name", valueColumnName="value", pkColumnValue="vm_instance_seq", allocationSize=1) + @Column(name="id", updatable=false, nullable = false) + protected long id; + + @Column(name="name", updatable=false, nullable=false, length=255) + protected String hostName = null; + + @Encrypt + @Column(name="vnc_password", updatable=true, nullable=false, length=255) + protected String vncPassword; + + @Column(name="proxy_id", updatable=true, nullable=true) + protected Long proxyId; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="proxy_assign_time", updatable=true, nullable=true) + protected Date proxyAssignTime; + + /** + * Note that state is intentionally missing the setter. Any updates to + * the state machine needs to go through the DAO object because someone + * else could be updating it as well. + */ + @Enumerated(value=EnumType.STRING) + @StateMachine(state=State.class, event=Event.class) + @Column(name="state", updatable=true, nullable=false, length=32) + protected State state = null; + + @Column(name="private_ip_address", updatable=true) + protected String privateIpAddress; + + @Column(name="instance_name", updatable=true, nullable=false) + protected String instanceName; + + @Column(name="vm_template_id", updatable=true, nullable=true, length=17) + protected Long templateId = new Long(-1); + + @Column(name="guest_os_id", nullable=false, length=17) + protected long guestOSId; + + @Column(name="host_id", updatable=true, nullable=true) + protected Long hostId; + + @Column(name="last_host_id", updatable=true, nullable=true) + protected Long lastHostId; + + @Column(name="pod_id", updatable=true, nullable=false) + protected Long podIdToDeployIn; + + @Column(name="private_mac_address", updatable=true, nullable=true) + protected String privateMacAddress; + + @Column(name="data_center_id", updatable=true, nullable=false) + protected long dataCenterIdToDeployIn; + + @Column(name="vm_type", updatable=false, nullable=false, length=32) + @Enumerated(value=EnumType.STRING) + protected Type type; + + @Column(name="ha_enabled", updatable=true, nullable=true) + protected boolean haEnabled; + + @Column(name="limit_cpu_use", updatable=true, nullable=true) + private boolean limitCpuUse; + + @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. + + @Column(name=GenericDao.CREATED_COLUMN) + protected Date created; + + @Column(name=GenericDao.REMOVED_COLUMN) + protected Date removed; + + @Column(name="update_time", updatable=true) + @Temporal(value=TemporalType.TIMESTAMP) + protected Date updateTime; + + @Column(name="domain_id") + protected long domainId; + + @Column(name="account_id") + protected long accountId; + + @Column(name="service_offering_id") + protected long serviceOfferingId; + + @Column(name="reservation_id") + protected String reservationId; + + @Column(name="hypervisor_type") + @Enumerated(value=EnumType.STRING) + protected HypervisorType hypervisorType; + + @Column(name="ram") + protected long ram; + + @Column(name="cpu") + protected int cpu; + + @Column(name="tags") + protected String tags; + + @Transient + Map details; + + @Column(name="uuid") + protected String uuid = UUID.randomUUID().toString(); + + //orchestration columns + @Column(name="owner") + private String owner = null; + + @Column(name="speed") + private int speed; + + @Transient + List computeTags; + + @Transient + List rootDiskTags; + + @Column(name="hostname") + private String hostname = null; + + @Column(name="displayname") + private String displayname = null; + + @Transient + List networkIds; + + + public VMEntityVO(long id, + long serviceOfferingId, + String name, + String instanceName, + Type type, + Long vmTemplateId, + HypervisorType hypervisorType, + long guestOSId, + long domainId, + long accountId, + boolean haEnabled) { + this.id = id; + this.hostName = name != null ? name : this.uuid; + if (vmTemplateId != null) { + this.templateId = vmTemplateId; + } + this.instanceName = instanceName; + this.type = type; + this.guestOSId = guestOSId; + this.haEnabled = haEnabled; + this.vncPassword = Long.toHexString(new Random().nextLong()); + this.state = State.Stopped; + this.accountId = accountId; + this.domainId = domainId; + this.serviceOfferingId = serviceOfferingId; + this.hypervisorType = hypervisorType; + this.limitCpuUse = false; + } + + public VMEntityVO(long id, + long serviceOfferingId, + String name, + String instanceName, + Type type, + Long vmTemplateId, + HypervisorType hypervisorType, + long guestOSId, + long domainId, + long accountId, + boolean haEnabled, + boolean limitResourceUse) { + this(id, serviceOfferingId, name, instanceName, type, vmTemplateId, hypervisorType, guestOSId, domainId, accountId, haEnabled); + this.limitCpuUse = limitResourceUse; + } + + + + protected VMEntityVO() { + } + + public Date getRemoved() { + return removed; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public Type getType() { + return type; + } + + public long getUpdated() { + return updated; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public HypervisorType getHypervisorType() { + return hypervisorType; + } + + @Override + public Date getCreated() { + return created; + } + + public Date getUpdateTime() { + return updateTime; + } + + @Override + public long getDataCenterIdToDeployIn() { + return dataCenterIdToDeployIn; + } + + @Override + public String getHostName() { + return hostName; + } + + @Override + public String getInstanceName() { + return instanceName; + } + + @Override + public State getState() { + return state; + } + + // don't use this directly, use VM state machine instead, this method is added for migration tool only + @Override + public void setState(State state) { + this.state = state; + } + + @Override + public String getPrivateIpAddress() { + return privateIpAddress; + } + + public void setPrivateIpAddress(String address) { + privateIpAddress = address; + } + + public void setVncPassword(String vncPassword) { + this.vncPassword = vncPassword; + } + + @Override + public String getVncPassword() { + return vncPassword; + } + + @Override + public long getServiceOfferingId() { + return serviceOfferingId; + } + + public Long getProxyId() { + return proxyId; + } + + public void setProxyId(Long proxyId) { + this.proxyId = proxyId; + } + + public Date getProxyAssignTime() { + return this.proxyAssignTime; + } + + public void setProxyAssignTime(Date time) { + this.proxyAssignTime = time; + } + + @Override + public long getTemplateId() { + if (templateId == null) { + return -1; + } else { + return templateId; + } + } + + public void setTemplateId(Long templateId) { + this.templateId = templateId; + } + + @Override + public long getGuestOSId() { + return guestOSId; + } + + public void setGuestOSId(long guestOSId) { + this.guestOSId = guestOSId; + } + + public void incrUpdated() { + updated++; + } + + public void decrUpdated() { + updated--; + } + + @Override + public Long getHostId() { + return hostId; + } + + @Override + public Long getLastHostId() { + return lastHostId; + } + + public void setLastHostId(Long lastHostId) { + this.lastHostId = lastHostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + @Override + public boolean isHaEnabled() { + return haEnabled; + } + + @Override + public boolean limitCpuUse() { + return limitCpuUse; + } + + public void setLimitCpuUse(boolean value) { + limitCpuUse = value; + } + + @Override + public String getPrivateMacAddress() { + return privateMacAddress; + } + + @Override + public Long getPodIdToDeployIn() { + return podIdToDeployIn; + } + + public void setPodId(long podId) { + this.podIdToDeployIn = podId; + } + + public void setPrivateMacAddress(String privateMacAddress) { + this.privateMacAddress = privateMacAddress; + } + + public void setDataCenterId(long dataCenterId) { + this.dataCenterIdToDeployIn = dataCenterId; + } + + public boolean isRemoved() { + return removed != null; + } + + public void setHaEnabled(boolean value) { + haEnabled = value; + } + + public void setReservationId(String reservationId) { + this.reservationId = reservationId; + } + + public String getReservationId() { + return this.reservationId; + } + + @Override + public Map getDetails() { + return details; + } + + public void setDetail(String name, String value) { + assert (details != null) : "Did you forget to load the details?"; + + details.put(name, value); + } + + public void setDetails(Map details) { + this.details = details; + } + + transient String toString; + @Override + public String toString() { + if (toString == null) { + toString = new StringBuilder("VM[").append(type.toString()).append("|").append(hostName).append("]").toString(); + } + return toString; + } + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VMEntityVO other = (VMEntityVO) obj; + if (id != other.id) + return false; + return true; + } + + + public void setServiceOfferingId(long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public int getSpeed() { + return speed; + } + + public void setSpeed(int speed) { + this.speed = speed; + } + + public List getComputeTags() { + return computeTags; + } + + public void setComputeTags(List computeTags) { + this.computeTags = computeTags; + } + + public List getRootDiskTags() { + return rootDiskTags; + } + + public void setRootDiskTags(List rootDiskTags) { + this.rootDiskTags = rootDiskTags; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public String getDisplayname() { + return displayname; + } + + public void setDisplayname(String displayname) { + this.displayname = displayname; + } + + public List getNetworkIds() { + return networkIds; + } + + public void setNetworkIds(List networkIds) { + this.networkIds = networkIds; + } + +} diff --git a/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java index 36784c5d934..30595f01c9e 100755 --- a/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/platform/orchestration/CloudOrchestrator.java @@ -19,23 +19,57 @@ package org.apache.cloudstack.platform.orchestration; import java.net.URL; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.inject.Inject; + import org.apache.cloudstack.engine.cloud.entity.api.NetworkEntity; import org.apache.cloudstack.engine.cloud.entity.api.TemplateEntity; import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity; +import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntityImpl; +import org.apache.cloudstack.engine.cloud.entity.api.VMEntityManager; import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity; import org.apache.cloudstack.engine.service.api.OrchestrationService; +import com.cloud.deploy.DeploymentPlan; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.utils.Pair; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; + public class CloudOrchestrator implements OrchestrationService { + @Inject + private VMEntityManager vmEntityManager; - + @Inject + private VirtualMachineManager _itMgr; + + @Inject + protected VMTemplateDao _templateDao = null; + + @Inject + protected VMInstanceDao _vmDao; + + @Inject + protected ServiceOfferingDao _serviceOfferingDao; + + @Inject + protected DiskOfferingDao _diskOfferingDao = null; + public VirtualMachineEntity createFromScratch(String uuid, String iso, String os, String hypervisor, String hostName, int cpu, int speed, long memory, List networks, List computeTags, Map details, String owner) { // TODO Auto-generated method stub @@ -96,21 +130,99 @@ public class CloudOrchestrator implements OrchestrationService { String owner, String templateId, String hostName, - String displayName, + String displayName, + String hypervisor, int cpu, int speed, - long memory, + long memory, + Long diskSize, List computeTags, List rootDiskTags, - List networks) { - return null; + List networks, DeploymentPlan plan) throws InsufficientCapacityException { + + VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager); + + HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor); + + //load vm instance and offerings and call virtualMachineManagerImpl + VMInstanceVO vm = _vmDao.findByUUID(id); + + // If the template represents an ISO, a disk offering must be passed in, + // and will be used to create the root disk + // Else, a disk offering is optional, and if present will be used to + // create the data disk + Pair rootDiskOffering = new Pair(null, null); + List> dataDiskOfferings = new ArrayList>(); + + ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + rootDiskOffering.first(offering); + + DiskOfferingVO diskOffering = _diskOfferingDao.findById(vm.getDiskOfferingId()); + if (diskOffering == null) { + throw new InvalidParameterValueException( + "Unable to find disk offering " + vm.getDiskOfferingId()); + } + Long size = null; + if (diskOffering.getDiskSize() == 0) { + size = diskSize; + if (size == null) { + throw new InvalidParameterValueException( + "Disk offering " + diskOffering + + " requires size parameter."); + } + } + dataDiskOfferings.add(new Pair(diskOffering, size)); + + if (_itMgr.allocate(vm, _templateDao.findById(new Long(templateId)), offering, rootDiskOffering, dataDiskOfferings, null, null, plan, hypervisorType, null) == null) { + return null; + } + + return vmEntity; } @Override - public VirtualMachineEntity createVirtualMachineFromScratch(String id, String owner, String isoId, String hostName, String displayName, String hypervisor, String os, int cpu, int speed, long memory, - List computeTags, List rootDiskTags, List networks) { - // TODO Auto-generated method stub - return null; + public VirtualMachineEntity createVirtualMachineFromScratch(String id, String owner, String isoId, String hostName, String displayName, String hypervisor, String os, int cpu, int speed, long memory,Long diskSize, + List computeTags, List rootDiskTags, List networks, DeploymentPlan plan) throws InsufficientCapacityException { + + VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager); + + //load vm instance and offerings and call virtualMachineManagerImpl + VMInstanceVO vm = _vmDao.findByUUID(id); + + + Pair rootDiskOffering = new Pair(null, null); + ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + rootDiskOffering.first(offering); + + List> dataDiskOfferings = new ArrayList>(); + Long diskOfferingId = vm.getDiskOfferingId(); + if (diskOfferingId == null) { + throw new InvalidParameterValueException( + "Installing from ISO requires a disk offering to be specified for the root disk."); + } + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); + if (diskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); + } + Long size = null; + if (diskOffering.getDiskSize() == 0) { + size = diskSize; + if (size == null) { + throw new InvalidParameterValueException("Disk offering " + + diskOffering + " requires size parameter."); + } + } + rootDiskOffering.first(diskOffering); + rootDiskOffering.second(size); + + + HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor); + + if (_itMgr.allocate(vm, _templateDao.findById(new Long(isoId)), offering, rootDiskOffering, dataDiskOfferings, null, null, plan, hypervisorType, null) == null) { + return null; + } + + return vmEntity; } @Override diff --git a/server/pom.xml b/server/pom.xml index 29922b62cb5..ed21c37bda8 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -70,6 +70,11 @@ tests test + + org.apache.cloudstack + cloud-engine-api + ${project.version} + diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 650a11b6372..e0454c32270 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -32,6 +32,8 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity; +import org.apache.cloudstack.engine.service.api.OrchestrationService; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -161,6 +163,7 @@ import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; @@ -181,6 +184,7 @@ import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeHostVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.StoragePoolDao; @@ -238,3584 +242,4437 @@ import com.cloud.vm.dao.VMInstanceDao; @Component @Local(value = { UserVmManager.class, UserVmService.class }) public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager { - private static final Logger s_logger = Logger.getLogger(UserVmManagerImpl.class); - - private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds - - @Inject - protected HostDao _hostDao = null; - @Inject - protected ServiceOfferingDao _offeringDao = null; - @Inject - protected DiskOfferingDao _diskOfferingDao = null; - @Inject - protected VMTemplateDao _templateDao = null; - @Inject - protected VMTemplateDetailsDao _templateDetailsDao = null; - @Inject - protected VMTemplateHostDao _templateHostDao = null; - @Inject - protected VMTemplateZoneDao _templateZoneDao = null; - @Inject - protected DomainDao _domainDao = null; - @Inject - protected UserVmDao _vmDao = null; - @Inject - protected VolumeDao _volsDao = null; - @Inject - protected DataCenterDao _dcDao = null; - @Inject - protected FirewallRulesDao _rulesDao = null; - @Inject - protected LoadBalancerVMMapDao _loadBalancerVMMapDao = null; - @Inject - protected PortForwardingRulesDao _portForwardingDao; - @Inject - protected IPAddressDao _ipAddressDao = null; - @Inject - protected HostPodDao _podDao = null; - @Inject - protected NetworkManager _networkMgr = null; - @Inject - protected StorageManager _storageMgr = null; - @Inject - protected SnapshotManager _snapshotMgr = null; - @Inject - protected AgentManager _agentMgr = null; - @Inject - protected ConfigurationManager _configMgr = null; - @Inject - protected AccountDao _accountDao = null; - @Inject - protected UserDao _userDao = null; - @Inject - protected SnapshotDao _snapshotDao = null; - @Inject - protected GuestOSDao _guestOSDao = null; - @Inject - protected HighAvailabilityManager _haMgr = null; - @Inject - protected AlertManager _alertMgr = null; - @Inject - protected AccountManager _accountMgr; - @Inject - protected AccountService _accountService; - @Inject - protected AsyncJobManager _asyncMgr; - @Inject - protected ClusterDao _clusterDao; - @Inject - protected StoragePoolDao _storagePoolDao; - @Inject - protected SecurityGroupManager _securityGroupMgr; - @Inject - protected ServiceOfferingDao _serviceOfferingDao; - @Inject - protected NetworkOfferingDao _networkOfferingDao; - @Inject - protected InstanceGroupDao _vmGroupDao; - @Inject - protected InstanceGroupVMMapDao _groupVMMapDao; - @Inject - protected VirtualMachineManager _itMgr; - @Inject - protected NetworkDao _networkDao; - @Inject - protected NicDao _nicDao; - @Inject - protected VpcDao _vpcDao; - @Inject - protected RulesManager _rulesMgr; - @Inject - protected LoadBalancingRulesManager _lbMgr; - @Inject - protected UsageEventDao _usageEventDao; - @Inject - protected SSHKeyPairDao _sshKeyPairDao; - @Inject - protected UserVmDetailsDao _vmDetailsDao; - @Inject - protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; - @Inject - protected SecurityGroupDao _securityGroupDao; - @Inject - protected CapacityManager _capacityMgr;; - @Inject - protected VMInstanceDao _vmInstanceDao; - @Inject - protected ResourceLimitService _resourceLimitMgr; - @Inject - protected FirewallManager _firewallMgr; - @Inject - protected ProjectManager _projectMgr; - @Inject - protected ResourceManager _resourceMgr; - @Inject - protected NetworkServiceMapDao _ntwkSrvcDao; - @Inject - SecurityGroupVMMapDao _securityGroupVMMapDao; - @Inject - protected ItWorkDao _workDao; - @Inject - protected VolumeHostDao _volumeHostDao; - @Inject - ResourceTagDao _resourceTagDao; - @Inject - PhysicalNetworkDao _physicalNetworkDao; - @Inject - VpcManager _vpcMgr; - - protected ScheduledExecutorService _executor = null; - protected int _expungeInterval; - protected int _expungeDelay; - - protected String _name; - protected String _instance; - protected String _zone; - - private ConfigurationDao _configDao; - private int _createprivatetemplatefromvolumewait; - private int _createprivatetemplatefromsnapshotwait; - @Override - public UserVmVO getVirtualMachine(long vmId) { - return _vmDao.findById(vmId); - } - - @Override - public List getVirtualMachines(long hostId) { - return _vmDao.listByHostId(hostId); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD, eventDescription = "resetting Vm password", async = true) - public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException { - Account caller = UserContext.current().getCaller(); - Long vmId = cmd.getId(); - UserVmVO userVm = _vmDao.findById(cmd.getId()); - _vmDao.loadDetails(userVm); - - // Do parameters input validation - if (userVm == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + cmd.getId()); - } - - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId()); - if (template == null || !template.getEnablePassword()) { - throw new InvalidParameterValueException("Fail to reset password for the virtual machine, the template is not password enabled"); - } - - if (userVm.getState() == State.Error || userVm.getState() == State.Expunging) { - s_logger.error("vm is not in the right state: " + vmId); - throw new InvalidParameterValueException("Vm with id " + vmId + " is not in the right state"); - } - - _accountMgr.checkAccess(caller, null, true, userVm); - - boolean result = resetVMPasswordInternal(cmd, password); - - if (result) { - userVm.setPassword(password); - //update the password in vm_details table too - // Check if an SSH key pair was selected for the instance and if so use it to encrypt & save the vm password - String sshPublicKey = userVm.getDetail("SSH.PublicKey"); - if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); - } - - userVm.setDetail("Encrypted.Password", encryptedPasswd); - _vmDao.saveDetails(userVm); - } - } else { - throw new CloudRuntimeException("Failed to reset password for the virtual machine "); - } - - return userVm; - } - - private boolean resetVMPasswordInternal(ResetVMPasswordCmd cmd, String password) throws ResourceUnavailableException, InsufficientCapacityException { - Long vmId = cmd.getId(); - Long userId = UserContext.current().getCallerUserId(); - VMInstanceVO vmInstance = _vmDao.findById(vmId); - - if (password == null || password.equals("")) { - return false; - } - - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); - if (template.getEnablePassword()) { - Nic defaultNic = _networkMgr.getDefaultNic(vmId); - if (defaultNic == null) { - s_logger.error("Unable to reset password for vm " + vmInstance + " as the instance doesn't have default nic"); - return false; - } - - Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); - NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkMgr.isSecurityGroupSupportedInNetwork(defaultNetwork), _networkMgr.getNetworkTag(template.getHypervisorType(), defaultNetwork)); - VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance); - vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); - - UserDataServiceProvider element = _networkMgr.getPasswordResetProvider(defaultNetwork); - if (element == null) { - throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + - " provider needed for password reset"); - } - - boolean result = element.savePassword(defaultNetwork, defaultNicProfile, vmProfile); - - // Need to reboot the virtual machine so that the password gets redownloaded from the DomR, and reset on the VM - if (!result) { - s_logger.debug("Failed to reset password for the virutal machine; no need to reboot the vm"); - return false; - } else { - if (vmInstance.getState() == State.Stopped) { - s_logger.debug("Vm " + vmInstance + " is stopped, not rebooting it as a part of password reset"); - return true; - } - - if (rebootVirtualMachine(userId, vmId) == null) { - s_logger.warn("Failed to reboot the vm " + vmInstance); - return false; - } else { - s_logger.debug("Vm " + vmInstance + " is rebooted successfully as a part of password reset"); - return true; - } - } - } else { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Reset password called for a vm that is not using a password enabled template"); - } - return false; - } - } - - @Override - public boolean stopVirtualMachine(long userId, long vmId) { - boolean status = false; - if (s_logger.isDebugEnabled()) { - s_logger.debug("Stopping vm=" + vmId); - } - UserVmVO vm = _vmDao.findById(vmId); - if (vm == null || vm.getRemoved() != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is either removed or deleted."); - } - return true; - } - - User user = _userDao.findById(userId); - Account account = _accountDao.findById(user.getAccountId()); - - try { - status = _itMgr.stop(vm, user, account); - } catch (ResourceUnavailableException e) { - s_logger.debug("Unable to stop due to ", e); - status = false; - } - - if (status) { - return status; - } else { - return status; - } - } - - private int getMaxDataVolumesSupported(UserVmVO vm) { - Long hostId = vm.getHostId(); - if (hostId == null) { - hostId = vm.getLastHostId(); - } - HostVO host = _hostDao.findById(hostId); - Integer maxDataVolumesSupported = null; - if (host != null) { - _hostDao.loadDetails(host); - maxDataVolumesSupported = _hypervisorCapabilitiesDao.getMaxDataVolumesLimit(host.getHypervisorType(), host.getDetail("product_version")); - } - if (maxDataVolumesSupported == null) { - maxDataVolumesSupported = 6; // 6 data disks by default if nothing is specified in 'hypervisor_capabilities' table - } - - return maxDataVolumesSupported.intValue(); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) - public Volume attachVolumeToVM(AttachVolumeCmd command) { - Long vmId = command.getVirtualMachineId(); - Long volumeId = command.getId(); - Long deviceId = command.getDeviceId(); - Account caller = UserContext.current().getCaller(); - - // Check that the volume ID is valid - VolumeVO volume = _volsDao.findById(volumeId); - // Check that the volume is a data volume - if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) { - throw new InvalidParameterValueException("Please specify a valid data volume."); - } - - // Check that the volume is not currently attached to any VM - if (volume.getInstanceId() != null) { - throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); - } - - // Check that the volume is not destroyed - if (volume.getState() == Volume.State.Destroy) { - throw new InvalidParameterValueException("Please specify a volume that is not destroyed."); - } - - // Check that the virtual machine ID is valid and it's a user vm - UserVmVO vm = _vmDao.findById(vmId); - if (vm == null || vm.getType() != VirtualMachine.Type.User) { - throw new InvalidParameterValueException("Please specify a valid User VM."); - } - - // Check that the VM is in the correct state - if (vm.getState() != State.Running && vm.getState() != State.Stopped) { - throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); - } - - // Check that the device ID is valid - if (deviceId != null) { - if (deviceId.longValue() == 0) { - throw new InvalidParameterValueException("deviceId can't be 0, which is used by Root device"); - } - } - - // Check that the number of data volumes attached to VM is less than that supported by hypervisor - List existingDataVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK); - int maxDataVolumesSupported = getMaxDataVolumesSupported(vm); - if (existingDataVolumes.size() >= maxDataVolumesSupported) { - throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + "). Please specify another VM."); - } - - // Check that the VM and the volume are in the same zone - if (vm.getDataCenterIdToDeployIn() != volume.getDataCenterId()) { - throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume."); - } - - // If local storage is disabled then attaching a volume with local disk offering not allowed - DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId()); - if (!dataCenter.isLocalStorageEnabled()) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); - if (diskOffering.getUseLocalStorage()) { - throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it"); - } - } - - //permission check - _accountMgr.checkAccess(caller, null, true, volume, vm); - - //Check if volume is stored on secondary Storage. - boolean isVolumeOnSec = false; - VolumeHostVO volHostVO = _volumeHostDao.findByVolumeId(volume.getId()); - if (volHostVO != null){ - isVolumeOnSec = true; - if( !(volHostVO.getDownloadState() == Status.DOWNLOADED) ){ - throw new InvalidParameterValueException("Volume is not uploaded yet. Please try this operation once the volume is uploaded"); - } - } - - if ( !(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.UploadOp.equals(volume.getState())) ) { - throw new InvalidParameterValueException("Volume state must be in Allocated, Ready or in Uploaded state"); - } - - VolumeVO rootVolumeOfVm = null; - List rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT); - if (rootVolumesOfVm.size() != 1) { - throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state."); - } else { - rootVolumeOfVm = rootVolumesOfVm.get(0); - } - - HypervisorType rootDiskHyperType = vm.getHypervisorType(); - - HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume.getId()); - if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) { - throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm"); - } - - //allocate deviceId - List vols = _volsDao.findByInstance(vmId); - if (deviceId != null) { - if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) { - throw new RuntimeException("deviceId should be 1,2,4-15"); - } - for (VolumeVO vol : vols) { - if (vol.getDeviceId().equals(deviceId)) { - throw new RuntimeException("deviceId " + deviceId + " is used by VM " + vm.getHostName()); - } - } - } else { - // allocate deviceId here - List devIds = new ArrayList(); - for (int i = 1; i < 15; i++) { - devIds.add(String.valueOf(i)); - } - devIds.remove("3"); - for (VolumeVO vol : vols) { - devIds.remove(vol.getDeviceId().toString().trim()); - } - deviceId = Long.parseLong(devIds.iterator().next()); - } - - boolean createVolumeOnBackend = true; - if (rootVolumeOfVm.getState() == Volume.State.Allocated) { - createVolumeOnBackend = false; - if(isVolumeOnSec){ - throw new CloudRuntimeException("Cant attach uploaded volume to the vm which is not created. Please start it and then retry"); - } - } - - //create volume on the backend only when vm's root volume is allocated - if (createVolumeOnBackend) { - if (volume.getState().equals(Volume.State.Allocated) || isVolumeOnSec) { - /* Need to create the volume */ - VMTemplateVO rootDiskTmplt = _templateDao.findById(vm.getTemplateId()); - DataCenterVO dcVO = _dcDao.findById(vm.getDataCenterIdToDeployIn()); - HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); - StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); - ServiceOfferingVO svo = _serviceOfferingDao.findById(vm.getServiceOfferingId()); - DiskOfferingVO diskVO = _diskOfferingDao.findById(volume.getDiskOfferingId()); - Long clusterId = (rootDiskPool == null ? null : rootDiskPool.getClusterId()); - - if (!isVolumeOnSec){ - volume = _storageMgr.createVolume(volume, vm, rootDiskTmplt, dcVO, pod, clusterId, svo, diskVO, new ArrayList(), volume.getSize(), rootDiskHyperType); - }else { - try { - // Format of data disk should be the same as root disk - if( ! volHostVO.getFormat().getFileExtension().equals(_storageMgr.getSupportedImageFormatForCluster(rootDiskPool.getClusterId())) ){ - throw new InvalidParameterValueException("Failed to attach volume to VM since volumes format " +volHostVO.getFormat().getFileExtension() + " is not compatible with the vm hypervisor type" ); - } - - // Check that there is some shared storage. - StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); - List sharedVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), null, true); - if (sharedVMPools.size() == 0) { - throw new CloudRuntimeException("Cannot attach volume since there are no shared storage pools in the VM's cluster to copy the uploaded volume to."); - } - - volume = _storageMgr.copyVolumeFromSecToPrimary(volume, vm, rootDiskTmplt, dcVO, pod, rootDiskPool.getClusterId(), svo, diskVO, new ArrayList(), volume.getSize(), rootDiskHyperType); - } catch (NoTransitionException e) { - throw new CloudRuntimeException("Unable to transition the volume ",e); - } - } - - if (volume == null) { - throw new CloudRuntimeException("Failed to create volume when attaching it to VM: " + vm.getHostName()); - } - } - - StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); - DiskOfferingVO volumeDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); - String[] volumeTags = volumeDiskOffering.getTagsArray(); - - boolean isVolumeOnSharedPool = !volumeDiskOffering.getUseLocalStorage(); - StoragePoolVO sourcePool = _storagePoolDao.findById(volume.getPoolId()); - List matchingVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, isVolumeOnSharedPool); - boolean moveVolumeNeeded = true; - if (matchingVMPools.size() == 0) { - String poolType; - if (vmRootVolumePool.getClusterId() != null) { - poolType = "cluster"; - } else if (vmRootVolumePool.getPodId() != null) { - poolType = "pod"; - } else { - poolType = "zone"; - } - throw new CloudRuntimeException("There are no storage pools in the VM's " + poolType + " with all of the volume's tags (" + volumeDiskOffering.getTags() + ")."); - } else { - long sourcePoolId = sourcePool.getId(); - Long sourcePoolDcId = sourcePool.getDataCenterId(); - Long sourcePoolPodId = sourcePool.getPodId(); - Long sourcePoolClusterId = sourcePool.getClusterId(); - for (StoragePoolVO vmPool : matchingVMPools) { - long vmPoolId = vmPool.getId(); - Long vmPoolDcId = vmPool.getDataCenterId(); - Long vmPoolPodId = vmPool.getPodId(); - Long vmPoolClusterId = vmPool.getClusterId(); - - // Moving a volume is not required if storage pools belongs to same cluster in case of shared volume or - // identical storage pool in case of local - if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId - && (isVolumeOnSharedPool || sourcePoolId == vmPoolId)) { - moveVolumeNeeded = false; - break; - } - } - } - - if (moveVolumeNeeded) { - if (isVolumeOnSharedPool) { - // Move the volume to a storage pool in the VM's zone, pod, or cluster - try { - volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); - } catch (ConcurrentOperationException e) { - throw new CloudRuntimeException(e.toString()); - } - } else { - throw new CloudRuntimeException("Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName() + " as migration of local data volume is not allowed"); - } - } - } - - AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); - if (asyncExecutor != null) { - AsyncJobVO job = asyncExecutor.getJob(); - - if (s_logger.isInfoEnabled()) { - s_logger.info("Trying to attaching volume " + volumeId + " to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status"); - } - - _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); - _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); - } - - String errorMsg = "Failed to attach volume: " + volume.getName() + " to VM: " + vm.getHostName(); - boolean sendCommand = (vm.getState() == State.Running); - AttachVolumeAnswer answer = null; - Long hostId = vm.getHostId(); - if (hostId == null) { - hostId = vm.getLastHostId(); - HostVO host = _hostDao.findById(hostId); - if (host != null && host.getHypervisorType() == HypervisorType.VMware) { - sendCommand = true; - } - } - - if (sendCommand) { - StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); - AttachVolumeCommand cmd = new AttachVolumeCommand(true, vm.getInstanceName(), volume.getPoolType(), volume.getFolder(), volume.getPath(), volume.getName(), deviceId, volume.getChainInfo()); - cmd.setPoolUuid(volumePool.getUuid()); - - try { - answer = (AttachVolumeAnswer) _agentMgr.send(hostId, cmd); - } catch (Exception e) { - throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); - } - } - - if (!sendCommand || (answer != null && answer.getResult())) { - // Mark the volume as attached - if (sendCommand) { - _volsDao.attachVolume(volume.getId(), vmId, answer.getDeviceId()); - } else { - _volsDao.attachVolume(volume.getId(), vmId, deviceId); - } - return _volsDao.findById(volumeId); - } else { - if (answer != null) { - String details = answer.getDetails(); - if (details != null && !details.isEmpty()) { - errorMsg += "; " + details; - } - } - throw new CloudRuntimeException(errorMsg); - } - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) - public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { - Account caller = UserContext.current().getCaller(); - if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null)) - || (cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd.getVirtualMachineId() == null))) { - throw new InvalidParameterValueException("Please provide either a volume id, or a tuple(device id, instance id)"); - } - - Long volumeId = cmmd.getId(); - VolumeVO volume = null; - - if (volumeId != null) { - volume = _volsDao.findById(volumeId); - } else { - volume = _volsDao.findByInstanceAndDeviceId(cmmd.getVirtualMachineId(), cmmd.getDeviceId()).get(0); - } - - Long vmId = null; - - if (cmmd.getVirtualMachineId() == null) { - vmId = volume.getInstanceId(); - } else { - vmId = cmmd.getVirtualMachineId(); - } - - // Check that the volume ID is valid - if (volume == null) { - throw new InvalidParameterValueException("Unable to find volume with ID: " + volumeId); - } - - // Permissions check - _accountMgr.checkAccess(caller, null, true, volume); - - // Check that the volume is a data volume - if (volume.getVolumeType() != Volume.Type.DATADISK) { - throw new InvalidParameterValueException("Please specify a data volume."); - } - - // Check that the volume is currently attached to a VM - if (vmId == null) { - throw new InvalidParameterValueException("The specified volume is not attached to a VM."); - } - - // Check that the VM is in the correct state - UserVmVO vm = _vmDao.findById(vmId); - if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) { - throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); - } - - AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor(); - if (asyncExecutor != null) { - AsyncJobVO job = asyncExecutor.getJob(); - - if (s_logger.isInfoEnabled()) { - s_logger.info("Trying to attaching volume " + volumeId + "to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status"); - } - - _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); - _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); - } - - String errorMsg = "Failed to detach volume: " + volume.getName() + " from VM: " + vm.getHostName(); - boolean sendCommand = (vm.getState() == State.Running); - Answer answer = null; - - if (sendCommand) { - AttachVolumeCommand cmd = new AttachVolumeCommand(false, vm.getInstanceName(), volume.getPoolType(), volume.getFolder(), volume.getPath(), volume.getName(), - cmmd.getDeviceId() != null ? cmmd.getDeviceId() : volume.getDeviceId(), volume.getChainInfo()); - - StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); - cmd.setPoolUuid(volumePool.getUuid()); - - try { - answer = _agentMgr.send(vm.getHostId(), cmd); - } catch (Exception e) { - throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); - } - } - - if (!sendCommand || (answer != null && answer.getResult())) { - // Mark the volume as detached - _volsDao.detachVolume(volume.getId()); - if (answer != null && answer instanceof AttachVolumeAnswer) { - volume.setChainInfo(((AttachVolumeAnswer) answer).getChainInfo()); - _volsDao.update(volume.getId(), volume); - } - - return _volsDao.findById(volumeId); - } else { - - if (answer != null) { - String details = answer.getDetails(); - if (details != null && !details.isEmpty()) { - errorMsg += "; " + details; - } - } - - throw new CloudRuntimeException(errorMsg); - } - } - - @Override - public boolean attachISOToVM(long vmId, long isoId, boolean attach) { - UserVmVO vm = _vmDao.findById(vmId); - - if (vm == null) { - return false; - } else if (vm.getState() != State.Running) { - return true; - } - String isoPath; - VMTemplateVO tmplt = _templateDao.findById(isoId); - if (tmplt == null) { - s_logger.warn("ISO: " + isoId + " does not exist"); - return false; - } - // Get the path of the ISO - Pair isoPathPair = null; - if (tmplt.getTemplateType() == TemplateType.PERHOST) { - isoPath = tmplt.getName(); - } else { - isoPathPair = _storageMgr.getAbsoluteIsoPath(isoId, vm.getDataCenterIdToDeployIn()); - if (isoPathPair == null) { - s_logger.warn("Couldn't get absolute iso path"); - return false; - } else { - isoPath = isoPathPair.first(); - } - } - - String vmName = vm.getInstanceName(); - - HostVO host = _hostDao.findById(vm.getHostId()); - if (host == null) { - s_logger.warn("Host: " + vm.getHostId() + " does not exist"); - return false; - } - AttachIsoCommand cmd = new AttachIsoCommand(vmName, isoPath, attach); - if (isoPathPair != null) { - cmd.setStoreUrl(isoPathPair.second()); - } - Answer a = _agentMgr.easySend(vm.getHostId(), cmd); - - return (a != null && a.getResult()); - } - - private UserVm rebootVirtualMachine(long userId, long vmId) throws InsufficientCapacityException, ResourceUnavailableException { - UserVmVO vm = _vmDao.findById(vmId); - User caller = _accountMgr.getActiveUser(userId); - Account owner = _accountMgr.getAccount(vm.getAccountId()); - - if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging || vm.getRemoved() != null) { - s_logger.warn("Vm id=" + vmId + " doesn't exist"); - return null; - } - - if (vm.getState() == State.Running && vm.getHostId() != null) { - return _itMgr.reboot(vm, null, caller, owner); - } else { - s_logger.error("Vm id=" + vmId + " is not in Running state, failed to reboot"); - return null; - } - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "upgrading Vm") - /* - * TODO: cleanup eventually - Refactored API call - */ - public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) { - Long vmId = cmd.getId(); - Long svcOffId = cmd.getServiceOfferingId(); - Account caller = UserContext.current().getCaller(); - - // Verify input parameters - UserVmVO vmInstance = _vmDao.findById(vmId); - if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - _accountMgr.checkAccess(caller, null, true, vmInstance); - - // Check that the specified service offering ID is valid - _itMgr.checkIfCanUpgrade(vmInstance, svcOffId); - - _itMgr.upgradeVmDb(vmId, svcOffId); - - return _vmDao.findById(vmInstance.getId()); - } - - - @Override - public HashMap getVirtualMachineStatistics(long hostId, String hostName, List vmIds) throws CloudRuntimeException { - HashMap vmStatsById = new HashMap(); - - if (vmIds.isEmpty()) { - return vmStatsById; - } - - List vmNames = new ArrayList(); - - for (Long vmId : vmIds) { - UserVmVO vm = _vmDao.findById(vmId); - vmNames.add(vm.getInstanceName()); - } - - Answer answer = _agentMgr.easySend(hostId, new GetVmStatsCommand(vmNames, _hostDao.findById(hostId).getGuid(), hostName)); - if (answer == null || !answer.getResult()) { - s_logger.warn("Unable to obtain VM statistics."); - return null; - } else { - HashMap vmStatsByName = ((GetVmStatsAnswer) answer).getVmStatsMap(); - - if (vmStatsByName == null) { - s_logger.warn("Unable to obtain VM statistics."); - return null; - } - - for (String vmName : vmStatsByName.keySet()) { - vmStatsById.put(vmIds.get(vmNames.indexOf(vmName)), vmStatsByName.get(vmName)); - } - } - - return vmStatsById; - } - - @Override - @DB - public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException, CloudRuntimeException { - - Long vmId = cmd.getId(); - Account caller = UserContext.current().getCaller(); - - // Verify input parameters - UserVmVO vm = _vmDao.findById(vmId.longValue()); - - if (vm == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - //check permissions - _accountMgr.checkAccess(caller, null, true, vm); - - if (vm.getRemoved() != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find vm or vm is removed: " + vmId); - } - throw new InvalidParameterValueException("Unable to find vm by id " + vmId); - } - - if (vm.getState() != State.Destroyed) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("vm is not in the right state: " + vmId); - } - throw new InvalidParameterValueException("Vm with id " + vmId + " is not in the right state"); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Recovering vm " + vmId); - } - - Transaction txn = Transaction.currentTxn(); - AccountVO account = null; - txn.start(); - - account = _accountDao.lockRow(vm.getAccountId(), true); - - // if the account is deleted, throw error - if (account.getRemoved() != null) { - throw new CloudRuntimeException("Unable to recover VM as the account is deleted"); - } - - // First check that the maximum number of UserVMs for the given accountId will not be exceeded - _resourceLimitMgr.checkResourceLimit(account, ResourceType.user_vm); - - _haMgr.cancelDestroy(vm, vm.getHostId()); - - try { - if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { - s_logger.debug("Unable to recover the vm because it is not in the correct state: " + vmId); - throw new InvalidParameterValueException("Unable to recover the vm because it is not in the correct state: " + vmId); - } - } catch (NoTransitionException e){ - throw new InvalidParameterValueException("Unable to recover the vm because it is not in the correct state: " + vmId); - } - - // Recover the VM's disks - List volumes = _volsDao.findByInstance(vmId); - for (VolumeVO volume : volumes) { - if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - // Create an event - Long templateId = volume.getTemplateId(); - Long diskOfferingId = volume.getDiskOfferingId(); - Long offeringId = null; - if (diskOfferingId != null) { - DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId); - if (offering != null && (offering.getType() == DiskOfferingVO.Type.Disk)) { - offeringId = offering.getId(); - } - } - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), offeringId, templateId, - volume.getSize()); - _usageEventDao.persist(usageEvent); - } - } - - _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.volume, new Long(volumes.size())); - - _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.user_vm); - - txn.commit(); - - return _vmDao.findById(vmId); - } - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - _configDao = locator.getDao(ConfigurationDao.class); - if (_configDao == null) { - throw new ConfigurationException("Unable to get the configuration dao."); - } - - Map configs = _configDao.getConfiguration("AgentManager", params); - - _instance = configs.get("instance.name"); - if (_instance == null) { - _instance = "DEFAULT"; - } - - String value = _configDao.getValue(Config.CreatePrivateTemplateFromVolumeWait.toString()); - _createprivatetemplatefromvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreatePrivateTemplateFromVolumeWait.getDefaultValue())); - - value = _configDao.getValue(Config.CreatePrivateTemplateFromSnapshotWait.toString()); - _createprivatetemplatefromsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreatePrivateTemplateFromSnapshotWait.getDefaultValue())); - - String workers = configs.get("expunge.workers"); - int wrks = NumbersUtil.parseInt(workers, 10); - - String time = configs.get("expunge.interval"); - _expungeInterval = NumbersUtil.parseInt(time, 86400); - if ( _expungeInterval < 600 ) { - _expungeInterval = 600; - } - time = configs.get("expunge.delay"); - _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); - if ( _expungeDelay < 600 ) { - _expungeDelay = 600; - } - _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger")); - - _itMgr.registerGuru(VirtualMachine.Type.User, this); - - VirtualMachine.State.getStateMachine().registerListener(new UserVmStateListener(_usageEventDao, _networkDao, _nicDao)); - - s_logger.info("User VM Manager is configured."); - - return true; - } - - @Override - public String getName() { - return _name; - } - - @Override - public boolean start() { - _executor.scheduleWithFixedDelay(new ExpungeTask(), _expungeInterval, _expungeInterval, TimeUnit.SECONDS); - return true; - } - - @Override - public boolean stop() { - _executor.shutdown(); - return true; - } - - protected UserVmManagerImpl() { - } - - public String getRandomPrivateTemplateName() { - return UUID.randomUUID().toString(); - } - - @Override - public Long convertToId(String vmName) { - if (!VirtualMachineName.isValidVmName(vmName, _instance)) { - return null; - } - return VirtualMachineName.getVmId(vmName); - } - - @Override - public boolean expunge(UserVmVO vm, long callerUserId, Account caller) { - UserContext ctx = UserContext.current(); - ctx.setAccountId(vm.getAccountId()); - - try { - //expunge the vm - if (!_itMgr.advanceExpunge(vm, _accountMgr.getSystemUser(), caller)) { - s_logger.info("Did not expunge " + vm); - return false; - } - - // Only if vm is not expunged already, cleanup it's resources - if (vm != null && vm.getRemoved() == null) { - // Cleanup vm resources - all the PF/LB/StaticNat rules associated with vm - s_logger.debug("Starting cleaning up vm " + vm + " resources..."); - if (cleanupVmResources(vm.getId())) { - s_logger.debug("Successfully cleaned up vm " + vm + " resources as a part of expunge process"); - } else { - s_logger.warn("Failed to cleanup resources as a part of vm " + vm + " expunge"); - return false; - } - - _itMgr.remove(vm, _accountMgr.getSystemUser(), caller); - } - - return true; - - } catch (ResourceUnavailableException e) { - s_logger.warn("Unable to expunge " + vm, e); - return false; - } catch (OperationTimedoutException e) { - s_logger.warn("Operation time out on expunging " + vm, e); - return false; - } catch (ConcurrentOperationException e) { - s_logger.warn("Concurrent operations on expunging " + vm, e); - return false; - } - } - - private boolean cleanupVmResources(long vmId) { - boolean success = true; - //Remove vm from security groups - _securityGroupMgr.removeInstanceFromGroups(vmId); - - //Remove vm from instance group - removeInstanceFromInstanceGroup(vmId); - - //cleanup firewall rules - if (_firewallMgr.revokeFirewallRulesForVm(vmId)) { - s_logger.debug("Firewall rules are removed successfully as a part of vm id=" + vmId + " expunge"); - } else { - success = false; - s_logger.warn("Fail to remove firewall rules as a part of vm id=" + vmId + " expunge"); - } - - //cleanup port forwarding rules - if (_rulesMgr.revokePortForwardingRulesForVm(vmId)) { - s_logger.debug("Port forwarding rules are removed successfully as a part of vm id=" + vmId + " expunge"); - } else { - success = false; - s_logger.warn("Fail to remove port forwarding rules as a part of vm id=" + vmId + " expunge"); - } - - // cleanup load balancer rules - if (_lbMgr.removeVmFromLoadBalancers(vmId)) { - s_logger.debug("Removed vm id=" + vmId + " from all load balancers as a part of expunge process"); - } else { - success = false; - s_logger.warn("Fail to remove vm id=" + vmId + " from load balancers as a part of expunge process"); - } - - // If vm is assigned to static nat, disable static nat for the ip address and disassociate ip if elasticIP is enabled - IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(vmId); - try { - if (ip != null) { - if (_rulesMgr.disableStaticNat(ip.getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM, true)) { - s_logger.debug("Disabled 1-1 nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge"); - } else { - s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge"); - success = false; - } - } - } catch (ResourceUnavailableException e) { - success = false; - s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge because resource is unavailable", e); - } - - return success; - } - - @Override - public void deletePrivateTemplateRecord(Long templateId) { - if (templateId != null) { - _templateDao.remove(templateId); - } - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true) - public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException { - Long userId = UserContext.current().getCallerUserId(); - - Account caller = UserContext.current().getCaller(); - boolean isAdmin = (isAdmin(caller.getType())); - - _accountMgr.checkAccess(caller, null, true, templateOwner); - - String name = cmd.getTemplateName(); - if ((name == null) || (name.length() > 32)) { - throw new InvalidParameterValueException("Template name cannot be null and should be less than 32 characters"); - } - - if(cmd.getTemplateTag() != null){ - if (!_accountService.isRootAdmin(caller.getType())){ - throw new PermissionDeniedException("Parameter templatetag can only be specified by a Root Admin, permission denied"); - } - } - - // do some parameter defaulting - Integer bits = cmd.getBits(); - Boolean requiresHvm = cmd.getRequiresHvm(); - Boolean passwordEnabled = cmd.isPasswordEnabled(); - Boolean isPublic = cmd.isPublic(); - Boolean featured = cmd.isFeatured(); - int bitsValue = ((bits == null) ? 64 : bits.intValue()); - boolean requiresHvmValue = ((requiresHvm == null) ? true : requiresHvm.booleanValue()); - boolean passwordEnabledValue = ((passwordEnabled == null) ? false : passwordEnabled.booleanValue()); - if (isPublic == null) { - isPublic = Boolean.FALSE; - } - boolean allowPublicUserTemplates = Boolean.parseBoolean(_configDao.getValue("allow.public.user.templates")); - if (!isAdmin && !allowPublicUserTemplates && isPublic) { - throw new PermissionDeniedException("Failed to create template " + name + ", only private templates can be created."); - } - - Long volumeId = cmd.getVolumeId(); - Long snapshotId = cmd.getSnapshotId(); - if ((volumeId == null) && (snapshotId == null)) { - throw new InvalidParameterValueException("Failed to create private template record, neither volume ID nor snapshot ID were specified."); - } - if ((volumeId != null) && (snapshotId != null)) { - throw new InvalidParameterValueException("Failed to create private template record, please specify only one of volume ID (" + volumeId + ") and snapshot ID (" + snapshotId + ")"); - } - - HypervisorType hyperType; - VolumeVO volume = null; - VMTemplateVO privateTemplate = null; - if (volumeId != null) { // create template from volume - volume = _volsDao.findById(volumeId); - if (volume == null) { - throw new InvalidParameterValueException("Failed to create private template record, unable to find volume " + volumeId); - } - //check permissions - _accountMgr.checkAccess(caller, null, true, volume); - - // If private template is created from Volume, check that the volume will not be active when the private template is - // created - if (!_storageMgr.volumeInactive(volume)) { - String msg = "Unable to create private template for volume: " + volume.getName() + "; volume is attached to a non-stopped VM, please stop the VM first"; - if (s_logger.isInfoEnabled()) { - s_logger.info(msg); - } - throw new CloudRuntimeException(msg); - } - hyperType = _volsDao.getHypervisorType(volumeId); - } else { // create template from snapshot - SnapshotVO snapshot = _snapshotDao.findById(snapshotId); - if (snapshot == null) { - throw new InvalidParameterValueException("Failed to create private template record, unable to find snapshot " + snapshotId); - } - - volume = _volsDao.findById(snapshot.getVolumeId()); - VolumeVO snapshotVolume = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId()); - - //check permissions - _accountMgr.checkAccess(caller, null, true, snapshot); - - if (snapshot.getStatus() != Snapshot.Status.BackedUp) { - throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.Status.BackedUp + " state yet and can't be used for template creation"); - } - - /* - // bug #11428. Operation not supported if vmware and snapshots parent volume = ROOT - if(snapshot.getHypervisorType() == HypervisorType.VMware && snapshotVolume.getVolumeType() == Type.DATADISK){ - throw new UnsupportedServiceException("operation not supported, snapshot with id " + snapshotId + " is created from Data Disk"); - } - */ - - hyperType = snapshot.getHypervisorType(); - } - - _resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.template); - - if (!isAdmin || featured == null) { - featured = Boolean.FALSE; - } - Long guestOSId = cmd.getOsTypeId(); - GuestOSVO guestOS = _guestOSDao.findById(guestOSId); - if (guestOS == null) { - throw new InvalidParameterValueException("GuestOS with ID: " + guestOSId + " does not exist."); - } - - String uniqueName = Long.valueOf((userId == null) ? 1 : userId).toString() + UUID.nameUUIDFromBytes(name.getBytes()).toString(); - Long nextTemplateId = _templateDao.getNextInSequence(Long.class, "id"); - String description = cmd.getDisplayText(); - boolean isExtractable = false; - Long sourceTemplateId = null; - if (volume != null) { - VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId()); - isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM; - if (template != null){ - sourceTemplateId = template.getId(); - }else if (volume.getVolumeType() == Type.ROOT){ //vm created out of blank template - UserVm userVm = ApiDBUtils.findUserVmById(volume.getInstanceId()); - sourceTemplateId = userVm.getIsoId(); - } - } - String templateTag = cmd.getTemplateTag(); - if(templateTag != null){ - if(s_logger.isDebugEnabled()){ - s_logger.debug("Adding template tag: "+templateTag); - } - } - privateTemplate = new VMTemplateVO(nextTemplateId, uniqueName, name, ImageFormat.RAW, isPublic, featured, isExtractable, TemplateType.USER, null, null, requiresHvmValue, bitsValue, templateOwner.getId(), - null, description, passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails()); - if(sourceTemplateId != null){ - if(s_logger.isDebugEnabled()){ - s_logger.debug("This template is getting created from other template, setting source template Id to: "+sourceTemplateId); - } - } - privateTemplate.setSourceTemplateId(sourceTemplateId); - - VMTemplateVO template = _templateDao.persist(privateTemplate); - // Increment the number of templates - if (template != null) { - if(cmd.getDetails() != null) { - _templateDetailsDao.persist(template.getId(), cmd.getDetails()); - } - - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template); - } - - if (template != null){ - return template; - }else { - throw new CloudRuntimeException("Failed to create a template"); - } - - } - - @Override - @DB - @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", async = true) - public VMTemplateVO createPrivateTemplate(CreateTemplateCmd command) throws CloudRuntimeException { - Long userId = UserContext.current().getCallerUserId(); - if (userId == null) { - userId = User.UID_SYSTEM; - } - long templateId = command.getEntityId(); - Long volumeId = command.getVolumeId(); - Long snapshotId = command.getSnapshotId(); - SnapshotCommand cmd = null; - VMTemplateVO privateTemplate = null; - - String uniqueName = getRandomPrivateTemplateName(); - - StoragePoolVO pool = null; - HostVO secondaryStorageHost = null; - Long zoneId = null; - Long accountId = null; - SnapshotVO snapshot = null; - String secondaryStorageURL = null; - try { - if (snapshotId != null) { // create template from snapshot - snapshot = _snapshotDao.findById(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Unable to find Snapshot for Id " + snapshotId); - } - zoneId = snapshot.getDataCenterId(); - secondaryStorageHost = _snapshotMgr.getSecondaryStorageHost(snapshot); - secondaryStorageURL = _snapshotMgr.getSecondaryStorageURL(snapshot); - String name = command.getTemplateName(); - String backupSnapshotUUID = snapshot.getBackupSnapshotId(); - if (backupSnapshotUUID == null) { - throw new CloudRuntimeException("Unable to create private template from snapshot " + snapshotId + " due to there is no backupSnapshotUUID for this snapshot"); - } - - Long dcId = snapshot.getDataCenterId(); - accountId = snapshot.getAccountId(); - volumeId = snapshot.getVolumeId(); - - String origTemplateInstallPath = null; - List pools = _storageMgr.ListByDataCenterHypervisor(zoneId, snapshot.getHypervisorType()); - if (pools == null || pools.size() == 0 ) { - throw new CloudRuntimeException("Unable to find storage pools in zone " + zoneId); - } - pool = pools.get(0); - if (snapshot.getVersion() != null && snapshot.getVersion().equalsIgnoreCase("2.1")) { - VolumeVO volume = _volsDao.findByIdIncludingRemoved(volumeId); - if (volume == null) { - throw new CloudRuntimeException("failed to upgrade snapshot " + snapshotId + " due to unable to find orignal volume:" + volumeId + ", try it later "); - } - if ( volume.getTemplateId() == null ) { - _snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); - } else { - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(volume.getTemplateId()); - if (template == null) { - throw new CloudRuntimeException("failed to upgrade snapshot " + snapshotId + " due to unalbe to find orignal template :" + volume.getTemplateId() + ", try it later "); - } - Long origTemplateId = template.getId(); - Long origTmpltAccountId = template.getAccountId(); - if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { - throw new CloudRuntimeException("failed to upgrade snapshot " + snapshotId + " due to volume:" + volumeId + " is being used, try it later "); - } - cmd = new UpgradeSnapshotCommand(null, secondaryStorageURL, dcId, accountId, volumeId, origTemplateId, origTmpltAccountId, null, snapshot.getBackupSnapshotId(), - snapshot.getName(), "2.1"); - if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { - throw new CloudRuntimeException("Creating template failed due to volume:" + volumeId + " is being used, try it later "); - } - Answer answer = null; - try { - answer = _storageMgr.sendToPool(pool, cmd); - cmd = null; - } catch (StorageUnavailableException e) { - } finally { - _volsDao.unlockFromLockTable(volumeId.toString()); - } - if ((answer != null) && answer.getResult()) { - _snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); - } else { - throw new CloudRuntimeException("Unable to upgrade snapshot"); - } - } - } - if( snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0 ) { - _snapshotMgr.downloadSnapshotsFromSwift(snapshot); - } - cmd = new CreatePrivateTemplateFromSnapshotCommand(pool.getUuid(), secondaryStorageURL, dcId, accountId, snapshot.getVolumeId(), backupSnapshotUUID, snapshot.getName(), - origTemplateInstallPath, templateId, name, _createprivatetemplatefromsnapshotwait); - } else if (volumeId != null) { - VolumeVO volume = _volsDao.findById(volumeId); - if (volume == null) { - throw new CloudRuntimeException("Unable to find volume for Id " + volumeId); - } - accountId = volume.getAccountId(); - - if (volume.getPoolId() == null) { - _templateDao.remove(templateId); - throw new CloudRuntimeException("Volume " + volumeId + " is empty, can't create template on it"); - } - String vmName = _storageMgr.getVmNameOnVolume(volume); - zoneId = volume.getDataCenterId(); - secondaryStorageHost = _storageMgr.getSecondaryStorageHost(zoneId); - if (secondaryStorageHost == null) { - throw new CloudRuntimeException("Can not find the secondary storage for zoneId " + zoneId); - } - secondaryStorageURL = secondaryStorageHost.getStorageUrl(); - - pool = _storagePoolDao.findById(volume.getPoolId()); - cmd = new CreatePrivateTemplateFromVolumeCommand(pool.getUuid(), secondaryStorageURL, templateId, accountId, command.getTemplateName(), uniqueName, volume.getPath(), vmName, _createprivatetemplatefromvolumewait); - - } else { - throw new CloudRuntimeException("Creating private Template need to specify snapshotId or volumeId"); - } - // FIXME: before sending the command, check if there's enough capacity - // on the storage server to create the template - - // This can be sent to a KVM host too. - CreatePrivateTemplateAnswer answer = null; - if (snapshotId != null) { - if (!_snapshotDao.lockInLockTable(snapshotId.toString(), 10)) { - throw new CloudRuntimeException("Creating template from snapshot failed due to snapshot:" + snapshotId + " is being used, try it later "); - } - } else { - if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { - throw new CloudRuntimeException("Creating template from volume failed due to volume:" + volumeId + " is being used, try it later "); - } - } - try { - answer = (CreatePrivateTemplateAnswer) _storageMgr.sendToPool(pool, cmd); - } catch (StorageUnavailableException e) { - } finally { - if (snapshotId != null) { - _snapshotDao.unlockFromLockTable(snapshotId.toString()); - } else { - _volsDao.unlockFromLockTable(volumeId.toString()); - } - } - if ((answer != null) && answer.getResult()) { - privateTemplate = _templateDao.findById(templateId); - String answerUniqueName = answer.getUniqueName(); - if (answerUniqueName != null) { - privateTemplate.setUniqueName(answerUniqueName); - } else { - privateTemplate.setUniqueName(uniqueName); - } - ImageFormat format = answer.getImageFormat(); - if (format != null) { - privateTemplate.setFormat(format); - } else { - // This never occurs. - // Specify RAW format makes it unusable for snapshots. - privateTemplate.setFormat(ImageFormat.RAW); - } - - String checkSum = getChecksum(secondaryStorageHost.getId(), answer.getPath()); - - Transaction txn = Transaction.currentTxn(); - - txn.start(); - - privateTemplate.setChecksum(checkSum); - _templateDao.update(templateId, privateTemplate); - - // add template zone ref for this template - _templateDao.addTemplateToZone(privateTemplate, zoneId); - VMTemplateHostVO templateHostVO = new VMTemplateHostVO(secondaryStorageHost.getId(), templateId); - templateHostVO.setDownloadPercent(100); - templateHostVO.setDownloadState(Status.DOWNLOADED); - templateHostVO.setInstallPath(answer.getPath()); - templateHostVO.setLastUpdated(new Date()); - templateHostVO.setSize(answer.getVirtualSize()); - templateHostVO.setPhysicalSize(answer.getphysicalSize()); - _templateHostDao.persist(templateHostVO); - - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_TEMPLATE_CREATE, privateTemplate.getAccountId(), secondaryStorageHost.getDataCenterId(), privateTemplate.getId(), - privateTemplate.getName(), null, privateTemplate.getSourceTemplateId(), templateHostVO.getSize()); - _usageEventDao.persist(usageEvent); - txn.commit(); - } - } finally { - if (snapshot != null && snapshot.getSwiftId() != null && secondaryStorageURL != null && zoneId != null && accountId != null && volumeId != null) { - _snapshotMgr.deleteSnapshotsForVolume (secondaryStorageURL, zoneId, accountId, volumeId); - } - if (privateTemplate == null) { - Transaction txn = Transaction.currentTxn(); - txn.start(); - // Remove the template record - _templateDao.expunge(templateId); - - // decrement resource count - if (accountId != null) { - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); - } - txn.commit(); - } - } - - if (privateTemplate != null){ - return privateTemplate; - }else { - throw new CloudRuntimeException("Failed to create a template"); - } - } - - @Override - public String getChecksum(Long hostId, String templatePath){ - HostVO ssHost = _hostDao.findById(hostId); - Host.Type type = ssHost.getType(); - if( type != Host.Type.SecondaryStorage && type != Host.Type.LocalSecondaryStorage ) { - return null; - } - String secUrl = ssHost.getStorageUrl(); - Answer answer; - answer = _agentMgr.sendToSecStorage(ssHost, new ComputeChecksumCommand(secUrl, templatePath)); - if(answer != null && answer.getResult()) { - return answer.getDetails(); - } - return null; - } - - // used for vm transitioning to error state - private void updateVmStateForFailedVmCreation(Long vmId) { - - UserVmVO vm = _vmDao.findById(vmId); - - - if (vm != null) { - if (vm.getState().equals(State.Stopped)) { - s_logger.debug("Destroying vm " + vm + " as it failed to create"); - try { - _itMgr.stateTransitTo(vm, VirtualMachine.Event.OperationFailedToError, null); - } catch (NoTransitionException e1) { - s_logger.warn(e1.getMessage()); - } - // destroy associated volumes for vm in error state - // get all volumes in non destroyed state - List volumesForThisVm = _volsDao.findUsableVolumesForInstance(vm.getId()); - for (VolumeVO volume : volumesForThisVm) { - try { - if (volume.getState() != Volume.State.Destroy) { - _storageMgr.destroyVolume(volume); - } - } catch (ConcurrentOperationException e) { - s_logger.warn("Unable to delete volume:" + volume.getId() + " for vm:" + vmId + " whilst transitioning to error state"); - } - } - String msg = "Failed to deploy Vm with Id: " + vmId; - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), msg, msg); - - _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.user_vm); - } - } - } - - protected class ExpungeTask implements Runnable { - public ExpungeTask() { - } - - @Override - public void run() { - GlobalLock scanLock = GlobalLock.getInternLock("UserVMExpunge"); - try { - if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { - try { - List vms = _vmDao.findDestroyedVms(new Date(System.currentTimeMillis() - ((long) _expungeDelay << 10))); - if (s_logger.isInfoEnabled()) { - if (vms.size() == 0) { - s_logger.trace("Found " + vms.size() + " vms to expunge."); - } else { - s_logger.info("Found " + vms.size() + " vms to expunge."); - } - } - for (UserVmVO vm : vms) { - try { - expunge(vm, _accountMgr.getSystemUser().getId(), _accountMgr.getSystemAccount()); - } catch (Exception e) { - s_logger.warn("Unable to expunge " + vm, e); - } - } - } catch (Exception e) { - s_logger.error("Caught the following Exception", e); - } finally { - scanLock.unlock(); - } - } - } finally { - scanLock.releaseRef(); - } - } - } - - private static boolean isAdmin(short accountType) { - return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_UPDATE, eventDescription = "updating Vm") - public UserVm updateVirtualMachine(UpdateVMCmd cmd) { - String displayName = cmd.getDisplayName(); - String group = cmd.getGroup(); - Boolean ha = cmd.getHaEnable(); - Long id = cmd.getId(); - Long osTypeId = cmd.getOsTypeId(); - String userData = cmd.getUserData(); - - // Input validation - UserVmVO vmInstance = null; - - // Verify input parameters - vmInstance = _vmDao.findById(id.longValue()); - - if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find virtual machine with id " + id); - } - - ServiceOffering offering = _serviceOfferingDao.findById(vmInstance.getServiceOfferingId()); - if (!offering.getOfferHA() && ha != null && ha) { - throw new InvalidParameterValueException("Can't enable ha for the vm as it's created from the Service offering having HA disabled"); - } - - _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, vmInstance); - - if (displayName == null) { - displayName = vmInstance.getDisplayName(); - } - - if (ha == null) { - ha = vmInstance.isHaEnabled(); - } - - UserVmVO vm = _vmDao.findById(id); - if (vm == null) { - throw new CloudRuntimeException("Unable to find virual machine with id " + id); - } - - if (vm.getState() == State.Error || vm.getState() == State.Expunging) { - s_logger.error("vm is not in the right state: " + id); - throw new InvalidParameterValueException("Vm with id " + id + " is not in the right state"); - } - - if (userData != null) { - validateUserData(userData); - // update userData on domain router. - } else { - userData = vmInstance.getUserData(); - } - - String description = ""; - - if (displayName != vmInstance.getDisplayName()) { - description += "New display name: " + displayName + ". "; - } - - if (ha != vmInstance.isHaEnabled()) { - if (ha) { - description += "Enabled HA. "; - } else { - description += "Disabled HA. "; - } - } - if (osTypeId == null) { - osTypeId = vmInstance.getGuestOSId(); - } else { - description += "Changed Guest OS Type to " + osTypeId + ". "; - } - - if (group != null) { - if (addInstanceToGroup(id, group)) { - description += "Added to group: " + group + "."; - } - } - - _vmDao.updateVM(id, displayName, ha, osTypeId, userData); - - return _vmDao.findById(id); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) - public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - return startVirtualMachine(cmd.getId(), cmd.getHostId(), null).first(); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true) - public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException { - Account caller = UserContext.current().getCaller(); - Long vmId = cmd.getId(); - - // Verify input parameters - UserVmVO vmInstance = _vmDao.findById(vmId.longValue()); - if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - _accountMgr.checkAccess(caller, null, true, vmInstance); - - return rebootVirtualMachine(UserContext.current().getCallerUserId(), vmId); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_DESTROY, eventDescription = "destroying Vm", async = true) - public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException { - return destroyVm(cmd.getId()); - } - - @Override - @DB - public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { - Account caller = UserContext.current().getCaller(); - Long domainId = cmd.getDomainId(); - String accountName = cmd.getAccountName(); - String groupName = cmd.getGroupName(); - Long projectId = cmd.getProjectId(); - - Account owner = _accountMgr.finalizeOwner(caller, accountName, domainId, projectId); - long accountId = owner.getId(); - - // Check if name is already in use by this account - boolean isNameInUse = _vmGroupDao.isNameInUse(accountId, groupName); - - if (isNameInUse) { - throw new InvalidParameterValueException("Unable to create vm group, a group with name " + groupName + " already exisits for account " + accountId); - } - - return createVmGroup(groupName, accountId); - } - - @DB - protected InstanceGroupVO createVmGroup(String groupName, long accountId) { - Account account = null; - final Transaction txn = Transaction.currentTxn(); - txn.start(); - try { - account = _accountDao.acquireInLockTable(accountId); // to ensure duplicate vm group names are not created. - if (account == null) { - s_logger.warn("Failed to acquire lock on account"); - return null; - } - InstanceGroupVO group = _vmGroupDao.findByAccountAndName(accountId, groupName); - if (group == null) { - group = new InstanceGroupVO(groupName, accountId); - group = _vmGroupDao.persist(group); - } - return group; - } finally { - if (account != null) { - _accountDao.releaseFromLockTable(accountId); - } - txn.commit(); - } - } - - @Override - public boolean deleteVmGroup(DeleteVMGroupCmd cmd) { - Account caller = UserContext.current().getCaller(); - Long groupId = cmd.getId(); - - // Verify input parameters - InstanceGroupVO group = _vmGroupDao.findById(groupId); - if ((group == null) || (group.getRemoved() != null)) { - throw new InvalidParameterValueException("unable to find a vm group with id " + groupId); - } - - _accountMgr.checkAccess(caller, null, true, group); - - return deleteVmGroup(groupId); - } - - @Override - public boolean deleteVmGroup(long groupId) { - // delete all the mappings from group_vm_map table - List groupVmMaps = _groupVMMapDao.listByGroupId(groupId); - for (InstanceGroupVMMapVO groupMap : groupVmMaps) { - SearchCriteria sc = _groupVMMapDao.createSearchCriteria(); - sc.addAnd("instanceId", SearchCriteria.Op.EQ, groupMap.getInstanceId()); - _groupVMMapDao.expunge(sc); - } - - if (_vmGroupDao.remove(groupId)) { - return true; - } else { - return false; - } - } - - @Override - @DB - public boolean addInstanceToGroup(long userVmId, String groupName) { - UserVmVO vm = _vmDao.findById(userVmId); - - InstanceGroupVO group = _vmGroupDao.findByAccountAndName(vm.getAccountId(), groupName); - // Create vm group if the group doesn't exist for this account - if (group == null) { - group = createVmGroup(groupName, vm.getAccountId()); - } - - if (group != null) { - final Transaction txn = Transaction.currentTxn(); - txn.start(); - UserVm userVm = _vmDao.acquireInLockTable(userVmId); - if (userVm == null) { - s_logger.warn("Failed to acquire lock on user vm id=" + userVmId); - } - try { - // don't let the group be deleted when we are assigning vm to it. - InstanceGroupVO ngrpLock = _vmGroupDao.lockRow(group.getId(), false); - if (ngrpLock == null) { - s_logger.warn("Failed to acquire lock on vm group id=" + group.getId() + " name=" + group.getName()); - txn.rollback(); - return false; - } - - // Currently don't allow to assign a vm to more than one group - if (_groupVMMapDao.listByInstanceId(userVmId) != null) { - // Delete all mappings from group_vm_map table - List groupVmMaps = _groupVMMapDao.listByInstanceId(userVmId); - for (InstanceGroupVMMapVO groupMap : groupVmMaps) { - SearchCriteria sc = _groupVMMapDao.createSearchCriteria(); - sc.addAnd("instanceId", SearchCriteria.Op.EQ, groupMap.getInstanceId()); - _groupVMMapDao.expunge(sc); - } - } - InstanceGroupVMMapVO groupVmMapVO = new InstanceGroupVMMapVO(group.getId(), userVmId); - _groupVMMapDao.persist(groupVmMapVO); - - txn.commit(); - return true; - } finally { - if (userVm != null) { - _vmDao.releaseFromLockTable(userVmId); - } - } - } - return false; - } - - @Override - public InstanceGroupVO getGroupForVm(long vmId) { - // TODO - in future releases vm can be assigned to multiple groups; but currently return just one group per vm - try { - List groupsToVmMap = _groupVMMapDao.listByInstanceId(vmId); - - if (groupsToVmMap != null && groupsToVmMap.size() != 0) { - InstanceGroupVO group = _vmGroupDao.findById(groupsToVmMap.get(0).getGroupId()); - return group; - } else { - return null; - } - } catch (Exception e) { - s_logger.warn("Error trying to get group for a vm: ", e); - return null; - } - } - - @Override - public void removeInstanceFromInstanceGroup(long vmId) { - try { - List groupVmMaps = _groupVMMapDao.listByInstanceId(vmId); - for (InstanceGroupVMMapVO groupMap : groupVmMaps) { - SearchCriteria sc = _groupVMMapDao.createSearchCriteria(); - sc.addAnd("instanceId", SearchCriteria.Op.EQ, groupMap.getInstanceId()); - _groupVMMapDao.expunge(sc); - } - } catch (Exception e) { - s_logger.warn("Error trying to remove vm from group: ", e); - } - } - - protected boolean validPassword(String password) { - if (password == null || password.length() == 0) { - return false; - } - for (int i = 0; i < password.length(); i++) { - if (password.charAt(i) == ' ') { - return false; - } - } - return true; - } - - @Override - public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, Account owner, - String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map requestedIps, String defaultIp, String keyboard) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { - - Account caller = UserContext.current().getCaller(); - List networkList = new ArrayList(); - - // Verify that caller can perform actions in behalf of vm owner - _accountMgr.checkAccess(caller, null, true, owner); - - // Get default guest network in Basic zone - Network defaultNetwork = _networkMgr.getExclusiveGuestNetwork(zone.getId()); - - if (defaultNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default network to start a vm"); - } else { - networkList.add(_networkDao.findById(defaultNetwork.getId())); - } - - boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); - - if (securityGroupIdList != null && isVmWare) { - throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor"); - } else if (!isVmWare && _networkMgr.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkMgr.canAddDefaultSecurityGroup()) { - //add the default securityGroup only if no security group is specified - if(securityGroupIdList == null || securityGroupIdList.isEmpty()){ - if (securityGroupIdList == null) { - securityGroupIdList = new ArrayList(); - } - SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(owner.getId()); - if (defaultGroup != null) { - securityGroupIdList.add(defaultGroup.getId()); - } else { - //create default security group for the account - if (s_logger.isDebugEnabled()) { - s_logger.debug("Couldn't find default security group for the account " + owner + " so creating a new one"); - } - defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, owner.getDomainId(), owner.getId(), owner.getAccountName()); - securityGroupIdList.add(defaultGroup.getId()); - } - } - } - - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, - diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard); - } - - @Override - public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, - List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, - String sshKeyPair, Map requestedIps, String defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, - ResourceAllocationException { - - Account caller = UserContext.current().getCaller(); - List networkList = new ArrayList(); - boolean isSecurityGroupEnabledNetworkUsed = false; - boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); - - //Verify that caller can perform actions in behalf of vm owner - _accountMgr.checkAccess(caller, null, true, owner); - - // If no network is specified, find system security group enabled network - if (networkIdList == null || networkIdList.isEmpty()) { - NetworkVO networkWithSecurityGroup = _networkMgr.getNetworkWithSecurityGroupEnabled(zone.getId()); - if (networkWithSecurityGroup == null) { - throw new InvalidParameterValueException("No network with security enabled is found in zone id=" + zone.getId()); - } - - networkList.add(networkWithSecurityGroup); - isSecurityGroupEnabledNetworkUsed = true; - - } else if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { - if (isVmWare) { - throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor"); - } - // Only one network can be specified, and it should be security group enabled - if (networkIdList.size() > 1) { - throw new InvalidParameterValueException("Only support one network per VM if security group enabled"); - } - - NetworkVO network = _networkDao.findById(networkIdList.get(0).longValue()); - - if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); - } - - if (!_networkMgr.isSecurityGroupSupportedInNetwork(network)) { - throw new InvalidParameterValueException("Network is not security group enabled: " + network.getId()); - } - - networkList.add(network); - isSecurityGroupEnabledNetworkUsed = true; - - } else { - // Verify that all the networks are Direct/Guest/AccountSpecific; can't create combination of SG enabled network and - // regular networks - for (Long networkId : networkIdList) { - NetworkVO network = _networkDao.findById(networkId); - - if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); - } - - boolean isSecurityGroupEnabled = _networkMgr.isSecurityGroupSupportedInNetwork(network); - if (isSecurityGroupEnabled && networkIdList.size() > 1) { - throw new InvalidParameterValueException("Can't create a vm with multiple networks one of which is Security Group enabled"); - } - - if (network.getTrafficType() != TrafficType.Guest || network.getGuestType() != Network.GuestType.Shared || (network.getGuestType() == Network.GuestType.Shared && !isSecurityGroupEnabled)) { - throw new InvalidParameterValueException("Can specify only Direct Guest Account specific networks when deploy vm in Security Group enabled zone"); - } - - // Perform account permission check - if (network.getGuestType() != Network.GuestType.Shared) { - // Check account permissions - List networkMap = _networkDao.listBy(owner.getId(), network.getId()); - if (networkMap == null || networkMap.isEmpty()) { - throw new PermissionDeniedException("Unable to create a vm using network with id " + network.getId() + ", permission denied"); - } - } - - networkList.add(network); - } - } - - // if network is security group enabled, and no security group is specified, then add the default security group automatically - if (isSecurityGroupEnabledNetworkUsed && !isVmWare && _networkMgr.canAddDefaultSecurityGroup()) { - - //add the default securityGroup only if no security group is specified - if(securityGroupIdList == null || securityGroupIdList.isEmpty()){ - if (securityGroupIdList == null) { - securityGroupIdList = new ArrayList(); - } - - SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(owner.getId()); - if (defaultGroup != null) { - securityGroupIdList.add(defaultGroup.getId()); - } else { - //create default security group for the account - if (s_logger.isDebugEnabled()) { - s_logger.debug("Couldn't find default security group for the account " + owner + " so creating a new one"); - } - defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, owner.getDomainId(), owner.getId(), owner.getAccountName()); - securityGroupIdList.add(defaultGroup.getId()); - } - } - } - - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, - diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard); - } - - @Override - public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, String hostName, - String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair, Map requestedIps, String defaultIp, String keyboard) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { - - Account caller = UserContext.current().getCaller(); - List networkList = new ArrayList(); - - // Verify that caller can perform actions in behalf of vm owner - _accountMgr.checkAccess(caller, null, true, owner); - - List vpcSupportedHTypes = _vpcMgr.getSupportedVpcHypervisors(); - if (networkIdList == null || networkIdList.isEmpty()) { - NetworkVO defaultNetwork = null; - - // if no network is passed in - // Check if default virtual network offering has Availability=Required. If it's true, search for corresponding - // network - // * if network is found, use it. If more than 1 virtual network is found, throw an error - // * if network is not found, create a new one and use it - - List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); - if (requiredOfferings.size() < 1) { - throw new InvalidParameterValueException("Unable to find network offering with availability=" + Availability.Required + " to automatically create the network as a part of vm creation"); - } - - if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { - // get Virtual networks - List virtualNetworks = _networkMgr.listNetworksForAccount(owner.getId(), zone.getId(), Network.GuestType.Isolated); - - if (virtualNetworks.isEmpty()) { - long physicalNetworkId = _networkMgr.findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); - // Validate physical network - PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); - if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: "+physicalNetworkId + " and tag: " +requiredOfferings.get(0).getTags()); - } - s_logger.debug("Creating network for account " + owner + " from the network offering id=" +requiredOfferings.get(0).getId() + " as a part of deployVM process"); - Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), - owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null, - null, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null); - defaultNetwork = _networkDao.findById(newNetwork.getId()); - } else if (virtualNetworks.size() > 1) { - throw new InvalidParameterValueException("More than 1 default Isolated networks are found for account " + owner + "; please specify networkIds"); - } else { - defaultNetwork = virtualNetworks.get(0); - } - } else { - throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); - } - - networkList.add(defaultNetwork); - - } else { - for (Long networkId : networkIdList) { - NetworkVO network = _networkDao.findById(networkId); - if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); - } - if (network.getVpcId() != null) { - //Only ISOs, XenServer, KVM, and VmWare template types are supported for vpc networks - if (template.getFormat() != ImageFormat.ISO && !vpcSupportedHTypes.contains(template.getHypervisorType())) { - throw new InvalidParameterValueException("Can't create vm from template with hypervisor " - + template.getHypervisorType() + " in vpc network " + network); - } - - //Only XenServer, KVM, and VMware hypervisors are supported for vpc networks - if (!vpcSupportedHTypes.contains(hypervisor)) { - throw new InvalidParameterValueException("Can't create vm of hypervisor type " + hypervisor + " in vpc network"); - } - - } - - _networkMgr.checkNetworkPermissions(owner, network); - - //don't allow to use system networks - NetworkOffering networkOffering = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); - if (networkOffering.isSystemOnly()) { - throw new InvalidParameterValueException("Network id=" + networkId + " is system only and can't be used for vm deployment"); - } - networkList.add(network); - } - } - - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIp, keyboard); - } - - @DB @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true) - protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, String hostName, String displayName, Account owner, Long diskOfferingId, - Long diskSize, List networkList, List securityGroupIdList, String group, String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, String defaultNetworkIp, String keyboard) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { - - _accountMgr.checkAccess(caller, null, true, owner); - - if (owner.getState() == Account.State.disabled) { - throw new PermissionDeniedException("The owner of vm to deploy is disabled: " + owner); - } - - long accountId = owner.getId(); - - assert !(requestedIps != null && defaultNetworkIp != null) : "requestedIp list and defaultNetworkIp should never be specified together"; - - if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { - throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getId()); - } - - if (zone.getDomainId() != null) { - DomainVO domain = _domainDao.findById(zone.getDomainId()); - if (domain == null) { - throw new CloudRuntimeException("Unable to find the domain " + zone.getDomainId() + " for the zone: " + zone); - } - // check that caller can operate with domain - _configMgr.checkZoneAccess(caller, zone); - // check that vm owner can create vm in the domain - _configMgr.checkZoneAccess(owner, zone); - } - - // check if account/domain is with in resource limits to create a new vm - boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2)); - - //verify security group ids - if (securityGroupIdList != null) { - for (Long securityGroupId : securityGroupIdList) { - SecurityGroup sg = _securityGroupDao.findById(securityGroupId); - if (sg == null) { - throw new InvalidParameterValueException("Unable to find security group by id " + securityGroupId); - } else { - //verify permissions - _accountMgr.checkAccess(caller, null, true, owner, sg); - } - } - } - - // check if we have available pools for vm deployment - long availablePools = _storagePoolDao.countPoolsByStatus(StoragePoolStatus.Up); - if (availablePools < 1) { - throw new StorageUnavailableException("There are no available pools in the UP state for vm deployment", -1); - } - - ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOffering.getId()); - - if (template.getTemplateType().equals(TemplateType.SYSTEM)) { - throw new InvalidParameterValueException("Unable to use system template " + template.getId() + " to deploy a user vm"); - } - List listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId()); - if (listZoneTemplate == null || listZoneTemplate.isEmpty()) { - throw new InvalidParameterValueException("The template " + template.getId() + " is not available for use"); - } - - if (isIso && !template.isBootable()) { - throw new InvalidParameterValueException("Installing from ISO requires an ISO that is bootable: " + template.getId()); - } - - // Check templates permissions - if (!template.isPublicTemplate()) { - Account templateOwner = _accountMgr.getAccount(template.getAccountId()); - _accountMgr.checkAccess(owner, null, true, templateOwner); - } - - // If the template represents an ISO, a disk offering must be passed in, and will be used to create the root disk - // Else, a disk offering is optional, and if present will be used to create the data disk - Pair rootDiskOffering = new Pair(null, null); - List> dataDiskOfferings = new ArrayList>(); - - if (isIso) { - if (diskOfferingId == null) { - throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); - } - DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); - if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); - } - Long size = null; - if (diskOffering.getDiskSize() == 0) { - size = diskSize; - if (size == null) { - throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); - } - } - rootDiskOffering.first(diskOffering); - rootDiskOffering.second(size); - } else { - rootDiskOffering.first(offering); - if (diskOfferingId != null) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); - if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); - } - Long size = null; - if (diskOffering.getDiskSize() == 0) { - size = diskSize; - if (size == null) { - throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); - } - } - dataDiskOfferings.add(new Pair(diskOffering, size)); - } - } - - //check if the user data is correct - validateUserData(userData); - - // Find an SSH public key corresponding to the key pair name, if one is given - String sshPublicKey = null; - if (sshKeyPair != null && !sshKeyPair.equals("")) { - SSHKeyPair pair = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), sshKeyPair); - if (pair == null) { - throw new InvalidParameterValueException("A key pair with name '" + sshKeyPair + "' was not found."); - } - - sshPublicKey = pair.getPublicKey(); - } - - List> networks = new ArrayList>(); - short defaultNetworkNumber = 0; - boolean securityGroupEnabled = false; - boolean vpcNetwork = false; - for (NetworkVO network : networkList) { - if (network.getDataCenterId() != zone.getId()) { - throw new InvalidParameterValueException("Network id=" + network.getId() + " doesn't belong to zone " + zone.getId()); - } - - String requestedIp = null; - if (requestedIps != null && !requestedIps.isEmpty()) { - requestedIp = requestedIps.get(network.getId()); - } - - NicProfile profile = new NicProfile(requestedIp); - - if (defaultNetworkNumber == 0) { - defaultNetworkNumber++; - // if user requested specific ip for default network, add it - if (defaultNetworkIp != null) { - profile = new NicProfile(defaultNetworkIp); - } - - profile.setDefaultNic(true); - } - - networks.add(new Pair(network, profile)); - - if (_networkMgr.isSecurityGroupSupportedInNetwork(network)) { - securityGroupEnabled = true; - } - - //vm can't be a part of more than 1 VPC network - if (network.getVpcId() != null) { - if (vpcNetwork) { - throw new InvalidParameterValueException("Vm can't be a part of more than 1 VPC network"); - } - vpcNetwork = true; - } - } - - if (securityGroupIdList != null && !securityGroupIdList.isEmpty() && !securityGroupEnabled) { - throw new InvalidParameterValueException("Unable to deploy vm with security groups as SecurityGroup service is not enabled for the vm's network"); - } - - // Verify network information - network default network has to be set; and vm can't have more than one default network - // This is a part of business logic because default network is required by Agent Manager in order to configure default - // gateway for the vm - if (defaultNetworkNumber == 0) { - throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); - } else if (defaultNetworkNumber > 1) { - throw new InvalidParameterValueException("Only 1 default network per vm is supported"); - } - - long id = _vmDao.getNextInSequence(Long.class, "id"); - - String instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); - - String uuidName = UUID.randomUUID().toString(); - - //verify hostname information - if (hostName == null) { - hostName = uuidName; - } else { - //1) check is hostName is RFC complient - if (!NetUtils.verifyDomainNameLabel(hostName, true)) { - throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); - } - //2) hostName has to be unique in the network domain - Map> ntwkDomains = new HashMap>(); - for (NetworkVO network : networkList) { - String ntwkDomain = network.getNetworkDomain(); - if (!ntwkDomains.containsKey(ntwkDomain)) { - List ntwkIds = new ArrayList(); - ntwkIds.add(network.getId()); - ntwkDomains.put(ntwkDomain, ntwkIds); - } else { - List ntwkIds = ntwkDomains.get(ntwkDomain); - ntwkIds.add(network.getId()); - ntwkDomains.put(ntwkDomain, ntwkIds); - } - } - - for (String ntwkDomain : ntwkDomains.keySet()) { - for (Long ntwkId : ntwkDomains.get(ntwkDomain)) { - //* get all vms hostNames in the network - List hostNames = _vmInstanceDao.listDistinctHostNames(ntwkId); - //* verify that there are no duplicates - if (hostNames.contains(hostName)) { - throw new InvalidParameterValueException("The vm with hostName " + hostName - + " already exists in the network domain: " + ntwkDomain + "; network=" - + _networkMgr.getNetwork(ntwkId)); - } - } - } - } - - HypervisorType hypervisorType = null; - if (template == null || template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) { - hypervisorType = hypervisor; - } else { - hypervisorType = template.getHypervisorType(); - } - Transaction txn = Transaction.currentTxn(); - txn.start(); - UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.getOfferHA(), offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), - offering.getId(), userData, hostName); - vm.setUuid(uuidName); - - if (sshPublicKey != null) { - vm.setDetail("SSH.PublicKey", sshPublicKey); - } - - if(keyboard != null && !keyboard.isEmpty()) - vm.setDetail(VmDetailConstants.KEYBOARD, keyboard); - - if (isIso) { - vm.setIsoId(template.getId()); - } - - s_logger.debug("Allocating in the DB for vm"); - DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); - - if (_itMgr.allocate(vm, _templateDao.findById(template.getId()), offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, hypervisorType, owner) == null) { - return null; - } - - _vmDao.saveDetails(vm); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Successfully allocated DB entry for " + vm); - } - UserContext.current().setEventDetails("Vm Id: " + vm.getId()); - - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString()); - _usageEventDao.persist(usageEvent); - - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm); - txn.commit(); - // Assign instance to the group - try { - if (group != null) { - boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); - if (!addToGroup) { - throw new CloudRuntimeException("Unable to assign Vm to the group " + group); - } - } - } catch (Exception ex) { - throw new CloudRuntimeException("Unable to assign Vm to the group " + group); - } - - _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList); - - return vm; - } - - private void validateUserData(String userData) { - byte[] decodedUserData = null; - if (userData != null) { - if (!Base64.isBase64(userData)) { - throw new InvalidParameterValueException("User data is not base64 encoded"); - } - if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { - throw new InvalidParameterValueException("User data is too long"); - } - decodedUserData = Base64.decodeBase64(userData.getBytes()); - if (decodedUserData.length > MAX_USER_DATA_LENGTH_BYTES) { - throw new InvalidParameterValueException("User data is too long"); - } - if (decodedUserData.length < 1) { - throw new InvalidParameterValueException("User data is too short"); - } - } - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "starting Vm", async = true) - public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { - return startVirtualMachine(cmd, null); - } - - protected UserVm startVirtualMachine(DeployVMCmd cmd, Map additonalParams) throws ResourceUnavailableException, InsufficientCapacityException, - ConcurrentOperationException { - - long vmId = cmd.getEntityId(); - Long hostId = cmd.getHostId(); - UserVmVO vm = _vmDao.findById(vmId); - - Pair> vmParamPair = null; - try { - vmParamPair = startVirtualMachine(vmId, hostId, additonalParams); - vm = vmParamPair.first();; - } finally { - updateVmStateForFailedVmCreation(vm.getId()); - } - - // Check that the password was passed in and is valid - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - if (template.getEnablePassword()) { - // this value is not being sent to the backend; need only for api display purposes - vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword)); - } - - return vm; - } - - @Override - public boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { - UserVmVO vm = profile.getVirtualMachine(); - Map details = _vmDetailsDao.findDetails(vm.getId()); - vm.setDetails(details); - - if (vm.getIsoId() != null) { - String isoPath = null; - - VirtualMachineTemplate template = _templateDao.findById(vm.getIsoId()); - if (template == null || template.getFormat() != ImageFormat.ISO) { - throw new CloudRuntimeException("Can not find ISO in vm_template table for id " + vm.getIsoId()); - } - - Pair isoPathPair = _storageMgr.getAbsoluteIsoPath(template.getId(), vm.getDataCenterIdToDeployIn()); - - if (template.getTemplateType() == TemplateType.PERHOST) { - isoPath = template.getName(); - } else { - if (isoPathPair == null) { - s_logger.warn("Couldn't get absolute iso path"); - return false; - } else { - isoPath = isoPathPair.first(); - } - } - - if (template.isBootable()) { - profile.setBootLoaderType(BootloaderType.CD); - } - GuestOSVO guestOS = _guestOSDao.findById(template.getGuestOSId()); - String displayName = null; - if (guestOS != null) { - displayName = guestOS.getDisplayName(); - } - VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, StoragePoolType.ISO, null, template.getName(), null, isoPath, 0, null, displayName); - - iso.setDeviceId(3); - profile.addDisk(iso); - } else { - VirtualMachineTemplate template = profile.getTemplate(); - /* create a iso placeholder */ - VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, StoragePoolType.ISO, null, template.getName(), null, null, 0, null); - iso.setDeviceId(3); - profile.addDisk(iso); - } - - return true; - } - - @Override - public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) { - UserVmVO userVm = profile.getVirtualMachine(); - List nics = _nicDao.listByVmId(userVm.getId()); - for (NicVO nic : nics) { - NetworkVO network = _networkDao.findById(nic.getNetworkId()); - if (network.getTrafficType() == TrafficType.Guest || network.getTrafficType() == TrafficType.Public) { - userVm.setPrivateIpAddress(nic.getIp4Address()); - userVm.setPrivateMacAddress(nic.getMacAddress()); - } - } - return true; - } - - @Override - public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile profile) { - return true; - } - - @Override - public boolean finalizeStart(VirtualMachineProfile profile, long hostId, Commands cmds, ReservationContext context){ - UserVmVO vm = profile.getVirtualMachine(); - - Answer[] answersToCmds = cmds.getAnswers(); - if(answersToCmds == null){ - if(s_logger.isDebugEnabled()){ - s_logger.debug("Returning from finalizeStart() since there are no answers to read"); - } - return true; - } - Answer startAnswer = cmds.getAnswer(StartAnswer.class); - String returnedIp = null; - String originalIp = null; - if (startAnswer != null) { - StartAnswer startAns = (StartAnswer) startAnswer; - VirtualMachineTO vmTO = startAns.getVirtualMachine(); - for (NicTO nicTO: vmTO.getNics()) { - if (nicTO.getType() == TrafficType.Guest) { - returnedIp = nicTO.getIp(); - } - } - } - - List nics = _nicDao.listByVmId(vm.getId()); - NicVO guestNic = null; - NetworkVO guestNetwork = null; - for (NicVO nic : nics) { - NetworkVO network = _networkDao.findById(nic.getNetworkId()); - long isDefault = (nic.isDefaultNic()) ? 1 : 0; - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm.getHostName(), network.getNetworkOfferingId(), null, isDefault); - _usageEventDao.persist(usageEvent); - if (network.getTrafficType() == TrafficType.Guest) { - originalIp = nic.getIp4Address(); - guestNic = nic; - guestNetwork = network; - } - } - boolean ipChanged = false; - if (originalIp != null && !originalIp.equalsIgnoreCase(returnedIp)) { - if (returnedIp != null && guestNic != null) { - guestNic.setIp4Address(returnedIp); - ipChanged = true; - } - } - if (returnedIp != null && !returnedIp.equalsIgnoreCase(originalIp)) { - if (guestNic != null) { - guestNic.setIp4Address(returnedIp); - ipChanged = true; - } - } - if (ipChanged) { - DataCenterVO dc = _dcDao.findById(vm.getDataCenterIdToDeployIn()); - UserVmVO userVm = profile.getVirtualMachine(); - //dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName()) - if (_ntwkSrvcDao.canProviderSupportServiceInNetwork(guestNetwork.getId(), Service.Dhcp, Provider.ExternalDhcpServer)){ - _nicDao.update(guestNic.getId(), guestNic); - userVm.setPrivateIpAddress(guestNic.getIp4Address()); - _vmDao.update(userVm.getId(), userVm); - - s_logger.info("Detected that ip changed in the answer, updated nic in the db with new ip " + returnedIp); - } - } - - //get system ip and create static nat rule for the vm - try { - _rulesMgr.getSystemIpAndEnableStaticNatForVm(profile.getVirtualMachine(), false); - } catch (Exception ex) { - s_logger.warn("Failed to get system ip and enable static nat for the vm " + profile.getVirtualMachine() + " due to exception ", ex); - return false; - } - - return true; - } - - @Override - public void finalizeExpunge(UserVmVO vm) { - } - - @Override - public UserVmVO persist(UserVmVO vm) { - return _vmDao.persist(vm); - } - - @Override - public UserVmVO findById(long id) { - return _vmDao.findById(id); - } - - @Override - public UserVmVO findByName(String name) { - if (!VirtualMachineName.isValidVmName(name)) { - return null; - } - return findById(VirtualMachineName.getVmId(name)); - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_STOP, eventDescription = "stopping Vm", async = true) - public UserVm stopVirtualMachine(long vmId, boolean forced) throws ConcurrentOperationException { - // Input validation - Account caller = UserContext.current().getCaller(); - Long userId = UserContext.current().getCallerUserId(); - - // if account is removed, return error - if (caller != null && caller.getRemoved() != null) { - throw new PermissionDeniedException("The account " + caller.getId() + " is removed"); - } - - UserVmVO vm = _vmDao.findById(vmId); - if (vm == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - _accountMgr.checkAccess(caller, null, true, vm); - UserVO user = _userDao.findById(userId); - - try { - _itMgr.advanceStop(vm, forced, user, caller); - } catch (ResourceUnavailableException e) { - throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e); - } catch (OperationTimedoutException e) { - throw new CloudRuntimeException("Unable to contact the agent to stop the virtual machine " + vm, e); - } - - return _vmDao.findById(vmId); - } - - @Override - public void finalizeStop(VirtualMachineProfile profile, StopAnswer answer) { - //release elastic IP here - IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(profile.getId()); - if (ip != null && ip.getSystem()) { - UserContext ctx = UserContext.current(); - try { - _rulesMgr.disableStaticNat(ip.getId(), ctx.getCaller(), ctx.getCallerUserId(), true); - } catch (Exception ex) { - s_logger.warn("Failed to disable static nat and release system ip " + ip + " as a part of vm " + profile.getVirtualMachine() + " stop due to exception ", ex); - } - } - } - - public String generateRandomPassword() { - return PasswordGenerator.generateRandomPassword(6); - } - - @Override - public Pair> startVirtualMachine(long vmId, Long hostId, Map additionalParams) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - // Input validation - Account callerAccount = UserContext.current().getCaller(); - UserVO callerUser = _userDao.findById(UserContext.current().getCallerUserId()); - - // if account is removed, return error - if (callerAccount != null && callerAccount.getRemoved() != null) { - throw new InvalidParameterValueException("The account " + callerAccount.getId() + " is removed"); - } - - UserVmVO vm = _vmDao.findById(vmId); - if (vm == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - _accountMgr.checkAccess(callerAccount, null, true, vm); - - Account owner = _accountDao.findById(vm.getAccountId()); - - if (owner == null) { - throw new InvalidParameterValueException("The owner of " + vm + " does not exist: " + vm.getAccountId()); - } - - if (owner.getState() == Account.State.disabled) { - throw new PermissionDeniedException("The owner of " + vm + " is disabled: " + vm.getAccountId()); - } - - Host destinationHost = null; - if(hostId != null){ - Account account = UserContext.current().getCaller(); - if(!_accountService.isRootAdmin(account.getType())){ - throw new PermissionDeniedException("Parameter hostid can only be specified by a Root Admin, permission denied"); - } - destinationHost = _hostDao.findById(hostId); - if (destinationHost == null) { - throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + hostId); - } - } - - //check if vm is security group enabled - if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkMgr.canAddDefaultSecurityGroup()) { - //if vm is not mapped to security group, create a mapping - if (s_logger.isDebugEnabled()) { - s_logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); - } - - SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); - if (defaultSecurityGroup != null) { - List groupList = new ArrayList(); - groupList.add(defaultSecurityGroup.getId()); - _securityGroupMgr.addInstanceToGroups(vmId, groupList); - } - } - - DataCenterDeployment plan = null; - if (destinationHost != null) { - s_logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); - } - - //Set parameters - Map params = null; - VMTemplateVO template = null; - if (vm.isUpdateParameters()) { - _vmDao.loadDetails(vm); - // Check that the password was passed in and is valid - template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - - String password = "saved_password"; - if (template.getEnablePassword()) { - password = generateRandomPassword(); - } - - if (!validPassword(password)) { - throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); - } - - // Check if an SSH key pair was selected for the instance and if so use it to encrypt & save the vm password - String sshPublicKey = vm.getDetail("SSH.PublicKey"); - if (sshPublicKey != null && !sshPublicKey.equals("") && password != null && !password.equals("saved_password")) { - String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey(sshPublicKey, password); - if (encryptedPasswd == null) { - throw new CloudRuntimeException("Error encrypting password"); - } - - vm.setDetail("Encrypted.Password", encryptedPasswd); - _vmDao.saveDetails(vm); - } - - params = new HashMap(); - if (additionalParams != null) { - params.putAll(additionalParams); - } - params.put(VirtualMachineProfile.Param.VmPassword, password); - } - - vm = _itMgr.start(vm, params, callerUser, callerAccount, plan); - - Pair> vmParamPair = new Pair(vm, params); - if (vm != null && vm.isUpdateParameters()) { - // this value is not being sent to the backend; need only for api display purposes - if (template.getEnablePassword()) { - vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword)); - vm.setUpdateParameters(false); - _vmDao.update(vm.getId(), vm); - } - } - - return vmParamPair; - } - - @Override - public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { - Account caller = UserContext.current().getCaller(); - Long userId = UserContext.current().getCallerUserId(); - - // Verify input parameters - UserVmVO vm = _vmDao.findById(vmId); - if (vm == null || vm.getRemoved() != null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find a virtual machine with specified vmId"); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - - if (vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { - s_logger.trace("Vm id=" + vmId + " is already destroyed"); - return vm; - } - - _accountMgr.checkAccess(caller, null, true, vm); - User userCaller = _userDao.findById(userId); - - boolean status; - State vmState = vm.getState(); - - try { - status = _itMgr.destroy(vm, userCaller, caller); - } catch (OperationTimedoutException e) { - CloudRuntimeException ex = new CloudRuntimeException("Unable to destroy with specified vmId", e); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - - if (status) { - // Mark the account's volumes as destroyed - List volumes = _volsDao.findByInstance(vmId); - for (VolumeVO volume : volumes) { - if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName()); - _usageEventDao.persist(usageEvent); - } - } - - if (vmState != State.Error) { - _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.user_vm); - } - - return _vmDao.findById(vmId); - } else { - CloudRuntimeException ex = new CloudRuntimeException("Failed to destroy vm with specified vmId"); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - } - - @Override - public List searchForUserVMs(ListVMsCmd cmd) { - Account caller = UserContext.current().getCaller(); - List permittedAccounts = new ArrayList(); - String hypervisor = cmd.getHypervisor(); - boolean listAll = cmd.listAll(); - Long id = cmd.getId(); - Map tags = cmd.getTags(); - - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); - _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, listAll, false); - Long domainId = domainIdRecursiveListProject.first(); - Boolean isRecursive = domainIdRecursiveListProject.second(); - ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); - - Criteria c = new Criteria("id", Boolean.TRUE, cmd.getStartIndex(), cmd.getPageSizeVal()); - c.addCriteria(Criteria.KEYWORD, cmd.getKeyword()); - c.addCriteria(Criteria.ID, cmd.getId()); - c.addCriteria(Criteria.NAME, cmd.getInstanceName()); - c.addCriteria(Criteria.STATE, cmd.getState()); - c.addCriteria(Criteria.DATACENTERID, cmd.getZoneId()); - c.addCriteria(Criteria.GROUPID, cmd.getGroupId()); - c.addCriteria(Criteria.FOR_VIRTUAL_NETWORK, cmd.getForVirtualNetwork()); - c.addCriteria(Criteria.NETWORKID, cmd.getNetworkId()); - c.addCriteria(Criteria.TEMPLATE_ID, cmd.getTemplateId()); - c.addCriteria(Criteria.ISO_ID, cmd.getIsoId()); - c.addCriteria(Criteria.VPC_ID, cmd.getVpcId()); - - if (domainId != null) { - c.addCriteria(Criteria.DOMAINID, domainId); - } - - if (HypervisorType.getType(hypervisor) != HypervisorType.None) { - c.addCriteria(Criteria.HYPERVISOR, hypervisor); - } else if (hypervisor != null) { - throw new InvalidParameterValueException("Invalid HypervisorType " + hypervisor); - } - - // ignore these search requests if it's not an admin - if (_accountMgr.isAdmin(caller.getType())) { - c.addCriteria(Criteria.PODID, cmd.getPodId()); - c.addCriteria(Criteria.HOSTID, cmd.getHostId()); - c.addCriteria(Criteria.STORAGE_ID, cmd.getStorageId()); - } - - if (!permittedAccounts.isEmpty()) { - c.addCriteria(Criteria.ACCOUNTID, permittedAccounts.toArray()); - } - c.addCriteria(Criteria.ISADMIN, _accountMgr.isAdmin(caller.getType())); - - return searchForUserVMs(c, caller, domainId, isRecursive, permittedAccounts, listAll, listProjectResourcesCriteria, tags); - } - - @Override - public List searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive, - List permittedAccounts, boolean listAll, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags) { - Filter searchFilter = new Filter(UserVmVO.class, c.getOrderBy(), c.getAscending(), c.getOffset(), c.getLimit()); - - SearchBuilder sb = _vmDao.createSearchBuilder(); - _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - - Object id = c.getCriteria(Criteria.ID); - Object name = c.getCriteria(Criteria.NAME); - Object state = c.getCriteria(Criteria.STATE); - Object notState = c.getCriteria(Criteria.NOTSTATE); - Object zone = c.getCriteria(Criteria.DATACENTERID); - Object pod = c.getCriteria(Criteria.PODID); - Object hostId = c.getCriteria(Criteria.HOSTID); - Object hostName = c.getCriteria(Criteria.HOSTNAME); - Object keyword = c.getCriteria(Criteria.KEYWORD); - Object isAdmin = c.getCriteria(Criteria.ISADMIN); - assert c.getCriteria(Criteria.IPADDRESS) == null : "We don't support search by ip address on VM any more. If you see this assert, it means we have to find a different way to search by the nic table."; - Object groupId = c.getCriteria(Criteria.GROUPID); - Object networkId = c.getCriteria(Criteria.NETWORKID); - Object hypervisor = c.getCriteria(Criteria.HYPERVISOR); - Object storageId = c.getCriteria(Criteria.STORAGE_ID); - Object templateId = c.getCriteria(Criteria.TEMPLATE_ID); - Object isoId = c.getCriteria(Criteria.ISO_ID); - Object vpcId = c.getCriteria(Criteria.VPC_ID); - - sb.and("displayName", sb.entity().getDisplayName(), SearchCriteria.Op.LIKE); - sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); - sb.and("name", sb.entity().getHostName(), SearchCriteria.Op.LIKE); - sb.and("stateEQ", sb.entity().getState(), SearchCriteria.Op.EQ); - sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ); - sb.and("stateNIN", sb.entity().getState(), SearchCriteria.Op.NIN); - sb.and("dataCenterId", sb.entity().getDataCenterIdToDeployIn(), SearchCriteria.Op.EQ); - sb.and("podId", sb.entity().getPodIdToDeployIn(), SearchCriteria.Op.EQ); - sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); - sb.and("hostIdEQ", sb.entity().getHostId(), SearchCriteria.Op.EQ); - sb.and("hostIdIN", sb.entity().getHostId(), SearchCriteria.Op.IN); - sb.and("templateId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); - sb.and("isoId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); - - if (groupId != null && (Long) groupId == -1) { - SearchBuilder vmSearch = _groupVMMapDao.createSearchBuilder(); - vmSearch.and("instanceId", vmSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - sb.join("vmSearch", vmSearch, sb.entity().getId(), vmSearch.entity().getInstanceId(), JoinBuilder.JoinType.LEFTOUTER); - } else if (groupId != null) { - SearchBuilder groupSearch = _groupVMMapDao.createSearchBuilder(); - groupSearch.and("groupId", groupSearch.entity().getGroupId(), SearchCriteria.Op.EQ); - sb.join("groupSearch", groupSearch, sb.entity().getId(), groupSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); - } - - if (tags != null && !tags.isEmpty()) { - SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); - for (int count=0; count < tags.size(); count++) { - tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ); - tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ); - tagSearch.cp(); - } - tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); - sb.groupBy(sb.entity().getId()); - sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); - } - - if (networkId != null) { - SearchBuilder nicSearch = _nicDao.createSearchBuilder(); - nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); - - SearchBuilder networkSearch = _networkDao.createSearchBuilder(); - networkSearch.and("networkId", networkSearch.entity().getId(), SearchCriteria.Op.EQ); - nicSearch.join("networkSearch", networkSearch, nicSearch.entity().getNetworkId(), networkSearch.entity().getId(), JoinBuilder.JoinType.INNER); - - sb.join("nicSearch", nicSearch, sb.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); - } - - if(vpcId != null && networkId == null){ - SearchBuilder nicSearch = _nicDao.createSearchBuilder(); - - SearchBuilder networkSearch = _networkDao.createSearchBuilder(); - nicSearch.join("networkSearch", networkSearch, nicSearch.entity().getNetworkId(), networkSearch.entity().getId(), JoinBuilder.JoinType.INNER); - - SearchBuilder vpcSearch = _vpcDao.createSearchBuilder(); - vpcSearch.and("vpcId", vpcSearch.entity().getId(), SearchCriteria.Op.EQ); - networkSearch.join("vpcSearch", vpcSearch, networkSearch.entity().getVpcId(), vpcSearch.entity().getId(), JoinBuilder.JoinType.INNER); - - sb.join("nicSearch", nicSearch, sb.entity().getId(), nicSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); - } - - if (storageId != null) { - SearchBuilder volumeSearch = _volsDao.createSearchBuilder(); - volumeSearch.and("poolId", volumeSearch.entity().getPoolId(), SearchCriteria.Op.EQ); - sb.join("volumeSearch", volumeSearch, sb.entity().getId(), volumeSearch.entity().getInstanceId(), JoinBuilder.JoinType.INNER); - } - - // populate the search criteria with the values passed in - SearchCriteria sc = sb.create(); - _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - - if (tags != null && !tags.isEmpty()) { - int count = 0; - sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.UserVm.toString()); - for (String key : tags.keySet()) { - sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key); - sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); - count++; - } - } - - if (groupId != null && (Long) groupId == -1) { - sc.setJoinParameters("vmSearch", "instanceId", (Object) null); - } else if (groupId != null) { - sc.setJoinParameters("groupSearch", "groupId", groupId); - } - - if (keyword != null) { - SearchCriteria ssc = _vmDao.createSearchCriteria(); - ssc.addOr("displayName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("hostName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("instanceName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("state", SearchCriteria.Op.EQ, keyword); - - sc.addAnd("displayName", SearchCriteria.Op.SC, ssc); - } - - if (id != null) { - sc.setParameters("id", id); - } - - if (templateId != null) { - sc.setParameters("templateId", templateId); - } - - if (isoId != null) { - sc.setParameters("isoId", isoId); - } - - if (networkId != null) { - sc.setJoinParameters("nicSearch", "networkId", networkId); - } - - if(vpcId != null && networkId == null){ - sc.setJoinParameters("vpcSearch", "vpcId", vpcId); - } - - if (name != null) { - sc.setParameters("name", "%" + name + "%"); - } - - if (state != null) { - if (notState != null && (Boolean) notState == true) { - sc.setParameters("stateNEQ", state); - } else { - sc.setParameters("stateEQ", state); - } - } - - if (hypervisor != null) { - sc.setParameters("hypervisorType", hypervisor); - } - - // Don't show Destroyed and Expunging vms to the end user - if ((isAdmin != null) && ((Boolean) isAdmin != true)) { - sc.setParameters("stateNIN", "Destroyed", "Expunging"); - } - - if (zone != null) { - sc.setParameters("dataCenterId", zone); - - if (state == null) { - sc.setParameters("stateNEQ", "Destroyed"); - } - } - if (pod != null) { - sc.setParameters("podId", pod); - - if (state == null) { - sc.setParameters("stateNEQ", "Destroyed"); - } - } - - if (hostId != null) { - sc.setParameters("hostIdEQ", hostId); - } else { - if (hostName != null) { - List hosts = _resourceMgr.listHostsByNameLike((String) hostName); - if (hosts != null & !hosts.isEmpty()) { - Long[] hostIds = new Long[hosts.size()]; - for (int i = 0; i < hosts.size(); i++) { - HostVO host = hosts.get(i); - hostIds[i] = host.getId(); - } - sc.setParameters("hostIdIN", (Object[]) hostIds); - } else { - return new ArrayList(); - } - } - } - - if (storageId != null) { - sc.setJoinParameters("volumeSearch", "poolId", storageId); - } - s_logger.debug("THE WHERE CLAUSE IS:" + sc.getWhereClause()); - return _vmDao.search(sc, searchFilter); - } - - @Override - public HypervisorType getHypervisorTypeOfUserVM(long vmId) { - UserVmVO userVm = _vmDao.findById(vmId); - if (userVm == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a virtual machine with specified id"); - ex.addProxyObject(userVm, vmId, "vmId"); - throw ex; - } - - return userVm.getHypervisorType(); - } - - @Override - public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, - ResourceAllocationException { - // TODO Auto-generated method stub - return null; - } - - @Override - public UserVm getUserVm(long vmId) { - return _vmDao.findById(vmId); - } - - @Override - public VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool) { - // access check - only root admin can migrate VM - Account caller = UserContext.current().getCaller(); - if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); - } - throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); - } - - VMInstanceVO vm = _vmInstanceDao.findById(vmId); - if (vm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId); - } - - if (vm.getState() != State.Stopped) { - InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Stopped, unable to migrate the vm having the specified id"); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - - if (vm.getType() != VirtualMachine.Type.User) { - throw new InvalidParameterValueException("can only do storage migration on user vm"); - } - - List vols = _volsDao.findByInstance(vm.getId()); - if (vols.size() > 1) { - throw new InvalidParameterValueException("Data disks attached to the vm, can not migrate. Need to dettach data disks at first"); - } - - HypervisorType destHypervisorType = _clusterDao.findById(destPool.getClusterId()).getHypervisorType(); - if (vm.getHypervisorType() != destHypervisorType) { - throw new InvalidParameterValueException("hypervisor is not compatible: dest: " + destHypervisorType.toString() + ", vm: " + vm.getHypervisorType().toString()); - } - VMInstanceVO migratedVm = _itMgr.storageMigration(vm, destPool); - return migratedVm; - - } - - private boolean isVMUsingLocalStorage(VMInstanceVO vm) - { - boolean usesLocalStorage = false; - ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); - if (svcOffering.getUseLocalStorage()) { - usesLocalStorage = true; - } else { - List volumes = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK); - for (VolumeVO vol : volumes) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); - if (diskOffering.getUseLocalStorage()) { - usesLocalStorage = true; - break; - } - } - } - return usesLocalStorage; - } - - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) - public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { - // access check - only root admin can migrate VM - Account caller = UserContext.current().getCaller(); - if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); - } - throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); - } - - VMInstanceVO vm = _vmInstanceDao.findById(vmId); - if (vm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId); - } - // business logic - if (vm.getState() != State.Running) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is not Running, unable to migrate the vm " + vm); - } - InvalidParameterValueException ex = new InvalidParameterValueException("VM is not Running, unable to migrate the vm with specified id"); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm)) { - if (s_logger.isDebugEnabled()) { - s_logger.debug(vm + " is not XenServer/VMware/KVM/Ovm, cannot migrate this VM."); - } - throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM only"); - } - - if (isVMUsingLocalStorage(vm)) { - if (s_logger.isDebugEnabled()) { - s_logger.debug(vm + " is using Local Storage, cannot migrate this VM."); - } - throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate"); - } - - //check if migrating to same host - long srcHostId = vm.getHostId(); - if(destinationHost.getId() == srcHostId){ - throw new InvalidParameterValueException("Cannot migrate VM, VM is already presnt on this host, please specify valid destination host to migrate the VM"); - } - - //check if host is UP - if(destinationHost.getStatus() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled){ - throw new InvalidParameterValueException("Cannot migrate VM, destination host is not in correct state, has status: "+destinationHost.getStatus() + ", state: " +destinationHost.getResourceState()); - } - - // call to core process - DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId()); - HostPodVO pod = _podDao.findById(destinationHost.getPodId()); - Cluster cluster = _clusterDao.findById(destinationHost.getClusterId()); - DeployDestination dest = new DeployDestination(dcVO, pod, cluster, destinationHost); - - //check max guest vm limit for the destinationHost - HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); - if(_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)){ - if (s_logger.isDebugEnabled()) { - s_logger.debug("Host name: " + destinationHost.getName() + ", hostId: "+ destinationHost.getId() +" already has max Running VMs(count includes system VMs), cannot migrate to this host"); - } - throw new VirtualMachineMigrationException("Destination host, hostId: "+ destinationHost.getId() +" already has max Running VMs(count includes system VMs), cannot migrate to this host"); - } - - VMInstanceVO migratedVm = _itMgr.migrate(vm, srcHostId, dest); - return migratedVm; - } - - @DB - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false) - public UserVm moveVMToUser(AssignVMCmd cmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { - // VERIFICATIONS and VALIDATIONS - - //VV 1: verify the two users - Account caller = UserContext.current().getCaller(); - if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN && caller.getType() != Account.ACCOUNT_TYPE_DOMAIN_ADMIN){ // only root admin can assign VMs - throw new InvalidParameterValueException("Only domain admins are allowed to assign VMs and not " + caller.getType()); - } - - //get and check the valid VM - UserVmVO vm = _vmDao.findById(cmd.getVmId()); - if (vm == null){ - throw new InvalidParameterValueException("There is no vm by that id " + cmd.getVmId()); - } else if (vm.getState() == State.Running) { // VV 3: check if vm is running - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is Running, unable to move the vm " + vm); - } - InvalidParameterValueException ex = new InvalidParameterValueException("VM is Running, unable to move the vm with specified vmId"); - ex.addProxyObject(vm, cmd.getVmId(), "vmId"); - throw ex; - } - - Account oldAccount = _accountService.getActiveAccountById(vm.getAccountId()); - if (oldAccount == null) { - throw new InvalidParameterValueException("Invalid account for VM " + vm.getAccountId() + " in domain."); - } - //don't allow to move the vm from the project - if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { - InvalidParameterValueException ex = new InvalidParameterValueException("Specified Vm id belongs to the project and can't be moved"); - ex.addProxyObject(vm, cmd.getVmId(), "vmId"); - throw ex; - } - Account newAccount = _accountService.getActiveAccountByName(cmd.getAccountName(), cmd.getDomainId()); - if (newAccount == null || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { - throw new InvalidParameterValueException("Invalid accountid=" + cmd.getAccountName() + " in domain " + cmd.getDomainId()); - } - - if (newAccount.getState() == Account.State.disabled) { - throw new InvalidParameterValueException("The new account owner " + cmd.getAccountName() + " is disabled."); - } - - // make sure the accounts are under same domain - if (oldAccount.getDomainId() != newAccount.getDomainId()){ - throw new InvalidParameterValueException("The account should be under same domain for moving VM between two accounts. Old owner domain =" + oldAccount.getDomainId() + - " New owner domain=" + newAccount.getDomainId()); - } - - // make sure the accounts are not same - if (oldAccount.getAccountId() == newAccount.getAccountId()){ - throw new InvalidParameterValueException("The account should be same domain for moving VM between two accounts. Account id =" + oldAccount.getAccountId()); - } - - - // don't allow to move the vm if there are existing PF/LB/Static Nat rules, or vm is assigned to static Nat ip - List pfrules = _portForwardingDao.listByVm(cmd.getVmId()); - if (pfrules != null && pfrules.size() > 0){ - throw new InvalidParameterValueException("Remove the Port forwarding rules for this VM before assigning to another user."); - } - List snrules = _rulesDao.listStaticNatByVmId(vm.getId()); - if (snrules != null && snrules.size() > 0){ - throw new InvalidParameterValueException("Remove the StaticNat rules for this VM before assigning to another user."); - } - List maps = _loadBalancerVMMapDao.listByInstanceId(vm.getId()); - if (maps != null && maps.size() > 0) { - throw new InvalidParameterValueException("Remove the load balancing rules for this VM before assigning to another user."); - } - // check for one on one nat - IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(cmd.getVmId()); - if (ip != null){ - if (ip.isOneToOneNat()){ - throw new InvalidParameterValueException("Remove the one to one nat rule for this VM for ip " + ip.toString()); - } - } - - DataCenterVO zone = _dcDao.findById(vm.getDataCenterIdToDeployIn()); - - //Remove vm from instance group - removeInstanceFromInstanceGroup(cmd.getVmId()); - - //VV 2: check if account/domain is with in resource limits to create a new vm - _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.user_vm); - - //VV 3: check if volumes are with in resource limits - _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.volume, _volsDao.findByInstance(cmd.getVmId()).size()); - - // VV 4: Check if new owner can use the vm template - VirtualMachineTemplate template = _templateDao.findById(vm.getTemplateId()); - if (!template.isPublicTemplate()) { - Account templateOwner = _accountMgr.getAccount(template.getAccountId()); - _accountMgr.checkAccess(newAccount, null, true, templateOwner); - } - - // VV 5: check the new account can create vm in the domain - DomainVO domain = _domainDao.findById(cmd.getDomainId()); - _accountMgr.checkAccess(newAccount, domain); - - Transaction txn = Transaction.currentTxn(); - txn.start(); - //generate destroy vm event for usage - _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), - vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString())); - // update resource counts - _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.user_vm); - - // OWNERSHIP STEP 1: update the vm owner - vm.setAccountId(newAccount.getAccountId()); - vm.setDomainId(cmd.getDomainId()); - _vmDao.persist(vm); - - // OS 2: update volume - List volumes = _volsDao.findByInstance(cmd.getVmId()); - for (VolumeVO volume : volumes) { - _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName())); - _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.volume); - volume.setAccountId(newAccount.getAccountId()); - _volsDao.persist(volume); - _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.volume); - _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize())); - //snapshots: mark these removed in db - List snapshots = _snapshotDao.listByVolumeIdIncludingRemoved(volume.getId()); - for (SnapshotVO snapshot: snapshots){ - _snapshotDao.remove(snapshot.getId()); - } - } - - _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.user_vm); - //generate usage events to account for this change - _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_CREATE, vm.getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), - vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString())); - - txn.commit(); - - VMInstanceVO vmoi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); - VirtualMachineProfileImpl vmOldProfile = new VirtualMachineProfileImpl(vmoi); - - // OS 3: update the network - List networkIdList = cmd.getNetworkIds(); - List securityGroupIdList = cmd.getSecurityGroupIdList(); - - if (zone.getNetworkType() == NetworkType.Basic) { - if (networkIdList != null && !networkIdList.isEmpty()) { - throw new InvalidParameterValueException("Can't move vm with network Ids; this is a basic zone VM"); - } - //cleanup the old security groups - _securityGroupMgr.removeInstanceFromGroups(cmd.getVmId()); - //cleanup the network for the oldOwner - _networkMgr.cleanupNics(vmOldProfile); - _networkMgr.expungeNics(vmOldProfile); - //security groups will be recreated for the new account, when the VM is started - List networkList = new ArrayList(); - - // Get default guest network in Basic zone - Network defaultNetwork = _networkMgr.getExclusiveGuestNetwork(zone.getId()); - - if (defaultNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default network to start a vm"); - } else { - networkList.add(_networkDao.findById(defaultNetwork.getId())); - } - - boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware); - - if (securityGroupIdList != null && isVmWare) { - throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor"); - } else if (!isVmWare && _networkMgr.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkMgr.canAddDefaultSecurityGroup()) { - if (securityGroupIdList == null) { - securityGroupIdList = new ArrayList(); - } - SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(newAccount.getId()); - if (defaultGroup != null) { - //check if security group id list already contains Default security group, and if not - add it - boolean defaultGroupPresent = false; - for (Long securityGroupId : securityGroupIdList) { - if (securityGroupId.longValue() == defaultGroup.getId()) { - defaultGroupPresent = true; - break; - } - } - - if (!defaultGroupPresent) { - securityGroupIdList.add(defaultGroup.getId()); - } - - } else { - //create default security group for the account - if (s_logger.isDebugEnabled()) { - s_logger.debug("Couldn't find default security group for the account " + newAccount + " so creating a new one"); - } - defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, newAccount.getDomainId(), newAccount.getId(), newAccount.getAccountName()); - securityGroupIdList.add(defaultGroup.getId()); - } - } - - - List> networks = new ArrayList>(); - NicProfile profile = new NicProfile(); - profile.setDefaultNic(true); - networks.add(new Pair(networkList.get(0), profile)); - - VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmi); - _networkMgr.allocate(vmProfile, networks); - - _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList); - - s_logger.debug("AssignVM: Basic zone, adding security groups no " + securityGroupIdList.size() + " to " + vm.getInstanceName() ); - } else { - if (zone.isSecurityGroupEnabled()) { - throw new InvalidParameterValueException("Not yet implemented for SecurityGroupEnabled advanced networks."); - } else { - if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { - throw new InvalidParameterValueException("Can't move vm with security groups; security group feature is not enabled in this zone"); - } - //cleanup the network for the oldOwner - _networkMgr.cleanupNics(vmOldProfile); - _networkMgr.expungeNics(vmOldProfile); - - Set applicableNetworks = new HashSet(); - - if (networkIdList != null && !networkIdList.isEmpty()){ - // add any additional networks - for (Long networkId : networkIdList) { - NetworkVO network = _networkDao.findById(networkId); - if (network == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find specified network id"); - ex.addProxyObject(network, networkId, "networkId"); - throw ex; - } - - _networkMgr.checkNetworkPermissions(newAccount, network); - - //don't allow to use system networks - NetworkOffering networkOffering = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); - if (networkOffering.isSystemOnly()) { - InvalidParameterValueException ex = new InvalidParameterValueException("Specified Network id is system only and can't be used for vm deployment"); - ex.addProxyObject(network, networkId, "networkId"); - throw ex; - } - applicableNetworks.add(network); - } - } - else { - NetworkVO defaultNetwork = null; - List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); - if (requiredOfferings.size() < 1) { - throw new InvalidParameterValueException("Unable to find network offering with availability=" - + Availability.Required + " to automatically create the network as a part of vm creation"); - } - if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { - // get Virtual networks - List virtualNetworks = _networkMgr.listNetworksForAccount(newAccount.getId(), zone.getId(), Network.GuestType.Isolated); - - if (virtualNetworks.isEmpty()) { - long physicalNetworkId = _networkMgr.findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); - // Validate physical network - PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); - if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: "+physicalNetworkId + " and tag: " +requiredOfferings.get(0).getTags()); - } - - s_logger.debug("Creating network for account " + newAccount + " from the network offering id=" + - requiredOfferings.get(0).getId() + " as a part of deployVM process"); - Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), - newAccount.getAccountName() + "-network", newAccount.getAccountName() + "-network", null, null, - null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null); - defaultNetwork = _networkDao.findById(newNetwork.getId()); - } else if (virtualNetworks.size() > 1) { - throw new InvalidParameterValueException("More than 1 default Isolated networks are found " + - "for account " + newAccount + "; please specify networkIds"); - } else { - defaultNetwork = virtualNetworks.get(0); - } - } else { - throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); - } - - applicableNetworks.add(defaultNetwork); - } - - // add the new nics - List> networks = new ArrayList>(); - int toggle=0; - for (NetworkVO appNet: applicableNetworks){ - NicProfile defaultNic = new NicProfile(); - if (toggle==0){ - defaultNic.setDefaultNic(true); - toggle++; - } - networks.add(new Pair(appNet, defaultNic)); - } - VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmi); - _networkMgr.allocate(vmProfile, networks); - s_logger.debug("AssignVM: Advance virtual, adding networks no " + networks.size() + " to " + vm.getInstanceName() ); - } //END IF NON SEC GRP ENABLED - } // END IF ADVANCED - s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + cmd.getAccountName()); - return vm; - } - - @Override - public UserVm restoreVM(RestoreVMCmd cmd) { - // Input validation - Account caller = UserContext.current().getCaller(); - Long userId = UserContext.current().getCallerUserId(); - UserVO user = _userDao.findById(userId); - boolean needRestart = false; - - long vmId = cmd.getVmId(); - UserVmVO vm = _vmDao.findById(vmId); - if (vm == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Cann not find VM with ID " + vmId); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - - Account owner = _accountDao.findById(vm.getAccountId()); - if (owner == null) { - throw new InvalidParameterValueException("The owner of " + vm + " does not exist: " + vm.getAccountId()); - } - - if (owner.getState() == Account.State.disabled) { - throw new PermissionDeniedException("The owner of " + vm + " is disabled: " + vm.getAccountId()); - } - - if (vm.getState() != VirtualMachine.State.Running && vm.getState() != VirtualMachine.State.Stopped) { - throw new CloudRuntimeException("Vm " + vmId + " currently in " + vm.getState() + " state, restore vm can only execute when VM in Running or Stopped"); - } - - if (vm.getState() == VirtualMachine.State.Running) { - needRestart = true; - } - - List rootVols = _volsDao.findByInstance(vmId); - if (rootVols.isEmpty()) { - InvalidParameterValueException ex = new InvalidParameterValueException("Can not find root volume for VM " + vmId); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - - VolumeVO root = rootVols.get(0); - long templateId = root.getTemplateId(); - VMTemplateVO template = _templateDao.findById(templateId); - if (template == null) { - InvalidParameterValueException ex = new InvalidParameterValueException("Cannot find template for specified volumeid and vmId"); - ex.addProxyObject(vm, vmId, "vmId"); - ex.addProxyObject(root, root.getId(), "volumeId"); - throw ex; - } - - if (needRestart) { - try { - _itMgr.stop(vm, user, caller); - } catch (ResourceUnavailableException e) { - s_logger.debug("Stop vm " + vmId + " failed", e); - CloudRuntimeException ex = new CloudRuntimeException("Stop vm failed for specified vmId"); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - } - - /* allocate a new volume from original template*/ - VolumeVO newVol = _storageMgr.allocateDuplicateVolume(root, null); - _volsDao.attachVolume(newVol.getId(), vmId, newVol.getDeviceId()); - - /* Detach and destory the old root volume */ - try { - _volsDao.detachVolume(root.getId()); - _storageMgr.destroyVolume(root); - } catch (ConcurrentOperationException e) { - s_logger.debug("Unable to delete old root volume " + root.getId() + ", user may manually delete it", e); - } - - if (needRestart) { - try { - _itMgr.start(vm, null, user, caller); - } catch (Exception e) { - s_logger.debug("Unable to start VM " + vmId, e); - CloudRuntimeException ex = new CloudRuntimeException("Unable to start VM with specified id" + e.getMessage()); - ex.addProxyObject(vm, vmId, "vmId"); - throw ex; - } - } - - s_logger.debug("Restore VM " + vmId + " with template " + root.getTemplateId() + " successfully"); - return vm; - } - - @Override - public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, - ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { - //not supported - throw new UnsupportedOperationException("Plug nic is not supported for vm of type " + vm.getType()); - } - - - @Override - public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, - ReservationContext context, DeployDestination dest) throws ConcurrentOperationException, ResourceUnavailableException { - //not supported - throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); - } - - @Override - public void prepareStop(VirtualMachineProfile profile) { - } - + private static final Logger s_logger = Logger + .getLogger(UserVmManagerImpl.class); + + private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 + // seconds + + @Inject + protected HostDao _hostDao = null; + @Inject + protected ServiceOfferingDao _offeringDao = null; + @Inject + protected DiskOfferingDao _diskOfferingDao = null; + @Inject + protected VMTemplateDao _templateDao = null; + @Inject + protected VMTemplateDetailsDao _templateDetailsDao = null; + @Inject + protected VMTemplateHostDao _templateHostDao = null; + @Inject + protected VMTemplateZoneDao _templateZoneDao = null; + @Inject + protected DomainDao _domainDao = null; + @Inject + protected UserVmDao _vmDao = null; + @Inject + protected VolumeDao _volsDao = null; + @Inject + protected DataCenterDao _dcDao = null; + @Inject + protected FirewallRulesDao _rulesDao = null; + @Inject + protected LoadBalancerVMMapDao _loadBalancerVMMapDao = null; + @Inject + protected PortForwardingRulesDao _portForwardingDao; + @Inject + protected IPAddressDao _ipAddressDao = null; + @Inject + protected HostPodDao _podDao = null; + @Inject + protected NetworkManager _networkMgr = null; + @Inject + protected StorageManager _storageMgr = null; + @Inject + protected SnapshotManager _snapshotMgr = null; + @Inject + protected AgentManager _agentMgr = null; + @Inject + protected ConfigurationManager _configMgr = null; + @Inject + protected AccountDao _accountDao = null; + @Inject + protected UserDao _userDao = null; + @Inject + protected SnapshotDao _snapshotDao = null; + @Inject + protected GuestOSDao _guestOSDao = null; + @Inject + protected HighAvailabilityManager _haMgr = null; + @Inject + protected AlertManager _alertMgr = null; + @Inject + protected AccountManager _accountMgr; + @Inject + protected AccountService _accountService; + @Inject + protected AsyncJobManager _asyncMgr; + @Inject + protected ClusterDao _clusterDao; + @Inject + protected StoragePoolDao _storagePoolDao; + @Inject + protected SecurityGroupManager _securityGroupMgr; + @Inject + protected ServiceOfferingDao _serviceOfferingDao; + @Inject + protected NetworkOfferingDao _networkOfferingDao; + @Inject + protected InstanceGroupDao _vmGroupDao; + @Inject + protected InstanceGroupVMMapDao _groupVMMapDao; + @Inject + protected VirtualMachineManager _itMgr; + @Inject + protected NetworkDao _networkDao; + @Inject + protected NicDao _nicDao; + @Inject + protected VpcDao _vpcDao; + @Inject + protected RulesManager _rulesMgr; + @Inject + protected LoadBalancingRulesManager _lbMgr; + @Inject + protected UsageEventDao _usageEventDao; + @Inject + protected SSHKeyPairDao _sshKeyPairDao; + @Inject + protected UserVmDetailsDao _vmDetailsDao; + @Inject + protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; + @Inject + protected SecurityGroupDao _securityGroupDao; + @Inject + protected CapacityManager _capacityMgr;; + @Inject + protected VMInstanceDao _vmInstanceDao; + @Inject + protected ResourceLimitService _resourceLimitMgr; + @Inject + protected FirewallManager _firewallMgr; + @Inject + protected ProjectManager _projectMgr; + @Inject + protected ResourceManager _resourceMgr; + @Inject + protected NetworkServiceMapDao _ntwkSrvcDao; + @Inject + SecurityGroupVMMapDao _securityGroupVMMapDao; + @Inject + protected ItWorkDao _workDao; + @Inject + protected VolumeHostDao _volumeHostDao; + @Inject + ResourceTagDao _resourceTagDao; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + VpcManager _vpcMgr; + @Inject + protected GuestOSCategoryDao _guestOSCategoryDao; + + protected ScheduledExecutorService _executor = null; + protected int _expungeInterval; + protected int _expungeDelay; + + protected String _name; + protected String _instance; + protected String _zone; + + private ConfigurationDao _configDao; + private int _createprivatetemplatefromvolumewait; + private int _createprivatetemplatefromsnapshotwait; + + @Inject + protected OrchestrationService _orchSrvc; + + @Override + public UserVmVO getVirtualMachine(long vmId) { + return _vmDao.findById(vmId); + } + + @Override + public List getVirtualMachines(long hostId) { + return _vmDao.listByHostId(hostId); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD, eventDescription = "resetting Vm password", async = true) + public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password) + throws ResourceUnavailableException, InsufficientCapacityException { + Account caller = UserContext.current().getCaller(); + Long vmId = cmd.getId(); + UserVmVO userVm = _vmDao.findById(cmd.getId()); + _vmDao.loadDetails(userVm); + + // Do parameters input validation + if (userVm == null) { + throw new InvalidParameterValueException( + "unable to find a virtual machine with id " + cmd.getId()); + } + + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm + .getTemplateId()); + if (template == null || !template.getEnablePassword()) { + throw new InvalidParameterValueException( + "Fail to reset password for the virtual machine, the template is not password enabled"); + } + + if (userVm.getState() == State.Error + || userVm.getState() == State.Expunging) { + s_logger.error("vm is not in the right state: " + vmId); + throw new InvalidParameterValueException("Vm with id " + vmId + + " is not in the right state"); + } + + _accountMgr.checkAccess(caller, null, true, userVm); + + boolean result = resetVMPasswordInternal(cmd, password); + + if (result) { + userVm.setPassword(password); + // update the password in vm_details table too + // Check if an SSH key pair was selected for the instance and if so + // use it to encrypt & save the vm password + String sshPublicKey = userVm.getDetail("SSH.PublicKey"); + if (sshPublicKey != null && !sshPublicKey.equals("") + && password != null && !password.equals("saved_password")) { + String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey( + sshPublicKey, password); + if (encryptedPasswd == null) { + throw new CloudRuntimeException("Error encrypting password"); + } + + userVm.setDetail("Encrypted.Password", encryptedPasswd); + _vmDao.saveDetails(userVm); + } + } else { + throw new CloudRuntimeException( + "Failed to reset password for the virtual machine "); + } + + return userVm; + } + + private boolean resetVMPasswordInternal(ResetVMPasswordCmd cmd, + String password) throws ResourceUnavailableException, + InsufficientCapacityException { + Long vmId = cmd.getId(); + Long userId = UserContext.current().getCallerUserId(); + VMInstanceVO vmInstance = _vmDao.findById(vmId); + + if (password == null || password.equals("")) { + return false; + } + + VMTemplateVO template = _templateDao + .findByIdIncludingRemoved(vmInstance.getTemplateId()); + if (template.getEnablePassword()) { + Nic defaultNic = _networkMgr.getDefaultNic(vmId); + if (defaultNic == null) { + s_logger.error("Unable to reset password for vm " + vmInstance + + " as the instance doesn't have default nic"); + return false; + } + + Network defaultNetwork = _networkDao.findById(defaultNic + .getNetworkId()); + NicProfile defaultNicProfile = new NicProfile(defaultNic, + defaultNetwork, null, null, null, + _networkMgr + .isSecurityGroupSupportedInNetwork(defaultNetwork), + _networkMgr.getNetworkTag(template.getHypervisorType(), + defaultNetwork)); + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl( + vmInstance); + vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, + password); + + UserDataServiceProvider element = _networkMgr + .getPasswordResetProvider(defaultNetwork); + if (element == null) { + throw new CloudRuntimeException( + "Can't find network element for " + + Service.UserData.getName() + + " provider needed for password reset"); + } + + boolean result = element.savePassword(defaultNetwork, + defaultNicProfile, vmProfile); + + // Need to reboot the virtual machine so that the password gets + // redownloaded from the DomR, and reset on the VM + if (!result) { + s_logger.debug("Failed to reset password for the virutal machine; no need to reboot the vm"); + return false; + } else { + if (vmInstance.getState() == State.Stopped) { + s_logger.debug("Vm " + + vmInstance + + " is stopped, not rebooting it as a part of password reset"); + return true; + } + + if (rebootVirtualMachine(userId, vmId) == null) { + s_logger.warn("Failed to reboot the vm " + vmInstance); + return false; + } else { + s_logger.debug("Vm " + + vmInstance + + " is rebooted successfully as a part of password reset"); + return true; + } + } + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Reset password called for a vm that is not using a password enabled template"); + } + return false; + } + } + + @Override + public boolean stopVirtualMachine(long userId, long vmId) { + boolean status = false; + if (s_logger.isDebugEnabled()) { + s_logger.debug("Stopping vm=" + vmId); + } + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null || vm.getRemoved() != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is either removed or deleted."); + } + return true; + } + + User user = _userDao.findById(userId); + Account account = _accountDao.findById(user.getAccountId()); + + try { + status = _itMgr.stop(vm, user, account); + } catch (ResourceUnavailableException e) { + s_logger.debug("Unable to stop due to ", e); + status = false; + } + + if (status) { + return status; + } else { + return status; + } + } + + private int getMaxDataVolumesSupported(UserVmVO vm) { + Long hostId = vm.getHostId(); + if (hostId == null) { + hostId = vm.getLastHostId(); + } + HostVO host = _hostDao.findById(hostId); + Integer maxDataVolumesSupported = null; + if (host != null) { + _hostDao.loadDetails(host); + maxDataVolumesSupported = _hypervisorCapabilitiesDao + .getMaxDataVolumesLimit(host.getHypervisorType(), + host.getDetail("product_version")); + } + if (maxDataVolumesSupported == null) { + maxDataVolumesSupported = 6; // 6 data disks by default if nothing + // is specified in + // 'hypervisor_capabilities' table + } + + return maxDataVolumesSupported.intValue(); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) + public Volume attachVolumeToVM(AttachVolumeCmd command) { + Long vmId = command.getVirtualMachineId(); + Long volumeId = command.getId(); + Long deviceId = command.getDeviceId(); + Account caller = UserContext.current().getCaller(); + + // Check that the volume ID is valid + VolumeVO volume = _volsDao.findById(volumeId); + // Check that the volume is a data volume + if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) { + throw new InvalidParameterValueException( + "Please specify a valid data volume."); + } + + // Check that the volume is not currently attached to any VM + if (volume.getInstanceId() != null) { + throw new InvalidParameterValueException( + "Please specify a volume that is not attached to any VM."); + } + + // Check that the volume is not destroyed + if (volume.getState() == Volume.State.Destroy) { + throw new InvalidParameterValueException( + "Please specify a volume that is not destroyed."); + } + + // Check that the virtual machine ID is valid and it's a user vm + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null || vm.getType() != VirtualMachine.Type.User) { + throw new InvalidParameterValueException( + "Please specify a valid User VM."); + } + + // Check that the VM is in the correct state + if (vm.getState() != State.Running && vm.getState() != State.Stopped) { + throw new InvalidParameterValueException( + "Please specify a VM that is either running or stopped."); + } + + // Check that the device ID is valid + if (deviceId != null) { + if (deviceId.longValue() == 0) { + throw new InvalidParameterValueException( + "deviceId can't be 0, which is used by Root device"); + } + } + + // Check that the number of data volumes attached to VM is less than + // that supported by hypervisor + List existingDataVolumes = _volsDao.findByInstanceAndType( + vmId, Volume.Type.DATADISK); + int maxDataVolumesSupported = getMaxDataVolumesSupported(vm); + if (existingDataVolumes.size() >= maxDataVolumesSupported) { + throw new InvalidParameterValueException( + "The specified VM already has the maximum number of data disks (" + + maxDataVolumesSupported + + "). Please specify another VM."); + } + + // Check that the VM and the volume are in the same zone + if (vm.getDataCenterIdToDeployIn() != volume.getDataCenterId()) { + throw new InvalidParameterValueException( + "Please specify a VM that is in the same zone as the volume."); + } + + // If local storage is disabled then attaching a volume with local disk + // offering not allowed + DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId()); + if (!dataCenter.isLocalStorageEnabled()) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume + .getDiskOfferingId()); + if (diskOffering.getUseLocalStorage()) { + throw new InvalidParameterValueException( + "Zone is not configured to use local storage but volume's disk offering " + + diskOffering.getName() + " uses it"); + } + } + + // permission check + _accountMgr.checkAccess(caller, null, true, volume, vm); + + // Check if volume is stored on secondary Storage. + boolean isVolumeOnSec = false; + VolumeHostVO volHostVO = _volumeHostDao.findByVolumeId(volume.getId()); + if (volHostVO != null) { + isVolumeOnSec = true; + if (!(volHostVO.getDownloadState() == Status.DOWNLOADED)) { + throw new InvalidParameterValueException( + "Volume is not uploaded yet. Please try this operation once the volume is uploaded"); + } + } + + if (!(Volume.State.Allocated.equals(volume.getState()) + || Volume.State.Ready.equals(volume.getState()) || Volume.State.UploadOp + .equals(volume.getState()))) { + throw new InvalidParameterValueException( + "Volume state must be in Allocated, Ready or in Uploaded state"); + } + + VolumeVO rootVolumeOfVm = null; + List rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, + Volume.Type.ROOT); + if (rootVolumesOfVm.size() != 1) { + throw new CloudRuntimeException( + "The VM " + + vm.getHostName() + + " has more than one ROOT volume and is in an invalid state."); + } else { + rootVolumeOfVm = rootVolumesOfVm.get(0); + } + + HypervisorType rootDiskHyperType = vm.getHypervisorType(); + + HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume + .getId()); + if (dataDiskHyperType != HypervisorType.None + && rootDiskHyperType != dataDiskHyperType) { + throw new InvalidParameterValueException( + "Can't attach a volume created by: " + dataDiskHyperType + + " to a " + rootDiskHyperType + " vm"); + } + + // allocate deviceId + List vols = _volsDao.findByInstance(vmId); + if (deviceId != null) { + if (deviceId.longValue() > 15 || deviceId.longValue() == 0 + || deviceId.longValue() == 3) { + throw new RuntimeException("deviceId should be 1,2,4-15"); + } + for (VolumeVO vol : vols) { + if (vol.getDeviceId().equals(deviceId)) { + throw new RuntimeException("deviceId " + deviceId + + " is used by VM " + vm.getHostName()); + } + } + } else { + // allocate deviceId here + List devIds = new ArrayList(); + for (int i = 1; i < 15; i++) { + devIds.add(String.valueOf(i)); + } + devIds.remove("3"); + for (VolumeVO vol : vols) { + devIds.remove(vol.getDeviceId().toString().trim()); + } + deviceId = Long.parseLong(devIds.iterator().next()); + } + + boolean createVolumeOnBackend = true; + if (rootVolumeOfVm.getState() == Volume.State.Allocated) { + createVolumeOnBackend = false; + if (isVolumeOnSec) { + throw new CloudRuntimeException( + "Cant attach uploaded volume to the vm which is not created. Please start it and then retry"); + } + } + + // create volume on the backend only when vm's root volume is allocated + if (createVolumeOnBackend) { + if (volume.getState().equals(Volume.State.Allocated) + || isVolumeOnSec) { + /* Need to create the volume */ + VMTemplateVO rootDiskTmplt = _templateDao.findById(vm + .getTemplateId()); + DataCenterVO dcVO = _dcDao.findById(vm + .getDataCenterIdToDeployIn()); + HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); + StoragePoolVO rootDiskPool = _storagePoolDao + .findById(rootVolumeOfVm.getPoolId()); + ServiceOfferingVO svo = _serviceOfferingDao.findById(vm + .getServiceOfferingId()); + DiskOfferingVO diskVO = _diskOfferingDao.findById(volume + .getDiskOfferingId()); + Long clusterId = (rootDiskPool == null ? null : rootDiskPool + .getClusterId()); + + if (!isVolumeOnSec) { + volume = _storageMgr.createVolume(volume, vm, + rootDiskTmplt, dcVO, pod, clusterId, svo, diskVO, + new ArrayList(), volume.getSize(), + rootDiskHyperType); + } else { + try { + // Format of data disk should be the same as root disk + if (!volHostVO + .getFormat() + .getFileExtension() + .equals(_storageMgr + .getSupportedImageFormatForCluster(rootDiskPool + .getClusterId()))) { + throw new InvalidParameterValueException( + "Failed to attach volume to VM since volumes format " + + volHostVO.getFormat() + .getFileExtension() + + " is not compatible with the vm hypervisor type"); + } + + // Check that there is some shared storage. + StoragePoolVO vmRootVolumePool = _storagePoolDao + .findById(rootVolumeOfVm.getPoolId()); + List sharedVMPools = _storagePoolDao + .findPoolsByTags( + vmRootVolumePool.getDataCenterId(), + vmRootVolumePool.getPodId(), + vmRootVolumePool.getClusterId(), null, + true); + if (sharedVMPools.size() == 0) { + throw new CloudRuntimeException( + "Cannot attach volume since there are no shared storage pools in the VM's cluster to copy the uploaded volume to."); + } + + volume = _storageMgr.copyVolumeFromSecToPrimary(volume, + vm, rootDiskTmplt, dcVO, pod, + rootDiskPool.getClusterId(), svo, diskVO, + new ArrayList(), + volume.getSize(), rootDiskHyperType); + } catch (NoTransitionException e) { + throw new CloudRuntimeException( + "Unable to transition the volume ", e); + } + } + + if (volume == null) { + throw new CloudRuntimeException( + "Failed to create volume when attaching it to VM: " + + vm.getHostName()); + } + } + + StoragePoolVO vmRootVolumePool = _storagePoolDao + .findById(rootVolumeOfVm.getPoolId()); + DiskOfferingVO volumeDiskOffering = _diskOfferingDao + .findById(volume.getDiskOfferingId()); + String[] volumeTags = volumeDiskOffering.getTagsArray(); + + boolean isVolumeOnSharedPool = !volumeDiskOffering + .getUseLocalStorage(); + StoragePoolVO sourcePool = _storagePoolDao.findById(volume + .getPoolId()); + List matchingVMPools = _storagePoolDao + .findPoolsByTags(vmRootVolumePool.getDataCenterId(), + vmRootVolumePool.getPodId(), + vmRootVolumePool.getClusterId(), volumeTags, + isVolumeOnSharedPool); + boolean moveVolumeNeeded = true; + if (matchingVMPools.size() == 0) { + String poolType; + if (vmRootVolumePool.getClusterId() != null) { + poolType = "cluster"; + } else if (vmRootVolumePool.getPodId() != null) { + poolType = "pod"; + } else { + poolType = "zone"; + } + throw new CloudRuntimeException( + "There are no storage pools in the VM's " + poolType + + " with all of the volume's tags (" + + volumeDiskOffering.getTags() + ")."); + } else { + long sourcePoolId = sourcePool.getId(); + Long sourcePoolDcId = sourcePool.getDataCenterId(); + Long sourcePoolPodId = sourcePool.getPodId(); + Long sourcePoolClusterId = sourcePool.getClusterId(); + for (StoragePoolVO vmPool : matchingVMPools) { + long vmPoolId = vmPool.getId(); + Long vmPoolDcId = vmPool.getDataCenterId(); + Long vmPoolPodId = vmPool.getPodId(); + Long vmPoolClusterId = vmPool.getClusterId(); + + // Moving a volume is not required if storage pools belongs + // to same cluster in case of shared volume or + // identical storage pool in case of local + if (sourcePoolDcId == vmPoolDcId + && sourcePoolPodId == vmPoolPodId + && sourcePoolClusterId == vmPoolClusterId + && (isVolumeOnSharedPool || sourcePoolId == vmPoolId)) { + moveVolumeNeeded = false; + break; + } + } + } + + if (moveVolumeNeeded) { + if (isVolumeOnSharedPool) { + // Move the volume to a storage pool in the VM's zone, pod, + // or cluster + try { + volume = _storageMgr.moveVolume(volume, + vmRootVolumePool.getDataCenterId(), + vmRootVolumePool.getPodId(), + vmRootVolumePool.getClusterId(), + dataDiskHyperType); + } catch (ConcurrentOperationException e) { + throw new CloudRuntimeException(e.toString()); + } + } else { + throw new CloudRuntimeException( + "Failed to attach local data volume " + + volume.getName() + + " to VM " + + vm.getDisplayName() + + " as migration of local data volume is not allowed"); + } + } + } + + AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor + .getCurrentExecutor(); + if (asyncExecutor != null) { + AsyncJobVO job = asyncExecutor.getJob(); + + if (s_logger.isInfoEnabled()) { + s_logger.info("Trying to attaching volume " + volumeId + + " to vm instance:" + vm.getId() + + ", update async job-" + job.getId() + + " progress status"); + } + + _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); + _asyncMgr.updateAsyncJobStatus(job.getId(), + BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); + } + + String errorMsg = "Failed to attach volume: " + volume.getName() + + " to VM: " + vm.getHostName(); + boolean sendCommand = (vm.getState() == State.Running); + AttachVolumeAnswer answer = null; + Long hostId = vm.getHostId(); + if (hostId == null) { + hostId = vm.getLastHostId(); + HostVO host = _hostDao.findById(hostId); + if (host != null + && host.getHypervisorType() == HypervisorType.VMware) { + sendCommand = true; + } + } + + if (sendCommand) { + StoragePoolVO volumePool = _storagePoolDao.findById(volume + .getPoolId()); + AttachVolumeCommand cmd = new AttachVolumeCommand(true, + vm.getInstanceName(), volume.getPoolType(), + volume.getFolder(), volume.getPath(), volume.getName(), + deviceId, volume.getChainInfo()); + cmd.setPoolUuid(volumePool.getUuid()); + + try { + answer = (AttachVolumeAnswer) _agentMgr.send(hostId, cmd); + } catch (Exception e) { + throw new CloudRuntimeException(errorMsg + " due to: " + + e.getMessage()); + } + } + + if (!sendCommand || (answer != null && answer.getResult())) { + // Mark the volume as attached + if (sendCommand) { + _volsDao.attachVolume(volume.getId(), vmId, + answer.getDeviceId()); + } else { + _volsDao.attachVolume(volume.getId(), vmId, deviceId); + } + return _volsDao.findById(volumeId); + } else { + if (answer != null) { + String details = answer.getDetails(); + if (details != null && !details.isEmpty()) { + errorMsg += "; " + details; + } + } + throw new CloudRuntimeException(errorMsg); + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) + public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { + Account caller = UserContext.current().getCaller(); + if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd + .getVirtualMachineId() == null) + || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd + .getVirtualMachineId() != null)) + || (cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd + .getVirtualMachineId() == null))) { + throw new InvalidParameterValueException( + "Please provide either a volume id, or a tuple(device id, instance id)"); + } + + Long volumeId = cmmd.getId(); + VolumeVO volume = null; + + if (volumeId != null) { + volume = _volsDao.findById(volumeId); + } else { + volume = _volsDao.findByInstanceAndDeviceId( + cmmd.getVirtualMachineId(), cmmd.getDeviceId()).get(0); + } + + Long vmId = null; + + if (cmmd.getVirtualMachineId() == null) { + vmId = volume.getInstanceId(); + } else { + vmId = cmmd.getVirtualMachineId(); + } + + // Check that the volume ID is valid + if (volume == null) { + throw new InvalidParameterValueException( + "Unable to find volume with ID: " + volumeId); + } + + // Permissions check + _accountMgr.checkAccess(caller, null, true, volume); + + // Check that the volume is a data volume + if (volume.getVolumeType() != Volume.Type.DATADISK) { + throw new InvalidParameterValueException( + "Please specify a data volume."); + } + + // Check that the volume is currently attached to a VM + if (vmId == null) { + throw new InvalidParameterValueException( + "The specified volume is not attached to a VM."); + } + + // Check that the VM is in the correct state + UserVmVO vm = _vmDao.findById(vmId); + if (vm.getState() != State.Running && vm.getState() != State.Stopped + && vm.getState() != State.Destroyed) { + throw new InvalidParameterValueException( + "Please specify a VM that is either running or stopped."); + } + + AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor + .getCurrentExecutor(); + if (asyncExecutor != null) { + AsyncJobVO job = asyncExecutor.getJob(); + + if (s_logger.isInfoEnabled()) { + s_logger.info("Trying to attaching volume " + volumeId + + "to vm instance:" + vm.getId() + + ", update async job-" + job.getId() + + " progress status"); + } + + _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); + _asyncMgr.updateAsyncJobStatus(job.getId(), + BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); + } + + String errorMsg = "Failed to detach volume: " + volume.getName() + + " from VM: " + vm.getHostName(); + boolean sendCommand = (vm.getState() == State.Running); + Answer answer = null; + + if (sendCommand) { + AttachVolumeCommand cmd = new AttachVolumeCommand(false, + vm.getInstanceName(), volume.getPoolType(), + volume.getFolder(), volume.getPath(), volume.getName(), + cmmd.getDeviceId() != null ? cmmd.getDeviceId() : volume + .getDeviceId(), volume.getChainInfo()); + + StoragePoolVO volumePool = _storagePoolDao.findById(volume + .getPoolId()); + cmd.setPoolUuid(volumePool.getUuid()); + + try { + answer = _agentMgr.send(vm.getHostId(), cmd); + } catch (Exception e) { + throw new CloudRuntimeException(errorMsg + " due to: " + + e.getMessage()); + } + } + + if (!sendCommand || (answer != null && answer.getResult())) { + // Mark the volume as detached + _volsDao.detachVolume(volume.getId()); + if (answer != null && answer instanceof AttachVolumeAnswer) { + volume.setChainInfo(((AttachVolumeAnswer) answer) + .getChainInfo()); + _volsDao.update(volume.getId(), volume); + } + + return _volsDao.findById(volumeId); + } else { + + if (answer != null) { + String details = answer.getDetails(); + if (details != null && !details.isEmpty()) { + errorMsg += "; " + details; + } + } + + throw new CloudRuntimeException(errorMsg); + } + } + + @Override + public boolean attachISOToVM(long vmId, long isoId, boolean attach) { + UserVmVO vm = _vmDao.findById(vmId); + + if (vm == null) { + return false; + } else if (vm.getState() != State.Running) { + return true; + } + String isoPath; + VMTemplateVO tmplt = _templateDao.findById(isoId); + if (tmplt == null) { + s_logger.warn("ISO: " + isoId + " does not exist"); + return false; + } + // Get the path of the ISO + Pair isoPathPair = null; + if (tmplt.getTemplateType() == TemplateType.PERHOST) { + isoPath = tmplt.getName(); + } else { + isoPathPair = _storageMgr.getAbsoluteIsoPath(isoId, + vm.getDataCenterIdToDeployIn()); + if (isoPathPair == null) { + s_logger.warn("Couldn't get absolute iso path"); + return false; + } else { + isoPath = isoPathPair.first(); + } + } + + String vmName = vm.getInstanceName(); + + HostVO host = _hostDao.findById(vm.getHostId()); + if (host == null) { + s_logger.warn("Host: " + vm.getHostId() + " does not exist"); + return false; + } + AttachIsoCommand cmd = new AttachIsoCommand(vmName, isoPath, attach); + if (isoPathPair != null) { + cmd.setStoreUrl(isoPathPair.second()); + } + Answer a = _agentMgr.easySend(vm.getHostId(), cmd); + + return (a != null && a.getResult()); + } + + private UserVm rebootVirtualMachine(long userId, long vmId) + throws InsufficientCapacityException, ResourceUnavailableException { + UserVmVO vm = _vmDao.findById(vmId); + User caller = _accountMgr.getActiveUser(userId); + Account owner = _accountMgr.getAccount(vm.getAccountId()); + + if (vm == null || vm.getState() == State.Destroyed + || vm.getState() == State.Expunging || vm.getRemoved() != null) { + s_logger.warn("Vm id=" + vmId + " doesn't exist"); + return null; + } + + if (vm.getState() == State.Running && vm.getHostId() != null) { + return _itMgr.reboot(vm, null, caller, owner); + } else { + s_logger.error("Vm id=" + vmId + + " is not in Running state, failed to reboot"); + return null; + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "upgrading Vm") + /* + * TODO: cleanup eventually - Refactored API call + */ + public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) { + Long vmId = cmd.getId(); + Long svcOffId = cmd.getServiceOfferingId(); + Account caller = UserContext.current().getCaller(); + + // Verify input parameters + UserVmVO vmInstance = _vmDao.findById(vmId); + if (vmInstance == null) { + throw new InvalidParameterValueException( + "unable to find a virtual machine with id " + vmId); + } + + _accountMgr.checkAccess(caller, null, true, vmInstance); + + // Check that the specified service offering ID is valid + _itMgr.checkIfCanUpgrade(vmInstance, svcOffId); + + _itMgr.upgradeVmDb(vmId, svcOffId); + + return _vmDao.findById(vmInstance.getId()); + } + + @Override + public HashMap getVirtualMachineStatistics(long hostId, + String hostName, List vmIds) throws CloudRuntimeException { + HashMap vmStatsById = new HashMap(); + + if (vmIds.isEmpty()) { + return vmStatsById; + } + + List vmNames = new ArrayList(); + + for (Long vmId : vmIds) { + UserVmVO vm = _vmDao.findById(vmId); + vmNames.add(vm.getInstanceName()); + } + + Answer answer = _agentMgr.easySend(hostId, new GetVmStatsCommand( + vmNames, _hostDao.findById(hostId).getGuid(), hostName)); + if (answer == null || !answer.getResult()) { + s_logger.warn("Unable to obtain VM statistics."); + return null; + } else { + HashMap vmStatsByName = ((GetVmStatsAnswer) answer) + .getVmStatsMap(); + + if (vmStatsByName == null) { + s_logger.warn("Unable to obtain VM statistics."); + return null; + } + + for (String vmName : vmStatsByName.keySet()) { + vmStatsById.put(vmIds.get(vmNames.indexOf(vmName)), + vmStatsByName.get(vmName)); + } + } + + return vmStatsById; + } + + @Override + @DB + public UserVm recoverVirtualMachine(RecoverVMCmd cmd) + throws ResourceAllocationException, CloudRuntimeException { + + Long vmId = cmd.getId(); + Account caller = UserContext.current().getCaller(); + + // Verify input parameters + UserVmVO vm = _vmDao.findById(vmId.longValue()); + + if (vm == null) { + throw new InvalidParameterValueException( + "unable to find a virtual machine with id " + vmId); + } + + // check permissions + _accountMgr.checkAccess(caller, null, true, vm); + + if (vm.getRemoved() != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find vm or vm is removed: " + vmId); + } + throw new InvalidParameterValueException("Unable to find vm by id " + + vmId); + } + + if (vm.getState() != State.Destroyed) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("vm is not in the right state: " + vmId); + } + throw new InvalidParameterValueException("Vm with id " + vmId + + " is not in the right state"); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Recovering vm " + vmId); + } + + Transaction txn = Transaction.currentTxn(); + AccountVO account = null; + txn.start(); + + account = _accountDao.lockRow(vm.getAccountId(), true); + + // if the account is deleted, throw error + if (account.getRemoved() != null) { + throw new CloudRuntimeException( + "Unable to recover VM as the account is deleted"); + } + + // First check that the maximum number of UserVMs for the given + // accountId will not be exceeded + _resourceLimitMgr.checkResourceLimit(account, ResourceType.user_vm); + + _haMgr.cancelDestroy(vm, vm.getHostId()); + + try { + if (!_itMgr.stateTransitTo(vm, + VirtualMachine.Event.RecoveryRequested, null)) { + s_logger.debug("Unable to recover the vm because it is not in the correct state: " + + vmId); + throw new InvalidParameterValueException( + "Unable to recover the vm because it is not in the correct state: " + + vmId); + } + } catch (NoTransitionException e) { + throw new InvalidParameterValueException( + "Unable to recover the vm because it is not in the correct state: " + + vmId); + } + + // Recover the VM's disks + List volumes = _volsDao.findByInstance(vmId); + for (VolumeVO volume : volumes) { + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + // Create an event + Long templateId = volume.getTemplateId(); + Long diskOfferingId = volume.getDiskOfferingId(); + Long offeringId = null; + if (diskOfferingId != null) { + DiskOfferingVO offering = _diskOfferingDao + .findById(diskOfferingId); + if (offering != null + && (offering.getType() == DiskOfferingVO.Type.Disk)) { + offeringId = offering.getId(); + } + } + UsageEventVO usageEvent = new UsageEventVO( + EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), + volume.getDataCenterId(), volume.getId(), + volume.getName(), offeringId, templateId, + volume.getSize()); + _usageEventDao.persist(usageEvent); + } + } + + _resourceLimitMgr.incrementResourceCount(account.getId(), + ResourceType.volume, new Long(volumes.size())); + + _resourceLimitMgr.incrementResourceCount(account.getId(), + ResourceType.user_vm); + + txn.commit(); + + return _vmDao.findById(vmId); + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _name = name; + + ComponentLocator locator = ComponentLocator.getCurrentLocator(); + _configDao = locator.getDao(ConfigurationDao.class); + if (_configDao == null) { + throw new ConfigurationException( + "Unable to get the configuration dao."); + } + + Map configs = _configDao.getConfiguration( + "AgentManager", params); + + _instance = configs.get("instance.name"); + if (_instance == null) { + _instance = "DEFAULT"; + } + + String value = _configDao + .getValue(Config.CreatePrivateTemplateFromVolumeWait.toString()); + _createprivatetemplatefromvolumewait = NumbersUtil.parseInt(value, + Integer.parseInt(Config.CreatePrivateTemplateFromVolumeWait + .getDefaultValue())); + + value = _configDao + .getValue(Config.CreatePrivateTemplateFromSnapshotWait + .toString()); + _createprivatetemplatefromsnapshotwait = NumbersUtil.parseInt(value, + Integer.parseInt(Config.CreatePrivateTemplateFromSnapshotWait + .getDefaultValue())); + + String workers = configs.get("expunge.workers"); + int wrks = NumbersUtil.parseInt(workers, 10); + + String time = configs.get("expunge.interval"); + _expungeInterval = NumbersUtil.parseInt(time, 86400); + if (_expungeInterval < 600) { + _expungeInterval = 600; + } + time = configs.get("expunge.delay"); + _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); + if (_expungeDelay < 600) { + _expungeDelay = 600; + } + _executor = Executors.newScheduledThreadPool(wrks, + new NamedThreadFactory("UserVm-Scavenger")); + + _itMgr.registerGuru(VirtualMachine.Type.User, this); + + VirtualMachine.State.getStateMachine().registerListener( + new UserVmStateListener(_usageEventDao, _networkDao, _nicDao)); + + s_logger.info("User VM Manager is configured."); + + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + _executor.scheduleWithFixedDelay(new ExpungeTask(), _expungeInterval, + _expungeInterval, TimeUnit.SECONDS); + return true; + } + + @Override + public boolean stop() { + _executor.shutdown(); + return true; + } + + protected UserVmManagerImpl() { + } + + public String getRandomPrivateTemplateName() { + return UUID.randomUUID().toString(); + } + + @Override + public Long convertToId(String vmName) { + if (!VirtualMachineName.isValidVmName(vmName, _instance)) { + return null; + } + return VirtualMachineName.getVmId(vmName); + } + + @Override + public boolean expunge(UserVmVO vm, long callerUserId, Account caller) { + UserContext ctx = UserContext.current(); + ctx.setAccountId(vm.getAccountId()); + + try { + // expunge the vm + if (!_itMgr.advanceExpunge(vm, _accountMgr.getSystemUser(), caller)) { + s_logger.info("Did not expunge " + vm); + return false; + } + + // Only if vm is not expunged already, cleanup it's resources + if (vm != null && vm.getRemoved() == null) { + // Cleanup vm resources - all the PF/LB/StaticNat rules + // associated with vm + s_logger.debug("Starting cleaning up vm " + vm + + " resources..."); + if (cleanupVmResources(vm.getId())) { + s_logger.debug("Successfully cleaned up vm " + vm + + " resources as a part of expunge process"); + } else { + s_logger.warn("Failed to cleanup resources as a part of vm " + + vm + " expunge"); + return false; + } + + _itMgr.remove(vm, _accountMgr.getSystemUser(), caller); + } + + return true; + + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to expunge " + vm, e); + return false; + } catch (OperationTimedoutException e) { + s_logger.warn("Operation time out on expunging " + vm, e); + return false; + } catch (ConcurrentOperationException e) { + s_logger.warn("Concurrent operations on expunging " + vm, e); + return false; + } + } + + private boolean cleanupVmResources(long vmId) { + boolean success = true; + // Remove vm from security groups + _securityGroupMgr.removeInstanceFromGroups(vmId); + + // Remove vm from instance group + removeInstanceFromInstanceGroup(vmId); + + // cleanup firewall rules + if (_firewallMgr.revokeFirewallRulesForVm(vmId)) { + s_logger.debug("Firewall rules are removed successfully as a part of vm id=" + + vmId + " expunge"); + } else { + success = false; + s_logger.warn("Fail to remove firewall rules as a part of vm id=" + + vmId + " expunge"); + } + + // cleanup port forwarding rules + if (_rulesMgr.revokePortForwardingRulesForVm(vmId)) { + s_logger.debug("Port forwarding rules are removed successfully as a part of vm id=" + + vmId + " expunge"); + } else { + success = false; + s_logger.warn("Fail to remove port forwarding rules as a part of vm id=" + + vmId + " expunge"); + } + + // cleanup load balancer rules + if (_lbMgr.removeVmFromLoadBalancers(vmId)) { + s_logger.debug("Removed vm id=" + vmId + + " from all load balancers as a part of expunge process"); + } else { + success = false; + s_logger.warn("Fail to remove vm id=" + vmId + + " from load balancers as a part of expunge process"); + } + + // If vm is assigned to static nat, disable static nat for the ip + // address and disassociate ip if elasticIP is enabled + IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(vmId); + try { + if (ip != null) { + if (_rulesMgr.disableStaticNat(ip.getId(), + _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), + User.UID_SYSTEM, true)) { + s_logger.debug("Disabled 1-1 nat for ip address " + ip + + " as a part of vm id=" + vmId + " expunge"); + } else { + s_logger.warn("Failed to disable static nat for ip address " + + ip + " as a part of vm id=" + vmId + " expunge"); + success = false; + } + } + } catch (ResourceUnavailableException e) { + success = false; + s_logger.warn("Failed to disable static nat for ip address " + ip + + " as a part of vm id=" + vmId + + " expunge because resource is unavailable", e); + } + + return success; + } + + @Override + public void deletePrivateTemplateRecord(Long templateId) { + if (templateId != null) { + _templateDao.remove(templateId); + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true) + public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, + Account templateOwner) throws ResourceAllocationException { + Long userId = UserContext.current().getCallerUserId(); + + Account caller = UserContext.current().getCaller(); + boolean isAdmin = (isAdmin(caller.getType())); + + _accountMgr.checkAccess(caller, null, true, templateOwner); + + String name = cmd.getTemplateName(); + if ((name == null) || (name.length() > 32)) { + throw new InvalidParameterValueException( + "Template name cannot be null and should be less than 32 characters"); + } + + if (cmd.getTemplateTag() != null) { + if (!_accountService.isRootAdmin(caller.getType())) { + throw new PermissionDeniedException( + "Parameter templatetag can only be specified by a Root Admin, permission denied"); + } + } + + // do some parameter defaulting + Integer bits = cmd.getBits(); + Boolean requiresHvm = cmd.getRequiresHvm(); + Boolean passwordEnabled = cmd.isPasswordEnabled(); + Boolean isPublic = cmd.isPublic(); + Boolean featured = cmd.isFeatured(); + int bitsValue = ((bits == null) ? 64 : bits.intValue()); + boolean requiresHvmValue = ((requiresHvm == null) ? true : requiresHvm + .booleanValue()); + boolean passwordEnabledValue = ((passwordEnabled == null) ? false + : passwordEnabled.booleanValue()); + if (isPublic == null) { + isPublic = Boolean.FALSE; + } + boolean allowPublicUserTemplates = Boolean.parseBoolean(_configDao + .getValue("allow.public.user.templates")); + if (!isAdmin && !allowPublicUserTemplates && isPublic) { + throw new PermissionDeniedException("Failed to create template " + + name + ", only private templates can be created."); + } + + Long volumeId = cmd.getVolumeId(); + Long snapshotId = cmd.getSnapshotId(); + if ((volumeId == null) && (snapshotId == null)) { + throw new InvalidParameterValueException( + "Failed to create private template record, neither volume ID nor snapshot ID were specified."); + } + if ((volumeId != null) && (snapshotId != null)) { + throw new InvalidParameterValueException( + "Failed to create private template record, please specify only one of volume ID (" + + volumeId + + ") and snapshot ID (" + + snapshotId + + ")"); + } + + HypervisorType hyperType; + VolumeVO volume = null; + VMTemplateVO privateTemplate = null; + if (volumeId != null) { // create template from volume + volume = _volsDao.findById(volumeId); + if (volume == null) { + throw new InvalidParameterValueException( + "Failed to create private template record, unable to find volume " + + volumeId); + } + // check permissions + _accountMgr.checkAccess(caller, null, true, volume); + + // If private template is created from Volume, check that the volume + // will not be active when the private template is + // created + if (!_storageMgr.volumeInactive(volume)) { + String msg = "Unable to create private template for volume: " + + volume.getName() + + "; volume is attached to a non-stopped VM, please stop the VM first"; + if (s_logger.isInfoEnabled()) { + s_logger.info(msg); + } + throw new CloudRuntimeException(msg); + } + hyperType = _volsDao.getHypervisorType(volumeId); + } else { // create template from snapshot + SnapshotVO snapshot = _snapshotDao.findById(snapshotId); + if (snapshot == null) { + throw new InvalidParameterValueException( + "Failed to create private template record, unable to find snapshot " + + snapshotId); + } + + volume = _volsDao.findById(snapshot.getVolumeId()); + VolumeVO snapshotVolume = _volsDao + .findByIdIncludingRemoved(snapshot.getVolumeId()); + + // check permissions + _accountMgr.checkAccess(caller, null, true, snapshot); + + if (snapshot.getStatus() != Snapshot.Status.BackedUp) { + throw new InvalidParameterValueException("Snapshot id=" + + snapshotId + " is not in " + Snapshot.Status.BackedUp + + " state yet and can't be used for template creation"); + } + + /* + * // bug #11428. Operation not supported if vmware and snapshots + * parent volume = ROOT if(snapshot.getHypervisorType() == + * HypervisorType.VMware && snapshotVolume.getVolumeType() == + * Type.DATADISK){ throw new UnsupportedServiceException( + * "operation not supported, snapshot with id " + snapshotId + + * " is created from Data Disk"); } + */ + + hyperType = snapshot.getHypervisorType(); + } + + _resourceLimitMgr.checkResourceLimit(templateOwner, + ResourceType.template); + + if (!isAdmin || featured == null) { + featured = Boolean.FALSE; + } + Long guestOSId = cmd.getOsTypeId(); + GuestOSVO guestOS = _guestOSDao.findById(guestOSId); + if (guestOS == null) { + throw new InvalidParameterValueException("GuestOS with ID: " + + guestOSId + " does not exist."); + } + + String uniqueName = Long.valueOf((userId == null) ? 1 : userId) + .toString() + + UUID.nameUUIDFromBytes(name.getBytes()).toString(); + Long nextTemplateId = _templateDao.getNextInSequence(Long.class, "id"); + String description = cmd.getDisplayText(); + boolean isExtractable = false; + Long sourceTemplateId = null; + if (volume != null) { + VMTemplateVO template = ApiDBUtils.findTemplateById(volume + .getTemplateId()); + isExtractable = template != null + && template.isExtractable() + && template.getTemplateType() != Storage.TemplateType.SYSTEM; + if (template != null) { + sourceTemplateId = template.getId(); + } else if (volume.getVolumeType() == Type.ROOT) { // vm created out + // of blank + // template + UserVm userVm = ApiDBUtils.findUserVmById(volume + .getInstanceId()); + sourceTemplateId = userVm.getIsoId(); + } + } + String templateTag = cmd.getTemplateTag(); + if (templateTag != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Adding template tag: " + templateTag); + } + } + privateTemplate = new VMTemplateVO(nextTemplateId, uniqueName, name, + ImageFormat.RAW, isPublic, featured, isExtractable, + TemplateType.USER, null, null, requiresHvmValue, bitsValue, + templateOwner.getId(), null, description, passwordEnabledValue, + guestOS.getId(), true, hyperType, templateTag, cmd.getDetails()); + if (sourceTemplateId != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("This template is getting created from other template, setting source template Id to: " + + sourceTemplateId); + } + } + privateTemplate.setSourceTemplateId(sourceTemplateId); + + VMTemplateVO template = _templateDao.persist(privateTemplate); + // Increment the number of templates + if (template != null) { + if (cmd.getDetails() != null) { + _templateDetailsDao.persist(template.getId(), cmd.getDetails()); + } + + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), + ResourceType.template); + } + + if (template != null) { + return template; + } else { + throw new CloudRuntimeException("Failed to create a template"); + } + + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", async = true) + public VMTemplateVO createPrivateTemplate(CreateTemplateCmd command) + throws CloudRuntimeException { + Long userId = UserContext.current().getCallerUserId(); + if (userId == null) { + userId = User.UID_SYSTEM; + } + long templateId = command.getEntityId(); + Long volumeId = command.getVolumeId(); + Long snapshotId = command.getSnapshotId(); + SnapshotCommand cmd = null; + VMTemplateVO privateTemplate = null; + + String uniqueName = getRandomPrivateTemplateName(); + + StoragePoolVO pool = null; + HostVO secondaryStorageHost = null; + Long zoneId = null; + Long accountId = null; + SnapshotVO snapshot = null; + String secondaryStorageURL = null; + try { + if (snapshotId != null) { // create template from snapshot + snapshot = _snapshotDao.findById(snapshotId); + if (snapshot == null) { + throw new CloudRuntimeException( + "Unable to find Snapshot for Id " + snapshotId); + } + zoneId = snapshot.getDataCenterId(); + secondaryStorageHost = _snapshotMgr + .getSecondaryStorageHost(snapshot); + secondaryStorageURL = _snapshotMgr + .getSecondaryStorageURL(snapshot); + String name = command.getTemplateName(); + String backupSnapshotUUID = snapshot.getBackupSnapshotId(); + if (backupSnapshotUUID == null) { + throw new CloudRuntimeException( + "Unable to create private template from snapshot " + + snapshotId + + " due to there is no backupSnapshotUUID for this snapshot"); + } + + Long dcId = snapshot.getDataCenterId(); + accountId = snapshot.getAccountId(); + volumeId = snapshot.getVolumeId(); + + String origTemplateInstallPath = null; + List pools = _storageMgr + .ListByDataCenterHypervisor(zoneId, + snapshot.getHypervisorType()); + if (pools == null || pools.size() == 0) { + throw new CloudRuntimeException( + "Unable to find storage pools in zone " + zoneId); + } + pool = pools.get(0); + if (snapshot.getVersion() != null + && snapshot.getVersion().equalsIgnoreCase("2.1")) { + VolumeVO volume = _volsDao + .findByIdIncludingRemoved(volumeId); + if (volume == null) { + throw new CloudRuntimeException( + "failed to upgrade snapshot " + + snapshotId + + " due to unable to find orignal volume:" + + volumeId + ", try it later "); + } + if (volume.getTemplateId() == null) { + _snapshotDao.updateSnapshotVersion(volumeId, "2.1", + "2.2"); + } else { + VMTemplateVO template = _templateDao + .findByIdIncludingRemoved(volume + .getTemplateId()); + if (template == null) { + throw new CloudRuntimeException( + "failed to upgrade snapshot " + + snapshotId + + " due to unalbe to find orignal template :" + + volume.getTemplateId() + + ", try it later "); + } + Long origTemplateId = template.getId(); + Long origTmpltAccountId = template.getAccountId(); + if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { + throw new CloudRuntimeException( + "failed to upgrade snapshot " + snapshotId + + " due to volume:" + volumeId + + " is being used, try it later "); + } + cmd = new UpgradeSnapshotCommand(null, + secondaryStorageURL, dcId, accountId, volumeId, + origTemplateId, origTmpltAccountId, null, + snapshot.getBackupSnapshotId(), + snapshot.getName(), "2.1"); + if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { + throw new CloudRuntimeException( + "Creating template failed due to volume:" + + volumeId + + " is being used, try it later "); + } + Answer answer = null; + try { + answer = _storageMgr.sendToPool(pool, cmd); + cmd = null; + } catch (StorageUnavailableException e) { + } finally { + _volsDao.unlockFromLockTable(volumeId.toString()); + } + if ((answer != null) && answer.getResult()) { + _snapshotDao.updateSnapshotVersion(volumeId, "2.1", + "2.2"); + } else { + throw new CloudRuntimeException( + "Unable to upgrade snapshot"); + } + } + } + if (snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0) { + _snapshotMgr.downloadSnapshotsFromSwift(snapshot); + } + cmd = new CreatePrivateTemplateFromSnapshotCommand( + pool.getUuid(), secondaryStorageURL, dcId, accountId, + snapshot.getVolumeId(), backupSnapshotUUID, + snapshot.getName(), origTemplateInstallPath, + templateId, name, + _createprivatetemplatefromsnapshotwait); + } else if (volumeId != null) { + VolumeVO volume = _volsDao.findById(volumeId); + if (volume == null) { + throw new CloudRuntimeException( + "Unable to find volume for Id " + volumeId); + } + accountId = volume.getAccountId(); + + if (volume.getPoolId() == null) { + _templateDao.remove(templateId); + throw new CloudRuntimeException("Volume " + volumeId + + " is empty, can't create template on it"); + } + String vmName = _storageMgr.getVmNameOnVolume(volume); + zoneId = volume.getDataCenterId(); + secondaryStorageHost = _storageMgr + .getSecondaryStorageHost(zoneId); + if (secondaryStorageHost == null) { + throw new CloudRuntimeException( + "Can not find the secondary storage for zoneId " + + zoneId); + } + secondaryStorageURL = secondaryStorageHost.getStorageUrl(); + + pool = _storagePoolDao.findById(volume.getPoolId()); + cmd = new CreatePrivateTemplateFromVolumeCommand( + pool.getUuid(), secondaryStorageURL, templateId, + accountId, command.getTemplateName(), uniqueName, + volume.getPath(), vmName, + _createprivatetemplatefromvolumewait); + + } else { + throw new CloudRuntimeException( + "Creating private Template need to specify snapshotId or volumeId"); + } + // FIXME: before sending the command, check if there's enough + // capacity + // on the storage server to create the template + + // This can be sent to a KVM host too. + CreatePrivateTemplateAnswer answer = null; + if (snapshotId != null) { + if (!_snapshotDao.lockInLockTable(snapshotId.toString(), 10)) { + throw new CloudRuntimeException( + "Creating template from snapshot failed due to snapshot:" + + snapshotId + + " is being used, try it later "); + } + } else { + if (!_volsDao.lockInLockTable(volumeId.toString(), 10)) { + throw new CloudRuntimeException( + "Creating template from volume failed due to volume:" + + volumeId + + " is being used, try it later "); + } + } + try { + answer = (CreatePrivateTemplateAnswer) _storageMgr.sendToPool( + pool, cmd); + } catch (StorageUnavailableException e) { + } finally { + if (snapshotId != null) { + _snapshotDao.unlockFromLockTable(snapshotId.toString()); + } else { + _volsDao.unlockFromLockTable(volumeId.toString()); + } + } + if ((answer != null) && answer.getResult()) { + privateTemplate = _templateDao.findById(templateId); + String answerUniqueName = answer.getUniqueName(); + if (answerUniqueName != null) { + privateTemplate.setUniqueName(answerUniqueName); + } else { + privateTemplate.setUniqueName(uniqueName); + } + ImageFormat format = answer.getImageFormat(); + if (format != null) { + privateTemplate.setFormat(format); + } else { + // This never occurs. + // Specify RAW format makes it unusable for snapshots. + privateTemplate.setFormat(ImageFormat.RAW); + } + + String checkSum = getChecksum(secondaryStorageHost.getId(), + answer.getPath()); + + Transaction txn = Transaction.currentTxn(); + + txn.start(); + + privateTemplate.setChecksum(checkSum); + _templateDao.update(templateId, privateTemplate); + + // add template zone ref for this template + _templateDao.addTemplateToZone(privateTemplate, zoneId); + VMTemplateHostVO templateHostVO = new VMTemplateHostVO( + secondaryStorageHost.getId(), templateId); + templateHostVO.setDownloadPercent(100); + templateHostVO.setDownloadState(Status.DOWNLOADED); + templateHostVO.setInstallPath(answer.getPath()); + templateHostVO.setLastUpdated(new Date()); + templateHostVO.setSize(answer.getVirtualSize()); + templateHostVO.setPhysicalSize(answer.getphysicalSize()); + _templateHostDao.persist(templateHostVO); + + UsageEventVO usageEvent = new UsageEventVO( + EventTypes.EVENT_TEMPLATE_CREATE, + privateTemplate.getAccountId(), + secondaryStorageHost.getDataCenterId(), + privateTemplate.getId(), privateTemplate.getName(), + null, privateTemplate.getSourceTemplateId(), + templateHostVO.getSize()); + _usageEventDao.persist(usageEvent); + txn.commit(); + } + } finally { + if (snapshot != null && snapshot.getSwiftId() != null + && secondaryStorageURL != null && zoneId != null + && accountId != null && volumeId != null) { + _snapshotMgr.deleteSnapshotsForVolume(secondaryStorageURL, + zoneId, accountId, volumeId); + } + if (privateTemplate == null) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + // Remove the template record + _templateDao.expunge(templateId); + + // decrement resource count + if (accountId != null) { + _resourceLimitMgr.decrementResourceCount(accountId, + ResourceType.template); + } + txn.commit(); + } + } + + if (privateTemplate != null) { + return privateTemplate; + } else { + throw new CloudRuntimeException("Failed to create a template"); + } + } + + @Override + public String getChecksum(Long hostId, String templatePath) { + HostVO ssHost = _hostDao.findById(hostId); + Host.Type type = ssHost.getType(); + if (type != Host.Type.SecondaryStorage + && type != Host.Type.LocalSecondaryStorage) { + return null; + } + String secUrl = ssHost.getStorageUrl(); + Answer answer; + answer = _agentMgr.sendToSecStorage(ssHost, new ComputeChecksumCommand( + secUrl, templatePath)); + if (answer != null && answer.getResult()) { + return answer.getDetails(); + } + return null; + } + + // used for vm transitioning to error state + private void updateVmStateForFailedVmCreation(Long vmId) { + + UserVmVO vm = _vmDao.findById(vmId); + + if (vm != null) { + if (vm.getState().equals(State.Stopped)) { + s_logger.debug("Destroying vm " + vm + + " as it failed to create"); + try { + _itMgr.stateTransitTo(vm, + VirtualMachine.Event.OperationFailedToError, null); + } catch (NoTransitionException e1) { + s_logger.warn(e1.getMessage()); + } + // destroy associated volumes for vm in error state + // get all volumes in non destroyed state + List volumesForThisVm = _volsDao + .findUsableVolumesForInstance(vm.getId()); + for (VolumeVO volume : volumesForThisVm) { + try { + if (volume.getState() != Volume.State.Destroy) { + _storageMgr.destroyVolume(volume); + } + } catch (ConcurrentOperationException e) { + s_logger.warn("Unable to delete volume:" + + volume.getId() + " for vm:" + vmId + + " whilst transitioning to error state"); + } + } + String msg = "Failed to deploy Vm with Id: " + vmId; + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, + vm.getDataCenterIdToDeployIn(), + vm.getPodIdToDeployIn(), msg, msg); + + _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), + ResourceType.user_vm); + } + } + } + + protected class ExpungeTask implements Runnable { + public ExpungeTask() { + } + + @Override + public void run() { + GlobalLock scanLock = GlobalLock.getInternLock("UserVMExpunge"); + try { + if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { + try { + List vms = _vmDao.findDestroyedVms(new Date( + System.currentTimeMillis() + - ((long) _expungeDelay << 10))); + if (s_logger.isInfoEnabled()) { + if (vms.size() == 0) { + s_logger.trace("Found " + vms.size() + + " vms to expunge."); + } else { + s_logger.info("Found " + vms.size() + + " vms to expunge."); + } + } + for (UserVmVO vm : vms) { + try { + expunge(vm, + _accountMgr.getSystemUser().getId(), + _accountMgr.getSystemAccount()); + } catch (Exception e) { + s_logger.warn("Unable to expunge " + vm, e); + } + } + } catch (Exception e) { + s_logger.error("Caught the following Exception", e); + } finally { + scanLock.unlock(); + } + } + } finally { + scanLock.releaseRef(); + } + } + } + + private static boolean isAdmin(short accountType) { + return ((accountType == Account.ACCOUNT_TYPE_ADMIN) + || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) + || (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_UPDATE, eventDescription = "updating Vm") + public UserVm updateVirtualMachine(UpdateVMCmd cmd) { + String displayName = cmd.getDisplayName(); + String group = cmd.getGroup(); + Boolean ha = cmd.getHaEnable(); + Long id = cmd.getId(); + Long osTypeId = cmd.getOsTypeId(); + String userData = cmd.getUserData(); + + // Input validation + UserVmVO vmInstance = null; + + // Verify input parameters + vmInstance = _vmDao.findById(id.longValue()); + + if (vmInstance == null) { + throw new InvalidParameterValueException( + "unable to find virtual machine with id " + id); + } + + ServiceOffering offering = _serviceOfferingDao.findById(vmInstance + .getServiceOfferingId()); + if (!offering.getOfferHA() && ha != null && ha) { + throw new InvalidParameterValueException( + "Can't enable ha for the vm as it's created from the Service offering having HA disabled"); + } + + _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, + vmInstance); + + if (displayName == null) { + displayName = vmInstance.getDisplayName(); + } + + if (ha == null) { + ha = vmInstance.isHaEnabled(); + } + + UserVmVO vm = _vmDao.findById(id); + if (vm == null) { + throw new CloudRuntimeException( + "Unable to find virual machine with id " + id); + } + + if (vm.getState() == State.Error || vm.getState() == State.Expunging) { + s_logger.error("vm is not in the right state: " + id); + throw new InvalidParameterValueException("Vm with id " + id + + " is not in the right state"); + } + + if (userData != null) { + validateUserData(userData); + // update userData on domain router. + } else { + userData = vmInstance.getUserData(); + } + + String description = ""; + + if (displayName != vmInstance.getDisplayName()) { + description += "New display name: " + displayName + ". "; + } + + if (ha != vmInstance.isHaEnabled()) { + if (ha) { + description += "Enabled HA. "; + } else { + description += "Disabled HA. "; + } + } + if (osTypeId == null) { + osTypeId = vmInstance.getGuestOSId(); + } else { + description += "Changed Guest OS Type to " + osTypeId + ". "; + } + + if (group != null) { + if (addInstanceToGroup(id, group)) { + description += "Added to group: " + group + "."; + } + } + + _vmDao.updateVM(id, displayName, ha, osTypeId, userData); + + return _vmDao.findById(id); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) + public UserVm startVirtualMachine(StartVMCmd cmd) + throws ExecutionException, ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + return startVirtualMachine(cmd.getId(), cmd.getHostId(), null).first(); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true) + public UserVm rebootVirtualMachine(RebootVMCmd cmd) + throws InsufficientCapacityException, ResourceUnavailableException { + Account caller = UserContext.current().getCaller(); + Long vmId = cmd.getId(); + + // Verify input parameters + UserVmVO vmInstance = _vmDao.findById(vmId.longValue()); + if (vmInstance == null) { + throw new InvalidParameterValueException( + "unable to find a virtual machine with id " + vmId); + } + + _accountMgr.checkAccess(caller, null, true, vmInstance); + + return rebootVirtualMachine(UserContext.current().getCallerUserId(), + vmId); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_DESTROY, eventDescription = "destroying Vm", async = true) + public UserVm destroyVm(DestroyVMCmd cmd) + throws ResourceUnavailableException, ConcurrentOperationException { + return destroyVm(cmd.getId()); + } + + @Override + @DB + public InstanceGroupVO createVmGroup(CreateVMGroupCmd cmd) { + Account caller = UserContext.current().getCaller(); + Long domainId = cmd.getDomainId(); + String accountName = cmd.getAccountName(); + String groupName = cmd.getGroupName(); + Long projectId = cmd.getProjectId(); + + Account owner = _accountMgr.finalizeOwner(caller, accountName, + domainId, projectId); + long accountId = owner.getId(); + + // Check if name is already in use by this account + boolean isNameInUse = _vmGroupDao.isNameInUse(accountId, groupName); + + if (isNameInUse) { + throw new InvalidParameterValueException( + "Unable to create vm group, a group with name " + groupName + + " already exisits for account " + accountId); + } + + return createVmGroup(groupName, accountId); + } + + @DB + protected InstanceGroupVO createVmGroup(String groupName, long accountId) { + Account account = null; + final Transaction txn = Transaction.currentTxn(); + txn.start(); + try { + account = _accountDao.acquireInLockTable(accountId); // to ensure + // duplicate + // vm group + // names are + // not + // created. + if (account == null) { + s_logger.warn("Failed to acquire lock on account"); + return null; + } + InstanceGroupVO group = _vmGroupDao.findByAccountAndName(accountId, + groupName); + if (group == null) { + group = new InstanceGroupVO(groupName, accountId); + group = _vmGroupDao.persist(group); + } + return group; + } finally { + if (account != null) { + _accountDao.releaseFromLockTable(accountId); + } + txn.commit(); + } + } + + @Override + public boolean deleteVmGroup(DeleteVMGroupCmd cmd) { + Account caller = UserContext.current().getCaller(); + Long groupId = cmd.getId(); + + // Verify input parameters + InstanceGroupVO group = _vmGroupDao.findById(groupId); + if ((group == null) || (group.getRemoved() != null)) { + throw new InvalidParameterValueException( + "unable to find a vm group with id " + groupId); + } + + _accountMgr.checkAccess(caller, null, true, group); + + return deleteVmGroup(groupId); + } + + @Override + public boolean deleteVmGroup(long groupId) { + // delete all the mappings from group_vm_map table + List groupVmMaps = _groupVMMapDao + .listByGroupId(groupId); + for (InstanceGroupVMMapVO groupMap : groupVmMaps) { + SearchCriteria sc = _groupVMMapDao + .createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, + groupMap.getInstanceId()); + _groupVMMapDao.expunge(sc); + } + + if (_vmGroupDao.remove(groupId)) { + return true; + } else { + return false; + } + } + + @Override + @DB + public boolean addInstanceToGroup(long userVmId, String groupName) { + UserVmVO vm = _vmDao.findById(userVmId); + + InstanceGroupVO group = _vmGroupDao.findByAccountAndName( + vm.getAccountId(), groupName); + // Create vm group if the group doesn't exist for this account + if (group == null) { + group = createVmGroup(groupName, vm.getAccountId()); + } + + if (group != null) { + final Transaction txn = Transaction.currentTxn(); + txn.start(); + UserVm userVm = _vmDao.acquireInLockTable(userVmId); + if (userVm == null) { + s_logger.warn("Failed to acquire lock on user vm id=" + + userVmId); + } + try { + // don't let the group be deleted when we are assigning vm to + // it. + InstanceGroupVO ngrpLock = _vmGroupDao.lockRow(group.getId(), + false); + if (ngrpLock == null) { + s_logger.warn("Failed to acquire lock on vm group id=" + + group.getId() + " name=" + group.getName()); + txn.rollback(); + return false; + } + + // Currently don't allow to assign a vm to more than one group + if (_groupVMMapDao.listByInstanceId(userVmId) != null) { + // Delete all mappings from group_vm_map table + List groupVmMaps = _groupVMMapDao + .listByInstanceId(userVmId); + for (InstanceGroupVMMapVO groupMap : groupVmMaps) { + SearchCriteria sc = _groupVMMapDao + .createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, + groupMap.getInstanceId()); + _groupVMMapDao.expunge(sc); + } + } + InstanceGroupVMMapVO groupVmMapVO = new InstanceGroupVMMapVO( + group.getId(), userVmId); + _groupVMMapDao.persist(groupVmMapVO); + + txn.commit(); + return true; + } finally { + if (userVm != null) { + _vmDao.releaseFromLockTable(userVmId); + } + } + } + return false; + } + + @Override + public InstanceGroupVO getGroupForVm(long vmId) { + // TODO - in future releases vm can be assigned to multiple groups; but + // currently return just one group per vm + try { + List groupsToVmMap = _groupVMMapDao + .listByInstanceId(vmId); + + if (groupsToVmMap != null && groupsToVmMap.size() != 0) { + InstanceGroupVO group = _vmGroupDao.findById(groupsToVmMap.get( + 0).getGroupId()); + return group; + } else { + return null; + } + } catch (Exception e) { + s_logger.warn("Error trying to get group for a vm: ", e); + return null; + } + } + + @Override + public void removeInstanceFromInstanceGroup(long vmId) { + try { + List groupVmMaps = _groupVMMapDao + .listByInstanceId(vmId); + for (InstanceGroupVMMapVO groupMap : groupVmMaps) { + SearchCriteria sc = _groupVMMapDao + .createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, + groupMap.getInstanceId()); + _groupVMMapDao.expunge(sc); + } + } catch (Exception e) { + s_logger.warn("Error trying to remove vm from group: ", e); + } + } + + protected boolean validPassword(String password) { + if (password == null || password.length() == 0) { + return false; + } + for (int i = 0; i < password.length(); i++) { + if (password.charAt(i) == ' ') { + return false; + } + } + return true; + } + + @Override + public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, + ServiceOffering serviceOffering, VirtualMachineTemplate template, + List securityGroupIdList, Account owner, String hostName, + String displayName, Long diskOfferingId, Long diskSize, + String group, HypervisorType hypervisor, String userData, + String sshKeyPair, Map requestedIps, + String defaultIp, String keyboard) + throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException, StorageUnavailableException, + ResourceAllocationException { + + Account caller = UserContext.current().getCaller(); + List networkList = new ArrayList(); + + // Verify that caller can perform actions in behalf of vm owner + _accountMgr.checkAccess(caller, null, true, owner); + + // Get default guest network in Basic zone + Network defaultNetwork = _networkMgr.getExclusiveGuestNetwork(zone + .getId()); + + if (defaultNetwork == null) { + throw new InvalidParameterValueException( + "Unable to find a default network to start a vm"); + } else { + networkList.add(_networkDao.findById(defaultNetwork.getId())); + } + + boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); + + if (securityGroupIdList != null && isVmWare) { + throw new InvalidParameterValueException( + "Security group feature is not supported for vmWare hypervisor"); + } else if (!isVmWare + && _networkMgr + .isSecurityGroupSupportedInNetwork(defaultNetwork) + && _networkMgr.canAddDefaultSecurityGroup()) { + // add the default securityGroup only if no security group is + // specified + if (securityGroupIdList == null || securityGroupIdList.isEmpty()) { + if (securityGroupIdList == null) { + securityGroupIdList = new ArrayList(); + } + SecurityGroup defaultGroup = _securityGroupMgr + .getDefaultSecurityGroup(owner.getId()); + if (defaultGroup != null) { + securityGroupIdList.add(defaultGroup.getId()); + } else { + // create default security group for the account + if (s_logger.isDebugEnabled()) { + s_logger.debug("Couldn't find default security group for the account " + + owner + " so creating a new one"); + } + defaultGroup = _securityGroupMgr.createSecurityGroup( + SecurityGroupManager.DEFAULT_GROUP_NAME, + SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, + owner.getDomainId(), owner.getId(), + owner.getAccountName()); + securityGroupIdList.add(defaultGroup.getId()); + } + } + } + + return createVirtualMachine(zone, serviceOffering, template, hostName, + displayName, owner, diskOfferingId, diskSize, networkList, + securityGroupIdList, group, userData, sshKeyPair, hypervisor, + caller, requestedIps, defaultIp, keyboard); + } + + @Override + public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, + ServiceOffering serviceOffering, VirtualMachineTemplate template, + List networkIdList, List securityGroupIdList, + Account owner, String hostName, String displayName, + Long diskOfferingId, Long diskSize, String group, + HypervisorType hypervisor, String userData, String sshKeyPair, + Map requestedIps, String defaultIp, String keyboard) + throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException, StorageUnavailableException, + ResourceAllocationException { + + Account caller = UserContext.current().getCaller(); + List networkList = new ArrayList(); + boolean isSecurityGroupEnabledNetworkUsed = false; + boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); + + // Verify that caller can perform actions in behalf of vm owner + _accountMgr.checkAccess(caller, null, true, owner); + + // If no network is specified, find system security group enabled + // network + if (networkIdList == null || networkIdList.isEmpty()) { + NetworkVO networkWithSecurityGroup = _networkMgr + .getNetworkWithSecurityGroupEnabled(zone.getId()); + if (networkWithSecurityGroup == null) { + throw new InvalidParameterValueException( + "No network with security enabled is found in zone id=" + + zone.getId()); + } + + networkList.add(networkWithSecurityGroup); + isSecurityGroupEnabledNetworkUsed = true; + + } else if (securityGroupIdList != null + && !securityGroupIdList.isEmpty()) { + if (isVmWare) { + throw new InvalidParameterValueException( + "Security group feature is not supported for vmWare hypervisor"); + } + // Only one network can be specified, and it should be security + // group enabled + if (networkIdList.size() > 1) { + throw new InvalidParameterValueException( + "Only support one network per VM if security group enabled"); + } + + NetworkVO network = _networkDao.findById(networkIdList.get(0) + .longValue()); + + if (network == null) { + throw new InvalidParameterValueException( + "Unable to find network by id " + + networkIdList.get(0).longValue()); + } + + if (!_networkMgr.isSecurityGroupSupportedInNetwork(network)) { + throw new InvalidParameterValueException( + "Network is not security group enabled: " + + network.getId()); + } + + networkList.add(network); + isSecurityGroupEnabledNetworkUsed = true; + + } else { + // Verify that all the networks are Direct/Guest/AccountSpecific; + // can't create combination of SG enabled network and + // regular networks + for (Long networkId : networkIdList) { + NetworkVO network = _networkDao.findById(networkId); + + if (network == null) { + throw new InvalidParameterValueException( + "Unable to find network by id " + + networkIdList.get(0).longValue()); + } + + boolean isSecurityGroupEnabled = _networkMgr + .isSecurityGroupSupportedInNetwork(network); + if (isSecurityGroupEnabled && networkIdList.size() > 1) { + throw new InvalidParameterValueException( + "Can't create a vm with multiple networks one of which is Security Group enabled"); + } + + if (network.getTrafficType() != TrafficType.Guest + || network.getGuestType() != Network.GuestType.Shared + || (network.getGuestType() == Network.GuestType.Shared && !isSecurityGroupEnabled)) { + throw new InvalidParameterValueException( + "Can specify only Direct Guest Account specific networks when deploy vm in Security Group enabled zone"); + } + + // Perform account permission check + if (network.getGuestType() != Network.GuestType.Shared) { + // Check account permissions + List networkMap = _networkDao.listBy( + owner.getId(), network.getId()); + if (networkMap == null || networkMap.isEmpty()) { + throw new PermissionDeniedException( + "Unable to create a vm using network with id " + + network.getId() + + ", permission denied"); + } + } + + networkList.add(network); + } + } + + // if network is security group enabled, and no security group is + // specified, then add the default security group automatically + if (isSecurityGroupEnabledNetworkUsed && !isVmWare + && _networkMgr.canAddDefaultSecurityGroup()) { + + // add the default securityGroup only if no security group is + // specified + if (securityGroupIdList == null || securityGroupIdList.isEmpty()) { + if (securityGroupIdList == null) { + securityGroupIdList = new ArrayList(); + } + + SecurityGroup defaultGroup = _securityGroupMgr + .getDefaultSecurityGroup(owner.getId()); + if (defaultGroup != null) { + securityGroupIdList.add(defaultGroup.getId()); + } else { + // create default security group for the account + if (s_logger.isDebugEnabled()) { + s_logger.debug("Couldn't find default security group for the account " + + owner + " so creating a new one"); + } + defaultGroup = _securityGroupMgr.createSecurityGroup( + SecurityGroupManager.DEFAULT_GROUP_NAME, + SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, + owner.getDomainId(), owner.getId(), + owner.getAccountName()); + securityGroupIdList.add(defaultGroup.getId()); + } + } + } + + return createVirtualMachine(zone, serviceOffering, template, hostName, + displayName, owner, diskOfferingId, diskSize, networkList, + securityGroupIdList, group, userData, sshKeyPair, hypervisor, + caller, requestedIps, defaultIp, keyboard); + } + + @Override + public UserVm createAdvancedVirtualMachine(DataCenter zone, + ServiceOffering serviceOffering, VirtualMachineTemplate template, + List networkIdList, Account owner, String hostName, + String displayName, Long diskOfferingId, Long diskSize, + String group, HypervisorType hypervisor, String userData, + String sshKeyPair, Map requestedIps, + String defaultIp, String keyboard) + throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException, StorageUnavailableException, + ResourceAllocationException { + + Account caller = UserContext.current().getCaller(); + List networkList = new ArrayList(); + + // Verify that caller can perform actions in behalf of vm owner + _accountMgr.checkAccess(caller, null, true, owner); + + List vpcSupportedHTypes = _vpcMgr + .getSupportedVpcHypervisors(); + if (networkIdList == null || networkIdList.isEmpty()) { + NetworkVO defaultNetwork = null; + + // if no network is passed in + // Check if default virtual network offering has + // Availability=Required. If it's true, search for corresponding + // network + // * if network is found, use it. If more than 1 virtual network is + // found, throw an error + // * if network is not found, create a new one and use it + + List requiredOfferings = _networkOfferingDao + .listByAvailability(Availability.Required, false); + if (requiredOfferings.size() < 1) { + throw new InvalidParameterValueException( + "Unable to find network offering with availability=" + + Availability.Required + + " to automatically create the network as a part of vm creation"); + } + + if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { + // get Virtual networks + List virtualNetworks = _networkMgr + .listNetworksForAccount(owner.getId(), zone.getId(), + Network.GuestType.Isolated); + + if (virtualNetworks.isEmpty()) { + long physicalNetworkId = _networkMgr.findPhysicalNetworkId( + zone.getId(), requiredOfferings.get(0).getTags(), + requiredOfferings.get(0).getTrafficType()); + // Validate physical network + PhysicalNetwork physicalNetwork = _physicalNetworkDao + .findById(physicalNetworkId); + if (physicalNetwork == null) { + throw new InvalidParameterValueException( + "Unable to find physical network with id: " + + physicalNetworkId + " and tag: " + + requiredOfferings.get(0).getTags()); + } + s_logger.debug("Creating network for account " + owner + + " from the network offering id=" + + requiredOfferings.get(0).getId() + + " as a part of deployVM process"); + Network newNetwork = _networkMgr.createGuestNetwork( + requiredOfferings.get(0).getId(), + owner.getAccountName() + "-network", + owner.getAccountName() + "-network", null, null, + null, null, owner, null, physicalNetwork, + zone.getId(), ACLType.Account, null, null); + defaultNetwork = _networkDao.findById(newNetwork.getId()); + } else if (virtualNetworks.size() > 1) { + throw new InvalidParameterValueException( + "More than 1 default Isolated networks are found for account " + + owner + "; please specify networkIds"); + } else { + defaultNetwork = virtualNetworks.get(0); + } + } else { + throw new InvalidParameterValueException( + "Required network offering id=" + + requiredOfferings.get(0).getId() + + " is not in " + NetworkOffering.State.Enabled); + } + + networkList.add(defaultNetwork); + + } else { + for (Long networkId : networkIdList) { + NetworkVO network = _networkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException( + "Unable to find network by id " + + networkIdList.get(0).longValue()); + } + if (network.getVpcId() != null) { + // Only ISOs, XenServer, KVM, and VmWare template types are + // supported for vpc networks + if (template.getFormat() != ImageFormat.ISO + && !vpcSupportedHTypes.contains(template + .getHypervisorType())) { + throw new InvalidParameterValueException( + "Can't create vm from template with hypervisor " + + template.getHypervisorType() + + " in vpc network " + network); + } + + // Only XenServer, KVM, and VMware hypervisors are supported + // for vpc networks + if (!vpcSupportedHTypes.contains(hypervisor)) { + throw new InvalidParameterValueException( + "Can't create vm of hypervisor type " + + hypervisor + " in vpc network"); + } + + } + + _networkMgr.checkNetworkPermissions(owner, network); + + // don't allow to use system networks + NetworkOffering networkOffering = _configMgr + .getNetworkOffering(network.getNetworkOfferingId()); + if (networkOffering.isSystemOnly()) { + throw new InvalidParameterValueException( + "Network id=" + + networkId + + " is system only and can't be used for vm deployment"); + } + networkList.add(network); + } + } + + return createVirtualMachine(zone, serviceOffering, template, hostName, + displayName, owner, diskOfferingId, diskSize, networkList, + null, group, userData, sshKeyPair, hypervisor, caller, + requestedIps, defaultIp, keyboard); + } + + @DB + @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true) + protected UserVm createVirtualMachine(DataCenter zone, + ServiceOffering serviceOffering, VirtualMachineTemplate template, + String hostName, String displayName, Account owner, + Long diskOfferingId, Long diskSize, List networkList, + List securityGroupIdList, String group, String userData, + String sshKeyPair, HypervisorType hypervisor, Account caller, + Map requestedIps, String defaultNetworkIp, + String keyboard) throws InsufficientCapacityException, + ResourceUnavailableException, ConcurrentOperationException, + StorageUnavailableException, ResourceAllocationException { + + _accountMgr.checkAccess(caller, null, true, owner); + + if (owner.getState() == Account.State.disabled) { + throw new PermissionDeniedException( + "The owner of vm to deploy is disabled: " + owner); + } + + long accountId = owner.getId(); + + assert !(requestedIps != null && defaultNetworkIp != null) : "requestedIp list and defaultNetworkIp should never be specified together"; + + if (Grouping.AllocationState.Disabled == zone.getAllocationState() + && !_accountMgr.isRootAdmin(caller.getType())) { + throw new PermissionDeniedException( + "Cannot perform this operation, Zone is currently disabled: " + + zone.getId()); + } + + if (zone.getDomainId() != null) { + DomainVO domain = _domainDao.findById(zone.getDomainId()); + if (domain == null) { + throw new CloudRuntimeException("Unable to find the domain " + + zone.getDomainId() + " for the zone: " + zone); + } + // check that caller can operate with domain + _configMgr.checkZoneAccess(caller, zone); + // check that vm owner can create vm in the domain + _configMgr.checkZoneAccess(owner, zone); + } + + // check if account/domain is with in resource limits to create a new vm + boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso + || diskOfferingId == null ? 1 : 2)); + + // verify security group ids + if (securityGroupIdList != null) { + for (Long securityGroupId : securityGroupIdList) { + SecurityGroup sg = _securityGroupDao.findById(securityGroupId); + if (sg == null) { + throw new InvalidParameterValueException( + "Unable to find security group by id " + + securityGroupId); + } else { + // verify permissions + _accountMgr.checkAccess(caller, null, true, owner, sg); + } + } + } + + // check if we have available pools for vm deployment + long availablePools = _storagePoolDao + .countPoolsByStatus(StoragePoolStatus.Up); + if (availablePools < 1) { + throw new StorageUnavailableException( + "There are no available pools in the UP state for vm deployment", + -1); + } + + ServiceOfferingVO offering = _serviceOfferingDao + .findById(serviceOffering.getId()); + + if (template.getTemplateType().equals(TemplateType.SYSTEM)) { + throw new InvalidParameterValueException( + "Unable to use system template " + template.getId() + + " to deploy a user vm"); + } + List listZoneTemplate = _templateZoneDao + .listByZoneTemplate(zone.getId(), template.getId()); + if (listZoneTemplate == null || listZoneTemplate.isEmpty()) { + throw new InvalidParameterValueException("The template " + + template.getId() + " is not available for use"); + } + + if (isIso && !template.isBootable()) { + throw new InvalidParameterValueException( + "Installing from ISO requires an ISO that is bootable: " + + template.getId()); + } + + // Check templates permissions + if (!template.isPublicTemplate()) { + Account templateOwner = _accountMgr.getAccount(template + .getAccountId()); + _accountMgr.checkAccess(owner, null, true, templateOwner); + } + + // check if the user data is correct + validateUserData(userData); + + // Find an SSH public key corresponding to the key pair name, if one is + // given + String sshPublicKey = null; + if (sshKeyPair != null && !sshKeyPair.equals("")) { + SSHKeyPair pair = _sshKeyPairDao.findByName(owner.getAccountId(), + owner.getDomainId(), sshKeyPair); + if (pair == null) { + throw new InvalidParameterValueException( + "A key pair with name '" + sshKeyPair + + "' was not found."); + } + + sshPublicKey = pair.getPublicKey(); + } + + List> networks = new ArrayList>(); + + List networkUuidList = new ArrayList(); + + short defaultNetworkNumber = 0; + boolean securityGroupEnabled = false; + boolean vpcNetwork = false; + for (NetworkVO network : networkList) { + if (network.getDataCenterId() != zone.getId()) { + throw new InvalidParameterValueException("Network id=" + + network.getId() + " doesn't belong to zone " + + zone.getId()); + } + + String requestedIp = null; + if (requestedIps != null && !requestedIps.isEmpty()) { + requestedIp = requestedIps.get(network.getId()); + } + + NicProfile profile = new NicProfile(requestedIp); + + if (defaultNetworkNumber == 0) { + defaultNetworkNumber++; + // if user requested specific ip for default network, add it + if (defaultNetworkIp != null) { + profile = new NicProfile(defaultNetworkIp); + } + + profile.setDefaultNic(true); + } + + networks.add(new Pair(network, profile)); + + if (_networkMgr.isSecurityGroupSupportedInNetwork(network)) { + securityGroupEnabled = true; + } + + // vm can't be a part of more than 1 VPC network + if (network.getVpcId() != null) { + if (vpcNetwork) { + throw new InvalidParameterValueException( + "Vm can't be a part of more than 1 VPC network"); + } + vpcNetwork = true; + } + + networkUuidList.add(network.getUuid()); + } + + if (securityGroupIdList != null && !securityGroupIdList.isEmpty() + && !securityGroupEnabled) { + throw new InvalidParameterValueException( + "Unable to deploy vm with security groups as SecurityGroup service is not enabled for the vm's network"); + } + + // Verify network information - network default network has to be set; + // and vm can't have more than one default network + // This is a part of business logic because default network is required + // by Agent Manager in order to configure default + // gateway for the vm + if (defaultNetworkNumber == 0) { + throw new InvalidParameterValueException( + "At least 1 default network has to be specified for the vm"); + } else if (defaultNetworkNumber > 1) { + throw new InvalidParameterValueException( + "Only 1 default network per vm is supported"); + } + + long id = _vmDao.getNextInSequence(Long.class, "id"); + + String instanceName = VirtualMachineName.getVmName(id, owner.getId(), + _instance); + + String uuidName = UUID.randomUUID().toString(); + + // verify hostname information + if (hostName == null) { + hostName = uuidName; + } else { + // 1) check is hostName is RFC complient + if (!NetUtils.verifyDomainNameLabel(hostName, true)) { + throw new InvalidParameterValueException( + "Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); + } + // 2) hostName has to be unique in the network domain + Map> ntwkDomains = new HashMap>(); + for (NetworkVO network : networkList) { + String ntwkDomain = network.getNetworkDomain(); + if (!ntwkDomains.containsKey(ntwkDomain)) { + List ntwkIds = new ArrayList(); + ntwkIds.add(network.getId()); + ntwkDomains.put(ntwkDomain, ntwkIds); + } else { + List ntwkIds = ntwkDomains.get(ntwkDomain); + ntwkIds.add(network.getId()); + ntwkDomains.put(ntwkDomain, ntwkIds); + } + } + + for (String ntwkDomain : ntwkDomains.keySet()) { + for (Long ntwkId : ntwkDomains.get(ntwkDomain)) { + // * get all vms hostNames in the network + List hostNames = _vmInstanceDao + .listDistinctHostNames(ntwkId); + // * verify that there are no duplicates + if (hostNames.contains(hostName)) { + throw new InvalidParameterValueException( + "The vm with hostName " + + hostName + + " already exists in the network domain: " + + ntwkDomain + "; network=" + + _networkMgr.getNetwork(ntwkId)); + } + } + } + } + + HypervisorType hypervisorType = null; + if (template == null || template.getHypervisorType() == null + || template.getHypervisorType() == HypervisorType.None) { + hypervisorType = hypervisor; + } else { + hypervisorType = template.getHypervisorType(); + } + Transaction txn = Transaction.currentTxn(); + txn.start(); + UserVmVO vm = new UserVmVO(id, instanceName, displayName, + template.getId(), hypervisorType, template.getGuestOSId(), + offering.getOfferHA(), offering.getLimitCpuUse(), + owner.getDomainId(), owner.getId(), offering.getId(), userData, + hostName); + vm.setUuid(uuidName); + + if (sshPublicKey != null) { + vm.setDetail("SSH.PublicKey", sshPublicKey); + } + + if (keyboard != null && !keyboard.isEmpty()) + vm.setDetail(VmDetailConstants.KEYBOARD, keyboard); + + if (isIso) { + vm.setIsoId(template.getId()); + } + + s_logger.debug("Allocating in the DB for vm"); + DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); + + + _vmDao.persist(vm); + _vmDao.saveDetails(vm); + + long guestOSId = template.getGuestOSId(); + GuestOSVO guestOS = _guestOSDao.findById(guestOSId); + long guestOSCategoryId = guestOS.getCategoryId(); + GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); + + List computeTags = new ArrayList(); + computeTags.add(offering.getHostTag()); + + List rootDiskTags = new ArrayList(); + rootDiskTags.add(offering.getTags()); + + if(isIso){ + VirtualMachineEntity vmEntity = _orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), owner.getAccountName(), vm.getIsoId().toString(), hostName, displayName, hypervisor.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkUuidList, plan); + }else { + VirtualMachineEntity vmEntity = _orchSrvc.createVirtualMachine(vm.getUuid(), owner.getAccountName(), new Long(template.getId()).toString(), hostName, displayName, hypervisor.name(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkUuidList, plan); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Successfully allocated DB entry for " + vm); + } + UserContext.current().setEventDetails("Vm Id: " + vm.getId()); + + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, + accountId, zone.getId(), vm.getId(), vm.getHostName(), + offering.getId(), template.getId(), hypervisorType.toString()); + _usageEventDao.persist(usageEvent); + + _resourceLimitMgr.incrementResourceCount(accountId, + ResourceType.user_vm); + txn.commit(); + // Assign instance to the group + try { + if (group != null) { + boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); + if (!addToGroup) { + throw new CloudRuntimeException( + "Unable to assign Vm to the group " + group); + } + } + } catch (Exception ex) { + throw new CloudRuntimeException("Unable to assign Vm to the group " + + group); + } + + _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList); + + return vm; + } + + private void validateUserData(String userData) { + byte[] decodedUserData = null; + if (userData != null) { + if (!Base64.isBase64(userData)) { + throw new InvalidParameterValueException( + "User data is not base64 encoded"); + } + if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { + throw new InvalidParameterValueException( + "User data is too long"); + } + decodedUserData = Base64.decodeBase64(userData.getBytes()); + if (decodedUserData.length > MAX_USER_DATA_LENGTH_BYTES) { + throw new InvalidParameterValueException( + "User data is too long"); + } + if (decodedUserData.length < 1) { + throw new InvalidParameterValueException( + "User data is too short"); + } + } + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "starting Vm", async = true) + public UserVm startVirtualMachine(DeployVMCmd cmd) + throws ResourceUnavailableException, InsufficientCapacityException, + ConcurrentOperationException { + return startVirtualMachine(cmd, null); + } + + protected UserVm startVirtualMachine(DeployVMCmd cmd, + Map additonalParams) + throws ResourceUnavailableException, InsufficientCapacityException, + ConcurrentOperationException { + + long vmId = cmd.getEntityId(); + Long hostId = cmd.getHostId(); + UserVmVO vm = _vmDao.findById(vmId); + + Pair> vmParamPair = null; + try { + vmParamPair = startVirtualMachine(vmId, hostId, additonalParams); + vm = vmParamPair.first(); + ; + } finally { + updateVmStateForFailedVmCreation(vm.getId()); + } + + // Check that the password was passed in and is valid + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm + .getTemplateId()); + if (template.getEnablePassword()) { + // this value is not being sent to the backend; need only for api + // display purposes + vm.setPassword((String) vmParamPair.second().get( + VirtualMachineProfile.Param.VmPassword)); + } + + return vm; + } + + @Override + public boolean finalizeVirtualMachineProfile( + VirtualMachineProfile profile, DeployDestination dest, + ReservationContext context) { + UserVmVO vm = profile.getVirtualMachine(); + Map details = _vmDetailsDao.findDetails(vm.getId()); + vm.setDetails(details); + + if (vm.getIsoId() != null) { + String isoPath = null; + + VirtualMachineTemplate template = _templateDao.findById(vm + .getIsoId()); + if (template == null || template.getFormat() != ImageFormat.ISO) { + throw new CloudRuntimeException( + "Can not find ISO in vm_template table for id " + + vm.getIsoId()); + } + + Pair isoPathPair = _storageMgr.getAbsoluteIsoPath( + template.getId(), vm.getDataCenterIdToDeployIn()); + + if (template.getTemplateType() == TemplateType.PERHOST) { + isoPath = template.getName(); + } else { + if (isoPathPair == null) { + s_logger.warn("Couldn't get absolute iso path"); + return false; + } else { + isoPath = isoPathPair.first(); + } + } + + if (template.isBootable()) { + profile.setBootLoaderType(BootloaderType.CD); + } + GuestOSVO guestOS = _guestOSDao.findById(template.getGuestOSId()); + String displayName = null; + if (guestOS != null) { + displayName = guestOS.getDisplayName(); + } + VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, + StoragePoolType.ISO, null, template.getName(), null, + isoPath, 0, null, displayName); + + iso.setDeviceId(3); + profile.addDisk(iso); + } else { + VirtualMachineTemplate template = profile.getTemplate(); + /* create a iso placeholder */ + VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, + StoragePoolType.ISO, null, template.getName(), null, null, + 0, null); + iso.setDeviceId(3); + profile.addDisk(iso); + } + + return true; + } + + @Override + public boolean finalizeDeployment(Commands cmds, + VirtualMachineProfile profile, DeployDestination dest, + ReservationContext context) { + UserVmVO userVm = profile.getVirtualMachine(); + List nics = _nicDao.listByVmId(userVm.getId()); + for (NicVO nic : nics) { + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + if (network.getTrafficType() == TrafficType.Guest + || network.getTrafficType() == TrafficType.Public) { + userVm.setPrivateIpAddress(nic.getIp4Address()); + userVm.setPrivateMacAddress(nic.getMacAddress()); + } + } + return true; + } + + @Override + public boolean finalizeCommandsOnStart(Commands cmds, + VirtualMachineProfile profile) { + return true; + } + + @Override + public boolean finalizeStart(VirtualMachineProfile profile, + long hostId, Commands cmds, ReservationContext context) { + UserVmVO vm = profile.getVirtualMachine(); + + Answer[] answersToCmds = cmds.getAnswers(); + if (answersToCmds == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Returning from finalizeStart() since there are no answers to read"); + } + return true; + } + Answer startAnswer = cmds.getAnswer(StartAnswer.class); + String returnedIp = null; + String originalIp = null; + if (startAnswer != null) { + StartAnswer startAns = (StartAnswer) startAnswer; + VirtualMachineTO vmTO = startAns.getVirtualMachine(); + for (NicTO nicTO : vmTO.getNics()) { + if (nicTO.getType() == TrafficType.Guest) { + returnedIp = nicTO.getIp(); + } + } + } + + List nics = _nicDao.listByVmId(vm.getId()); + NicVO guestNic = null; + NetworkVO guestNetwork = null; + for (NicVO nic : nics) { + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + long isDefault = (nic.isDefaultNic()) ? 1 : 0; + UsageEventVO usageEvent = new UsageEventVO( + EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, + vm.getAccountId(), vm.getDataCenterIdToDeployIn(), + vm.getId(), vm.getHostName(), + network.getNetworkOfferingId(), null, isDefault); + _usageEventDao.persist(usageEvent); + if (network.getTrafficType() == TrafficType.Guest) { + originalIp = nic.getIp4Address(); + guestNic = nic; + guestNetwork = network; + } + } + boolean ipChanged = false; + if (originalIp != null && !originalIp.equalsIgnoreCase(returnedIp)) { + if (returnedIp != null && guestNic != null) { + guestNic.setIp4Address(returnedIp); + ipChanged = true; + } + } + if (returnedIp != null && !returnedIp.equalsIgnoreCase(originalIp)) { + if (guestNic != null) { + guestNic.setIp4Address(returnedIp); + ipChanged = true; + } + } + if (ipChanged) { + DataCenterVO dc = _dcDao.findById(vm.getDataCenterIdToDeployIn()); + UserVmVO userVm = profile.getVirtualMachine(); + // dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName()) + if (_ntwkSrvcDao.canProviderSupportServiceInNetwork( + guestNetwork.getId(), Service.Dhcp, + Provider.ExternalDhcpServer)) { + _nicDao.update(guestNic.getId(), guestNic); + userVm.setPrivateIpAddress(guestNic.getIp4Address()); + _vmDao.update(userVm.getId(), userVm); + + s_logger.info("Detected that ip changed in the answer, updated nic in the db with new ip " + + returnedIp); + } + } + + // get system ip and create static nat rule for the vm + try { + _rulesMgr.getSystemIpAndEnableStaticNatForVm( + profile.getVirtualMachine(), false); + } catch (Exception ex) { + s_logger.warn( + "Failed to get system ip and enable static nat for the vm " + + profile.getVirtualMachine() + + " due to exception ", ex); + return false; + } + + return true; + } + + @Override + public void finalizeExpunge(UserVmVO vm) { + } + + @Override + public UserVmVO persist(UserVmVO vm) { + return _vmDao.persist(vm); + } + + @Override + public UserVmVO findById(long id) { + return _vmDao.findById(id); + } + + @Override + public UserVmVO findByName(String name) { + if (!VirtualMachineName.isValidVmName(name)) { + return null; + } + return findById(VirtualMachineName.getVmId(name)); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_STOP, eventDescription = "stopping Vm", async = true) + public UserVm stopVirtualMachine(long vmId, boolean forced) + throws ConcurrentOperationException { + // Input validation + Account caller = UserContext.current().getCaller(); + Long userId = UserContext.current().getCallerUserId(); + + // if account is removed, return error + if (caller != null && caller.getRemoved() != null) { + throw new PermissionDeniedException("The account " + caller.getId() + + " is removed"); + } + + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException( + "unable to find a virtual machine with id " + vmId); + } + + _accountMgr.checkAccess(caller, null, true, vm); + UserVO user = _userDao.findById(userId); + + try { + _itMgr.advanceStop(vm, forced, user, caller); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException( + "Unable to contact the agent to stop the virtual machine " + + vm, e); + } catch (OperationTimedoutException e) { + throw new CloudRuntimeException( + "Unable to contact the agent to stop the virtual machine " + + vm, e); + } + + return _vmDao.findById(vmId); + } + + @Override + public void finalizeStop(VirtualMachineProfile profile, + StopAnswer answer) { + // release elastic IP here + IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(profile.getId()); + if (ip != null && ip.getSystem()) { + UserContext ctx = UserContext.current(); + try { + _rulesMgr.disableStaticNat(ip.getId(), ctx.getCaller(), + ctx.getCallerUserId(), true); + } catch (Exception ex) { + s_logger.warn( + "Failed to disable static nat and release system ip " + + ip + " as a part of vm " + + profile.getVirtualMachine() + + " stop due to exception ", ex); + } + } + } + + public String generateRandomPassword() { + return PasswordGenerator.generateRandomPassword(6); + } + + @Override + public Pair> startVirtualMachine( + long vmId, Long hostId, + Map additionalParams) + throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { + // Input validation + Account callerAccount = UserContext.current().getCaller(); + UserVO callerUser = _userDao.findById(UserContext.current() + .getCallerUserId()); + + // if account is removed, return error + if (callerAccount != null && callerAccount.getRemoved() != null) { + throw new InvalidParameterValueException("The account " + + callerAccount.getId() + " is removed"); + } + + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException( + "unable to find a virtual machine with id " + vmId); + } + + _accountMgr.checkAccess(callerAccount, null, true, vm); + + Account owner = _accountDao.findById(vm.getAccountId()); + + if (owner == null) { + throw new InvalidParameterValueException("The owner of " + vm + + " does not exist: " + vm.getAccountId()); + } + + if (owner.getState() == Account.State.disabled) { + throw new PermissionDeniedException("The owner of " + vm + + " is disabled: " + vm.getAccountId()); + } + + Host destinationHost = null; + if (hostId != null) { + Account account = UserContext.current().getCaller(); + if (!_accountService.isRootAdmin(account.getType())) { + throw new PermissionDeniedException( + "Parameter hostid can only be specified by a Root Admin, permission denied"); + } + destinationHost = _hostDao.findById(hostId); + if (destinationHost == null) { + throw new InvalidParameterValueException( + "Unable to find the host to deploy the VM, host id=" + + hostId); + } + } + + // check if vm is security group enabled + if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) + && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() + && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) + && _networkMgr.canAddDefaultSecurityGroup()) { + // if vm is not mapped to security group, create a mapping + if (s_logger.isDebugEnabled()) { + s_logger.debug("Vm " + + vm + + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); + } + + SecurityGroup defaultSecurityGroup = _securityGroupMgr + .getDefaultSecurityGroup(vm.getAccountId()); + if (defaultSecurityGroup != null) { + List groupList = new ArrayList(); + groupList.add(defaultSecurityGroup.getId()); + _securityGroupMgr.addInstanceToGroups(vmId, groupList); + } + } + + DataCenterDeployment plan = null; + if (destinationHost != null) { + s_logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterIdToDeployIn(), + destinationHost.getPodId(), destinationHost.getClusterId(), + destinationHost.getId(), null, null); + } + + // Set parameters + Map params = null; + VMTemplateVO template = null; + if (vm.isUpdateParameters()) { + _vmDao.loadDetails(vm); + // Check that the password was passed in and is valid + template = _templateDao + .findByIdIncludingRemoved(vm.getTemplateId()); + + String password = "saved_password"; + if (template.getEnablePassword()) { + password = generateRandomPassword(); + } + + if (!validPassword(password)) { + throw new InvalidParameterValueException( + "A valid password for this virtual machine was not provided."); + } + + // Check if an SSH key pair was selected for the instance and if so + // use it to encrypt & save the vm password + String sshPublicKey = vm.getDetail("SSH.PublicKey"); + if (sshPublicKey != null && !sshPublicKey.equals("") + && password != null && !password.equals("saved_password")) { + String encryptedPasswd = RSAHelper.encryptWithSSHPublicKey( + sshPublicKey, password); + if (encryptedPasswd == null) { + throw new CloudRuntimeException("Error encrypting password"); + } + + vm.setDetail("Encrypted.Password", encryptedPasswd); + _vmDao.saveDetails(vm); + } + + params = new HashMap(); + if (additionalParams != null) { + params.putAll(additionalParams); + } + params.put(VirtualMachineProfile.Param.VmPassword, password); + } + + vm = _itMgr.start(vm, params, callerUser, callerAccount, plan); + + Pair> vmParamPair = new Pair( + vm, params); + if (vm != null && vm.isUpdateParameters()) { + // this value is not being sent to the backend; need only for api + // display purposes + if (template.getEnablePassword()) { + vm.setPassword((String) vmParamPair.second().get( + VirtualMachineProfile.Param.VmPassword)); + vm.setUpdateParameters(false); + _vmDao.update(vm.getId(), vm); + } + } + + return vmParamPair; + } + + @Override + public UserVm destroyVm(long vmId) throws ResourceUnavailableException, + ConcurrentOperationException { + Account caller = UserContext.current().getCaller(); + Long userId = UserContext.current().getCallerUserId(); + + // Verify input parameters + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null || vm.getRemoved() != null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Unable to find a virtual machine with specified vmId"); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + + if (vm.getState() == State.Destroyed + || vm.getState() == State.Expunging) { + s_logger.trace("Vm id=" + vmId + " is already destroyed"); + return vm; + } + + _accountMgr.checkAccess(caller, null, true, vm); + User userCaller = _userDao.findById(userId); + + boolean status; + State vmState = vm.getState(); + + try { + status = _itMgr.destroy(vm, userCaller, caller); + } catch (OperationTimedoutException e) { + CloudRuntimeException ex = new CloudRuntimeException( + "Unable to destroy with specified vmId", e); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + + if (status) { + // Mark the account's volumes as destroyed + List volumes = _volsDao.findByInstance(vmId); + for (VolumeVO volume : volumes) { + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + UsageEventVO usageEvent = new UsageEventVO( + EventTypes.EVENT_VOLUME_DELETE, + volume.getAccountId(), volume.getDataCenterId(), + volume.getId(), volume.getName()); + _usageEventDao.persist(usageEvent); + } + } + + if (vmState != State.Error) { + _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), + ResourceType.user_vm); + } + + return _vmDao.findById(vmId); + } else { + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to destroy vm with specified vmId"); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + } + + @Override + public List searchForUserVMs(ListVMsCmd cmd) { + Account caller = UserContext.current().getCaller(); + List permittedAccounts = new ArrayList(); + String hypervisor = cmd.getHypervisor(); + boolean listAll = cmd.listAll(); + Long id = cmd.getId(); + Map tags = cmd.getTags(); + + Ternary domainIdRecursiveListProject = new Ternary( + cmd.getDomainId(), cmd.isRecursive(), null); + _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), + cmd.getProjectId(), permittedAccounts, + domainIdRecursiveListProject, listAll, false); + Long domainId = domainIdRecursiveListProject.first(); + Boolean isRecursive = domainIdRecursiveListProject.second(); + ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject + .third(); + + Criteria c = new Criteria("id", Boolean.TRUE, cmd.getStartIndex(), + cmd.getPageSizeVal()); + c.addCriteria(Criteria.KEYWORD, cmd.getKeyword()); + c.addCriteria(Criteria.ID, cmd.getId()); + c.addCriteria(Criteria.NAME, cmd.getInstanceName()); + c.addCriteria(Criteria.STATE, cmd.getState()); + c.addCriteria(Criteria.DATACENTERID, cmd.getZoneId()); + c.addCriteria(Criteria.GROUPID, cmd.getGroupId()); + c.addCriteria(Criteria.FOR_VIRTUAL_NETWORK, cmd.getForVirtualNetwork()); + c.addCriteria(Criteria.NETWORKID, cmd.getNetworkId()); + c.addCriteria(Criteria.TEMPLATE_ID, cmd.getTemplateId()); + c.addCriteria(Criteria.ISO_ID, cmd.getIsoId()); + c.addCriteria(Criteria.VPC_ID, cmd.getVpcId()); + + if (domainId != null) { + c.addCriteria(Criteria.DOMAINID, domainId); + } + + if (HypervisorType.getType(hypervisor) != HypervisorType.None) { + c.addCriteria(Criteria.HYPERVISOR, hypervisor); + } else if (hypervisor != null) { + throw new InvalidParameterValueException("Invalid HypervisorType " + + hypervisor); + } + + // ignore these search requests if it's not an admin + if (_accountMgr.isAdmin(caller.getType())) { + c.addCriteria(Criteria.PODID, cmd.getPodId()); + c.addCriteria(Criteria.HOSTID, cmd.getHostId()); + c.addCriteria(Criteria.STORAGE_ID, cmd.getStorageId()); + } + + if (!permittedAccounts.isEmpty()) { + c.addCriteria(Criteria.ACCOUNTID, permittedAccounts.toArray()); + } + c.addCriteria(Criteria.ISADMIN, _accountMgr.isAdmin(caller.getType())); + + return searchForUserVMs(c, caller, domainId, isRecursive, + permittedAccounts, listAll, listProjectResourcesCriteria, tags); + } + + @Override + public List searchForUserVMs(Criteria c, Account caller, + Long domainId, boolean isRecursive, List permittedAccounts, + boolean listAll, + ListProjectResourcesCriteria listProjectResourcesCriteria, + Map tags) { + Filter searchFilter = new Filter(UserVmVO.class, c.getOrderBy(), + c.getAscending(), c.getOffset(), c.getLimit()); + + SearchBuilder sb = _vmDao.createSearchBuilder(); + _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, + permittedAccounts, listProjectResourcesCriteria); + + Object id = c.getCriteria(Criteria.ID); + Object name = c.getCriteria(Criteria.NAME); + Object state = c.getCriteria(Criteria.STATE); + Object notState = c.getCriteria(Criteria.NOTSTATE); + Object zone = c.getCriteria(Criteria.DATACENTERID); + Object pod = c.getCriteria(Criteria.PODID); + Object hostId = c.getCriteria(Criteria.HOSTID); + Object hostName = c.getCriteria(Criteria.HOSTNAME); + Object keyword = c.getCriteria(Criteria.KEYWORD); + Object isAdmin = c.getCriteria(Criteria.ISADMIN); + assert c.getCriteria(Criteria.IPADDRESS) == null : "We don't support search by ip address on VM any more. If you see this assert, it means we have to find a different way to search by the nic table."; + Object groupId = c.getCriteria(Criteria.GROUPID); + Object networkId = c.getCriteria(Criteria.NETWORKID); + Object hypervisor = c.getCriteria(Criteria.HYPERVISOR); + Object storageId = c.getCriteria(Criteria.STORAGE_ID); + Object templateId = c.getCriteria(Criteria.TEMPLATE_ID); + Object isoId = c.getCriteria(Criteria.ISO_ID); + Object vpcId = c.getCriteria(Criteria.VPC_ID); + + sb.and("displayName", sb.entity().getDisplayName(), + SearchCriteria.Op.LIKE); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getHostName(), SearchCriteria.Op.LIKE); + sb.and("stateEQ", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ); + sb.and("stateNIN", sb.entity().getState(), SearchCriteria.Op.NIN); + sb.and("dataCenterId", sb.entity().getDataCenterIdToDeployIn(), + SearchCriteria.Op.EQ); + sb.and("podId", sb.entity().getPodIdToDeployIn(), SearchCriteria.Op.EQ); + sb.and("hypervisorType", sb.entity().getHypervisorType(), + SearchCriteria.Op.EQ); + sb.and("hostIdEQ", sb.entity().getHostId(), SearchCriteria.Op.EQ); + sb.and("hostIdIN", sb.entity().getHostId(), SearchCriteria.Op.IN); + sb.and("templateId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); + sb.and("isoId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); + + if (groupId != null && (Long) groupId == -1) { + SearchBuilder vmSearch = _groupVMMapDao + .createSearchBuilder(); + vmSearch.and("instanceId", vmSearch.entity().getInstanceId(), + SearchCriteria.Op.EQ); + sb.join("vmSearch", vmSearch, sb.entity().getId(), vmSearch + .entity().getInstanceId(), JoinBuilder.JoinType.LEFTOUTER); + } else if (groupId != null) { + SearchBuilder groupSearch = _groupVMMapDao + .createSearchBuilder(); + groupSearch.and("groupId", groupSearch.entity().getGroupId(), + SearchCriteria.Op.EQ); + sb.join("groupSearch", groupSearch, sb.entity().getId(), + groupSearch.entity().getInstanceId(), + JoinBuilder.JoinType.INNER); + } + + if (tags != null && !tags.isEmpty()) { + SearchBuilder tagSearch = _resourceTagDao + .createSearchBuilder(); + for (int count = 0; count < tags.size(); count++) { + tagSearch.or().op("key" + String.valueOf(count), + tagSearch.entity().getKey(), SearchCriteria.Op.EQ); + tagSearch.and("value" + String.valueOf(count), tagSearch + .entity().getValue(), SearchCriteria.Op.EQ); + tagSearch.cp(); + } + tagSearch.and("resourceType", tagSearch.entity().getResourceType(), + SearchCriteria.Op.EQ); + sb.groupBy(sb.entity().getId()); + sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch + .entity().getResourceId(), JoinBuilder.JoinType.INNER); + } + + if (networkId != null) { + SearchBuilder nicSearch = _nicDao.createSearchBuilder(); + nicSearch.and("networkId", nicSearch.entity().getNetworkId(), + SearchCriteria.Op.EQ); + + SearchBuilder networkSearch = _networkDao + .createSearchBuilder(); + networkSearch.and("networkId", networkSearch.entity().getId(), + SearchCriteria.Op.EQ); + nicSearch.join("networkSearch", networkSearch, nicSearch.entity() + .getNetworkId(), networkSearch.entity().getId(), + JoinBuilder.JoinType.INNER); + + sb.join("nicSearch", nicSearch, sb.entity().getId(), nicSearch + .entity().getInstanceId(), JoinBuilder.JoinType.INNER); + } + + if (vpcId != null && networkId == null) { + SearchBuilder nicSearch = _nicDao.createSearchBuilder(); + + SearchBuilder networkSearch = _networkDao + .createSearchBuilder(); + nicSearch.join("networkSearch", networkSearch, nicSearch.entity() + .getNetworkId(), networkSearch.entity().getId(), + JoinBuilder.JoinType.INNER); + + SearchBuilder vpcSearch = _vpcDao.createSearchBuilder(); + vpcSearch.and("vpcId", vpcSearch.entity().getId(), + SearchCriteria.Op.EQ); + networkSearch.join("vpcSearch", vpcSearch, networkSearch.entity() + .getVpcId(), vpcSearch.entity().getId(), + JoinBuilder.JoinType.INNER); + + sb.join("nicSearch", nicSearch, sb.entity().getId(), nicSearch + .entity().getInstanceId(), JoinBuilder.JoinType.INNER); + } + + if (storageId != null) { + SearchBuilder volumeSearch = _volsDao + .createSearchBuilder(); + volumeSearch.and("poolId", volumeSearch.entity().getPoolId(), + SearchCriteria.Op.EQ); + sb.join("volumeSearch", volumeSearch, sb.entity().getId(), + volumeSearch.entity().getInstanceId(), + JoinBuilder.JoinType.INNER); + } + + // populate the search criteria with the values passed in + SearchCriteria sc = sb.create(); + _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, + permittedAccounts, listProjectResourcesCriteria); + + if (tags != null && !tags.isEmpty()) { + int count = 0; + sc.setJoinParameters("tagSearch", "resourceType", + TaggedResourceType.UserVm.toString()); + for (String key : tags.keySet()) { + sc.setJoinParameters("tagSearch", + "key" + String.valueOf(count), key); + sc.setJoinParameters("tagSearch", + "value" + String.valueOf(count), tags.get(key)); + count++; + } + } + + if (groupId != null && (Long) groupId == -1) { + sc.setJoinParameters("vmSearch", "instanceId", (Object) null); + } else if (groupId != null) { + sc.setJoinParameters("groupSearch", "groupId", groupId); + } + + if (keyword != null) { + SearchCriteria ssc = _vmDao.createSearchCriteria(); + ssc.addOr("displayName", SearchCriteria.Op.LIKE, "%" + keyword + + "%"); + ssc.addOr("hostName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("instanceName", SearchCriteria.Op.LIKE, "%" + keyword + + "%"); + ssc.addOr("state", SearchCriteria.Op.EQ, keyword); + + sc.addAnd("displayName", SearchCriteria.Op.SC, ssc); + } + + if (id != null) { + sc.setParameters("id", id); + } + + if (templateId != null) { + sc.setParameters("templateId", templateId); + } + + if (isoId != null) { + sc.setParameters("isoId", isoId); + } + + if (networkId != null) { + sc.setJoinParameters("nicSearch", "networkId", networkId); + } + + if (vpcId != null && networkId == null) { + sc.setJoinParameters("vpcSearch", "vpcId", vpcId); + } + + if (name != null) { + sc.setParameters("name", "%" + name + "%"); + } + + if (state != null) { + if (notState != null && (Boolean) notState == true) { + sc.setParameters("stateNEQ", state); + } else { + sc.setParameters("stateEQ", state); + } + } + + if (hypervisor != null) { + sc.setParameters("hypervisorType", hypervisor); + } + + // Don't show Destroyed and Expunging vms to the end user + if ((isAdmin != null) && ((Boolean) isAdmin != true)) { + sc.setParameters("stateNIN", "Destroyed", "Expunging"); + } + + if (zone != null) { + sc.setParameters("dataCenterId", zone); + + if (state == null) { + sc.setParameters("stateNEQ", "Destroyed"); + } + } + if (pod != null) { + sc.setParameters("podId", pod); + + if (state == null) { + sc.setParameters("stateNEQ", "Destroyed"); + } + } + + if (hostId != null) { + sc.setParameters("hostIdEQ", hostId); + } else { + if (hostName != null) { + List hosts = _resourceMgr + .listHostsByNameLike((String) hostName); + if (hosts != null & !hosts.isEmpty()) { + Long[] hostIds = new Long[hosts.size()]; + for (int i = 0; i < hosts.size(); i++) { + HostVO host = hosts.get(i); + hostIds[i] = host.getId(); + } + sc.setParameters("hostIdIN", (Object[]) hostIds); + } else { + return new ArrayList(); + } + } + } + + if (storageId != null) { + sc.setJoinParameters("volumeSearch", "poolId", storageId); + } + s_logger.debug("THE WHERE CLAUSE IS:" + sc.getWhereClause()); + return _vmDao.search(sc, searchFilter); + } + + @Override + public HypervisorType getHypervisorTypeOfUserVM(long vmId) { + UserVmVO userVm = _vmDao.findById(vmId); + if (userVm == null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "unable to find a virtual machine with specified id"); + ex.addProxyObject(userVm, vmId, "vmId"); + throw ex; + } + + return userVm.getHypervisorType(); + } + + @Override + public UserVm createVirtualMachine(DeployVMCmd cmd) + throws InsufficientCapacityException, ResourceUnavailableException, + ConcurrentOperationException, StorageUnavailableException, + ResourceAllocationException { + // TODO Auto-generated method stub + return null; + } + + @Override + public UserVm getUserVm(long vmId) { + return _vmDao.findById(vmId); + } + + @Override + public VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool) { + // access check - only root admin can migrate VM + Account caller = UserContext.current().getCaller(); + if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); + } + throw new PermissionDeniedException( + "No permission to migrate VM, Only Root Admin can migrate a VM!"); + } + + VMInstanceVO vm = _vmInstanceDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException( + "Unable to find the VM by id=" + vmId); + } + + if (vm.getState() != State.Stopped) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "VM is not Stopped, unable to migrate the vm having the specified id"); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + + if (vm.getType() != VirtualMachine.Type.User) { + throw new InvalidParameterValueException( + "can only do storage migration on user vm"); + } + + List vols = _volsDao.findByInstance(vm.getId()); + if (vols.size() > 1) { + throw new InvalidParameterValueException( + "Data disks attached to the vm, can not migrate. Need to dettach data disks at first"); + } + + HypervisorType destHypervisorType = _clusterDao.findById( + destPool.getClusterId()).getHypervisorType(); + if (vm.getHypervisorType() != destHypervisorType) { + throw new InvalidParameterValueException( + "hypervisor is not compatible: dest: " + + destHypervisorType.toString() + ", vm: " + + vm.getHypervisorType().toString()); + } + VMInstanceVO migratedVm = _itMgr.storageMigration(vm, destPool); + return migratedVm; + + } + + private boolean isVMUsingLocalStorage(VMInstanceVO vm) { + boolean usesLocalStorage = false; + ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm + .getServiceOfferingId()); + if (svcOffering.getUseLocalStorage()) { + usesLocalStorage = true; + } else { + List volumes = _volsDao.findByInstanceAndType(vm.getId(), + Volume.Type.DATADISK); + for (VolumeVO vol : volumes) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol + .getDiskOfferingId()); + if (diskOffering.getUseLocalStorage()) { + usesLocalStorage = true; + break; + } + } + } + return usesLocalStorage; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) + public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) + throws ResourceUnavailableException, ConcurrentOperationException, + ManagementServerException, VirtualMachineMigrationException { + // access check - only root admin can migrate VM + Account caller = UserContext.current().getCaller(); + if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); + } + throw new PermissionDeniedException( + "No permission to migrate VM, Only Root Admin can migrate a VM!"); + } + + VMInstanceVO vm = _vmInstanceDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException( + "Unable to find the VM by id=" + vmId); + } + // business logic + if (vm.getState() != State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is not Running, unable to migrate the vm " + + vm); + } + InvalidParameterValueException ex = new InvalidParameterValueException( + "VM is not Running, unable to migrate the vm with specified id"); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) + && !vm.getHypervisorType().equals(HypervisorType.VMware) + && !vm.getHypervisorType().equals(HypervisorType.KVM) + && !vm.getHypervisorType().equals(HypervisorType.Ovm)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(vm + + " is not XenServer/VMware/KVM/Ovm, cannot migrate this VM."); + } + throw new InvalidParameterValueException( + "Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM only"); + } + + if (isVMUsingLocalStorage(vm)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(vm + + " is using Local Storage, cannot migrate this VM."); + } + throw new InvalidParameterValueException( + "Unsupported operation, VM uses Local storage, cannot migrate"); + } + + // check if migrating to same host + long srcHostId = vm.getHostId(); + if (destinationHost.getId() == srcHostId) { + throw new InvalidParameterValueException( + "Cannot migrate VM, VM is already presnt on this host, please specify valid destination host to migrate the VM"); + } + + // check if host is UP + if (destinationHost.getStatus() != com.cloud.host.Status.Up + || destinationHost.getResourceState() != ResourceState.Enabled) { + throw new InvalidParameterValueException( + "Cannot migrate VM, destination host is not in correct state, has status: " + + destinationHost.getStatus() + ", state: " + + destinationHost.getResourceState()); + } + + // call to core process + DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId()); + HostPodVO pod = _podDao.findById(destinationHost.getPodId()); + Cluster cluster = _clusterDao.findById(destinationHost.getClusterId()); + DeployDestination dest = new DeployDestination(dcVO, pod, cluster, + destinationHost); + + // check max guest vm limit for the destinationHost + HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); + if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Host name: " + + destinationHost.getName() + + ", hostId: " + + destinationHost.getId() + + " already has max Running VMs(count includes system VMs), cannot migrate to this host"); + } + throw new VirtualMachineMigrationException( + "Destination host, hostId: " + + destinationHost.getId() + + " already has max Running VMs(count includes system VMs), cannot migrate to this host"); + } + + VMInstanceVO migratedVm = _itMgr.migrate(vm, srcHostId, dest); + return migratedVm; + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false) + public UserVm moveVMToUser(AssignVMCmd cmd) + throws ResourceAllocationException, ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + // VERIFICATIONS and VALIDATIONS + + // VV 1: verify the two users + Account caller = UserContext.current().getCaller(); + if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN + && caller.getType() != Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { // only + // root + // admin + // can + // assign + // VMs + throw new InvalidParameterValueException( + "Only domain admins are allowed to assign VMs and not " + + caller.getType()); + } + + // get and check the valid VM + UserVmVO vm = _vmDao.findById(cmd.getVmId()); + if (vm == null) { + throw new InvalidParameterValueException( + "There is no vm by that id " + cmd.getVmId()); + } else if (vm.getState() == State.Running) { // VV 3: check if vm is + // running + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is Running, unable to move the vm " + vm); + } + InvalidParameterValueException ex = new InvalidParameterValueException( + "VM is Running, unable to move the vm with specified vmId"); + ex.addProxyObject(vm, cmd.getVmId(), "vmId"); + throw ex; + } + + Account oldAccount = _accountService.getActiveAccountById(vm + .getAccountId()); + if (oldAccount == null) { + throw new InvalidParameterValueException("Invalid account for VM " + + vm.getAccountId() + " in domain."); + } + // don't allow to move the vm from the project + if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Specified Vm id belongs to the project and can't be moved"); + ex.addProxyObject(vm, cmd.getVmId(), "vmId"); + throw ex; + } + Account newAccount = _accountService.getActiveAccountByName( + cmd.getAccountName(), cmd.getDomainId()); + if (newAccount == null + || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) { + throw new InvalidParameterValueException("Invalid accountid=" + + cmd.getAccountName() + " in domain " + cmd.getDomainId()); + } + + if (newAccount.getState() == Account.State.disabled) { + throw new InvalidParameterValueException("The new account owner " + + cmd.getAccountName() + " is disabled."); + } + + // make sure the accounts are under same domain + if (oldAccount.getDomainId() != newAccount.getDomainId()) { + throw new InvalidParameterValueException( + "The account should be under same domain for moving VM between two accounts. Old owner domain =" + + oldAccount.getDomainId() + + " New owner domain=" + + newAccount.getDomainId()); + } + + // make sure the accounts are not same + if (oldAccount.getAccountId() == newAccount.getAccountId()) { + throw new InvalidParameterValueException( + "The account should be same domain for moving VM between two accounts. Account id =" + + oldAccount.getAccountId()); + } + + // don't allow to move the vm if there are existing PF/LB/Static Nat + // rules, or vm is assigned to static Nat ip + List pfrules = _portForwardingDao.listByVm(cmd + .getVmId()); + if (pfrules != null && pfrules.size() > 0) { + throw new InvalidParameterValueException( + "Remove the Port forwarding rules for this VM before assigning to another user."); + } + List snrules = _rulesDao + .listStaticNatByVmId(vm.getId()); + if (snrules != null && snrules.size() > 0) { + throw new InvalidParameterValueException( + "Remove the StaticNat rules for this VM before assigning to another user."); + } + List maps = _loadBalancerVMMapDao + .listByInstanceId(vm.getId()); + if (maps != null && maps.size() > 0) { + throw new InvalidParameterValueException( + "Remove the load balancing rules for this VM before assigning to another user."); + } + // check for one on one nat + IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(cmd.getVmId()); + if (ip != null) { + if (ip.isOneToOneNat()) { + throw new InvalidParameterValueException( + "Remove the one to one nat rule for this VM for ip " + + ip.toString()); + } + } + + DataCenterVO zone = _dcDao.findById(vm.getDataCenterIdToDeployIn()); + + // Remove vm from instance group + removeInstanceFromInstanceGroup(cmd.getVmId()); + + // VV 2: check if account/domain is with in resource limits to create a + // new vm + _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.user_vm); + + // VV 3: check if volumes are with in resource limits + _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.volume, + _volsDao.findByInstance(cmd.getVmId()).size()); + + // VV 4: Check if new owner can use the vm template + VirtualMachineTemplate template = _templateDao.findById(vm + .getTemplateId()); + if (!template.isPublicTemplate()) { + Account templateOwner = _accountMgr.getAccount(template + .getAccountId()); + _accountMgr.checkAccess(newAccount, null, true, templateOwner); + } + + // VV 5: check the new account can create vm in the domain + DomainVO domain = _domainDao.findById(cmd.getDomainId()); + _accountMgr.checkAccess(newAccount, domain); + + Transaction txn = Transaction.currentTxn(); + txn.start(); + // generate destroy vm event for usage + _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_DESTROY, vm + .getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm + .getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), + vm.getHypervisorType().toString())); + // update resource counts + _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), + ResourceType.user_vm); + + // OWNERSHIP STEP 1: update the vm owner + vm.setAccountId(newAccount.getAccountId()); + vm.setDomainId(cmd.getDomainId()); + _vmDao.persist(vm); + + // OS 2: update volume + List volumes = _volsDao.findByInstance(cmd.getVmId()); + for (VolumeVO volume : volumes) { + _usageEventDao + .persist(new UsageEventVO(EventTypes.EVENT_VOLUME_DELETE, + volume.getAccountId(), volume.getDataCenterId(), + volume.getId(), volume.getName())); + _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), + ResourceType.volume); + volume.setAccountId(newAccount.getAccountId()); + _volsDao.persist(volume); + _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), + ResourceType.volume); + _usageEventDao.persist(new UsageEventVO( + EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), + volume.getDataCenterId(), volume.getId(), volume.getName(), + volume.getDiskOfferingId(), volume.getTemplateId(), volume + .getSize())); + // snapshots: mark these removed in db + List snapshots = _snapshotDao + .listByVolumeIdIncludingRemoved(volume.getId()); + for (SnapshotVO snapshot : snapshots) { + _snapshotDao.remove(snapshot.getId()); + } + } + + _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), + ResourceType.user_vm); + // generate usage events to account for this change + _usageEventDao.persist(new UsageEventVO(EventTypes.EVENT_VM_CREATE, vm + .getAccountId(), vm.getDataCenterIdToDeployIn(), vm.getId(), vm + .getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(), + vm.getHypervisorType().toString())); + + txn.commit(); + + VMInstanceVO vmoi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); + VirtualMachineProfileImpl vmOldProfile = new VirtualMachineProfileImpl( + vmoi); + + // OS 3: update the network + List networkIdList = cmd.getNetworkIds(); + List securityGroupIdList = cmd.getSecurityGroupIdList(); + + if (zone.getNetworkType() == NetworkType.Basic) { + if (networkIdList != null && !networkIdList.isEmpty()) { + throw new InvalidParameterValueException( + "Can't move vm with network Ids; this is a basic zone VM"); + } + // cleanup the old security groups + _securityGroupMgr.removeInstanceFromGroups(cmd.getVmId()); + // cleanup the network for the oldOwner + _networkMgr.cleanupNics(vmOldProfile); + _networkMgr.expungeNics(vmOldProfile); + // security groups will be recreated for the new account, when the + // VM is started + List networkList = new ArrayList(); + + // Get default guest network in Basic zone + Network defaultNetwork = _networkMgr.getExclusiveGuestNetwork(zone + .getId()); + + if (defaultNetwork == null) { + throw new InvalidParameterValueException( + "Unable to find a default network to start a vm"); + } else { + networkList.add(_networkDao.findById(defaultNetwork.getId())); + } + + boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware); + + if (securityGroupIdList != null && isVmWare) { + throw new InvalidParameterValueException( + "Security group feature is not supported for vmWare hypervisor"); + } else if (!isVmWare + && _networkMgr + .isSecurityGroupSupportedInNetwork(defaultNetwork) + && _networkMgr.canAddDefaultSecurityGroup()) { + if (securityGroupIdList == null) { + securityGroupIdList = new ArrayList(); + } + SecurityGroup defaultGroup = _securityGroupMgr + .getDefaultSecurityGroup(newAccount.getId()); + if (defaultGroup != null) { + // check if security group id list already contains Default + // security group, and if not - add it + boolean defaultGroupPresent = false; + for (Long securityGroupId : securityGroupIdList) { + if (securityGroupId.longValue() == defaultGroup.getId()) { + defaultGroupPresent = true; + break; + } + } + + if (!defaultGroupPresent) { + securityGroupIdList.add(defaultGroup.getId()); + } + + } else { + // create default security group for the account + if (s_logger.isDebugEnabled()) { + s_logger.debug("Couldn't find default security group for the account " + + newAccount + " so creating a new one"); + } + defaultGroup = _securityGroupMgr.createSecurityGroup( + SecurityGroupManager.DEFAULT_GROUP_NAME, + SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, + newAccount.getDomainId(), newAccount.getId(), + newAccount.getAccountName()); + securityGroupIdList.add(defaultGroup.getId()); + } + } + + List> networks = new ArrayList>(); + NicProfile profile = new NicProfile(); + profile.setDefaultNic(true); + networks.add(new Pair(networkList.get(0), + profile)); + + VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), vm.getId()); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl( + vmi); + _networkMgr.allocate(vmProfile, networks); + + _securityGroupMgr.addInstanceToGroups(vm.getId(), + securityGroupIdList); + + s_logger.debug("AssignVM: Basic zone, adding security groups no " + + securityGroupIdList.size() + " to " + + vm.getInstanceName()); + } else { + if (zone.isSecurityGroupEnabled()) { + throw new InvalidParameterValueException( + "Not yet implemented for SecurityGroupEnabled advanced networks."); + } else { + if (securityGroupIdList != null + && !securityGroupIdList.isEmpty()) { + throw new InvalidParameterValueException( + "Can't move vm with security groups; security group feature is not enabled in this zone"); + } + // cleanup the network for the oldOwner + _networkMgr.cleanupNics(vmOldProfile); + _networkMgr.expungeNics(vmOldProfile); + + Set applicableNetworks = new HashSet(); + + if (networkIdList != null && !networkIdList.isEmpty()) { + // add any additional networks + for (Long networkId : networkIdList) { + NetworkVO network = _networkDao.findById(networkId); + if (network == null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Unable to find specified network id"); + ex.addProxyObject(network, networkId, "networkId"); + throw ex; + } + + _networkMgr + .checkNetworkPermissions(newAccount, network); + + // don't allow to use system networks + NetworkOffering networkOffering = _configMgr + .getNetworkOffering(network + .getNetworkOfferingId()); + if (networkOffering.isSystemOnly()) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Specified Network id is system only and can't be used for vm deployment"); + ex.addProxyObject(network, networkId, "networkId"); + throw ex; + } + applicableNetworks.add(network); + } + } else { + NetworkVO defaultNetwork = null; + List requiredOfferings = _networkOfferingDao + .listByAvailability(Availability.Required, false); + if (requiredOfferings.size() < 1) { + throw new InvalidParameterValueException( + "Unable to find network offering with availability=" + + Availability.Required + + " to automatically create the network as a part of vm creation"); + } + if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { + // get Virtual networks + List virtualNetworks = _networkMgr + .listNetworksForAccount(newAccount.getId(), + zone.getId(), + Network.GuestType.Isolated); + + if (virtualNetworks.isEmpty()) { + long physicalNetworkId = _networkMgr + .findPhysicalNetworkId(zone.getId(), + requiredOfferings.get(0).getTags(), + requiredOfferings.get(0) + .getTrafficType()); + // Validate physical network + PhysicalNetwork physicalNetwork = _physicalNetworkDao + .findById(physicalNetworkId); + if (physicalNetwork == null) { + throw new InvalidParameterValueException( + "Unable to find physical network with id: " + + physicalNetworkId + + " and tag: " + + requiredOfferings.get(0) + .getTags()); + } + + s_logger.debug("Creating network for account " + + newAccount + + " from the network offering id=" + + requiredOfferings.get(0).getId() + + " as a part of deployVM process"); + Network newNetwork = _networkMgr + .createGuestNetwork(requiredOfferings + .get(0).getId(), + newAccount.getAccountName() + + "-network", + newAccount.getAccountName() + + "-network", null, null, + null, null, newAccount, null, + physicalNetwork, zone.getId(), + ACLType.Account, null, null); + defaultNetwork = _networkDao.findById(newNetwork + .getId()); + } else if (virtualNetworks.size() > 1) { + throw new InvalidParameterValueException( + "More than 1 default Isolated networks are found " + + "for account " + newAccount + + "; please specify networkIds"); + } else { + defaultNetwork = virtualNetworks.get(0); + } + } else { + throw new InvalidParameterValueException( + "Required network offering id=" + + requiredOfferings.get(0).getId() + + " is not in " + + NetworkOffering.State.Enabled); + } + + applicableNetworks.add(defaultNetwork); + } + + // add the new nics + List> networks = new ArrayList>(); + int toggle = 0; + for (NetworkVO appNet : applicableNetworks) { + NicProfile defaultNic = new NicProfile(); + if (toggle == 0) { + defaultNic.setDefaultNic(true); + toggle++; + } + networks.add(new Pair(appNet, + defaultNic)); + } + VMInstanceVO vmi = _itMgr.findByIdAndType(vm.getType(), + vm.getId()); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl( + vmi); + _networkMgr.allocate(vmProfile, networks); + s_logger.debug("AssignVM: Advance virtual, adding networks no " + + networks.size() + " to " + vm.getInstanceName()); + } // END IF NON SEC GRP ENABLED + } // END IF ADVANCED + s_logger.info("AssignVM: vm " + vm.getInstanceName() + + " now belongs to account " + cmd.getAccountName()); + return vm; + } + + @Override + public UserVm restoreVM(RestoreVMCmd cmd) { + // Input validation + Account caller = UserContext.current().getCaller(); + Long userId = UserContext.current().getCallerUserId(); + UserVO user = _userDao.findById(userId); + boolean needRestart = false; + + long vmId = cmd.getVmId(); + UserVmVO vm = _vmDao.findById(vmId); + if (vm == null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Cann not find VM with ID " + vmId); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + + Account owner = _accountDao.findById(vm.getAccountId()); + if (owner == null) { + throw new InvalidParameterValueException("The owner of " + vm + + " does not exist: " + vm.getAccountId()); + } + + if (owner.getState() == Account.State.disabled) { + throw new PermissionDeniedException("The owner of " + vm + + " is disabled: " + vm.getAccountId()); + } + + if (vm.getState() != VirtualMachine.State.Running + && vm.getState() != VirtualMachine.State.Stopped) { + throw new CloudRuntimeException( + "Vm " + + vmId + + " currently in " + + vm.getState() + + " state, restore vm can only execute when VM in Running or Stopped"); + } + + if (vm.getState() == VirtualMachine.State.Running) { + needRestart = true; + } + + List rootVols = _volsDao.findByInstance(vmId); + if (rootVols.isEmpty()) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Can not find root volume for VM " + vmId); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + + VolumeVO root = rootVols.get(0); + long templateId = root.getTemplateId(); + VMTemplateVO template = _templateDao.findById(templateId); + if (template == null) { + InvalidParameterValueException ex = new InvalidParameterValueException( + "Cannot find template for specified volumeid and vmId"); + ex.addProxyObject(vm, vmId, "vmId"); + ex.addProxyObject(root, root.getId(), "volumeId"); + throw ex; + } + + if (needRestart) { + try { + _itMgr.stop(vm, user, caller); + } catch (ResourceUnavailableException e) { + s_logger.debug("Stop vm " + vmId + " failed", e); + CloudRuntimeException ex = new CloudRuntimeException( + "Stop vm failed for specified vmId"); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + } + + /* allocate a new volume from original template */ + VolumeVO newVol = _storageMgr.allocateDuplicateVolume(root, null); + _volsDao.attachVolume(newVol.getId(), vmId, newVol.getDeviceId()); + + /* Detach and destory the old root volume */ + try { + _volsDao.detachVolume(root.getId()); + _storageMgr.destroyVolume(root); + } catch (ConcurrentOperationException e) { + s_logger.debug("Unable to delete old root volume " + root.getId() + + ", user may manually delete it", e); + } + + if (needRestart) { + try { + _itMgr.start(vm, null, user, caller); + } catch (Exception e) { + s_logger.debug("Unable to start VM " + vmId, e); + CloudRuntimeException ex = new CloudRuntimeException( + "Unable to start VM with specified id" + e.getMessage()); + ex.addProxyObject(vm, vmId, "vmId"); + throw ex; + } + } + + s_logger.debug("Restore VM " + vmId + " with template " + + root.getTemplateId() + " successfully"); + return vm; + } + + @Override + public boolean plugNic(Network network, NicTO nic, VirtualMachineTO vm, + ReservationContext context, DeployDestination dest) + throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { + // not supported + throw new UnsupportedOperationException( + "Plug nic is not supported for vm of type " + vm.getType()); + } + + @Override + public boolean unplugNic(Network network, NicTO nic, VirtualMachineTO vm, + ReservationContext context, DeployDestination dest) + throws ConcurrentOperationException, ResourceUnavailableException { + // not supported + throw new UnsupportedOperationException( + "Unplug nic is not supported for vm of type " + vm.getType()); + } + + @Override + public void prepareStop(VirtualMachineProfile profile) { + } + } diff --git a/server/src/com/cloud/vm/dao/VMInstanceDao.java b/server/src/com/cloud/vm/dao/VMInstanceDao.java index 2cf3d75018c..8939ff54cd9 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDao.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDao.java @@ -107,5 +107,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< * @return */ List listDistinctHostNames(long networkId, VirtualMachine.Type... types); + + VMInstanceVO findByUUID(String uuid); } diff --git a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index d97f4d78175..952827fe404 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -609,4 +609,10 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem txn.commit(); return result; } + + @Override + public VMInstanceVO findByUUID(String uuid) { + // TODO Auto-generated method stub + return null; + } } diff --git a/setup/db/4.1-new-db-schema.sql b/setup/db/4.1-new-db-schema.sql index 4dfcc4bcb16..a2c3a787caa 100644 --- a/setup/db/4.1-new-db-schema.sql +++ b/setup/db/4.1-new-db-schema.sql @@ -42,3 +42,25 @@ CREATE TABLE `cloud`.`image_data_store` ( PRIMARY KEY(`id`), CONSTRAINT `fk_tags__image_data_store_provider_id` FOREIGN KEY(`image_provider_id`) REFERENCES `image_data_store_provider`(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`vm_compute_tags` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `vm_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'vm id', + `compute_tag` varchar(255) NOT NULL COMMENT 'name of tag', + PRIMARY KEY(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`vm_root_disk_tags` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `vm_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'vm id', + `root_disk_tag` varchar(255) NOT NULL COMMENT 'name of tag', + PRIMARY KEY(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE `cloud`.`vm_network_map` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `vm_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'vm id', + `network_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'network id', + PRIMARY KEY(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file