/** * Abiquo community edition * cloud management application for hybrid clouds * Copyright (C) 2008-2010 - Abiquo Holdings S.L. * * This application is free software; you can redistribute it and/or * modify it under the terms of the GNU LESSER GENERAL PUBLIC * LICENSE as published by the Free Software Foundation under * version 3 of the License * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * LESSER GENERAL PUBLIC LICENSE v.3 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package com.abiquo.server.core.infrastructure.storage; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToOne; import javax.persistence.Table; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.hibernate.annotations.Filter; import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.FilterDefs; import org.hibernate.annotations.Filters; import org.hibernate.annotations.ForeignKey; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; import com.abiquo.model.enumerator.VolumeState; import com.abiquo.model.validation.IscsiPath; import com.abiquo.server.core.appslibrary.VirtualMachineTemplate; import com.abiquo.server.core.cloud.VirtualDatacenter; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.infrastructure.management.Rasd; import com.abiquo.server.core.infrastructure.management.RasdManagement; import com.softwarementors.validation.constraints.LeadingOrTrailingWhitespace; import com.softwarementors.validation.constraints.Required; @Entity @Table(name = VolumeManagement.TABLE_NAME) @FilterDefs({@FilterDef(name = VolumeManagement.NOT_TEMP), @FilterDef(name = VolumeManagement.ONLY_TEMP)}) @Filters({@Filter(name = VolumeManagement.NOT_TEMP, condition = "temporal is null"), @Filter(name = VolumeManagement.ONLY_TEMP, condition = "temporal is not null")}) @DiscriminatorValue(VolumeManagement.DISCRIMINATOR) @NamedQueries({ @NamedQuery(name = VolumeManagement.VOLUMES_ATTACHED_TO_VM, query = VolumeManagement.ATTACHED_TO_VM), @NamedQuery(name = VolumeManagement.VOLUMES_AVAILABLES, query = VolumeManagement.AVAILABLES), @NamedQuery(name = VolumeManagement.VOLUMES_BY_VDC, query = VolumeManagement.BY_VDC), @NamedQuery(name = VolumeManagement.VOLUMES_BY_POOL, query = VolumeManagement.BY_POOL)}) public class VolumeManagement extends RasdManagement { public static final String DISCRIMINATOR = "8"; // CIMResourceTypeEnum.iSCSI_HBA public static final String ALLOCATION_UNITS = "MegaBytes"; public static final String TABLE_NAME = "volume_management"; // Queries public static final String VOLUMES_BY_VDC = "VOLUMES_BY_VDC"; public static final String VOLUMES_BY_POOL = "VOLUMES_BY_POOL"; public static final String VOLUMES_ATTACHED_TO_VM = "VOLUMES_ATTACHED_TO_VM"; public static final String VOLUMES_AVAILABLES = "VOLUMES_AVAILABLES"; public static final String NOT_TEMP = "volumemanagement_not_temp"; public static final String ONLY_TEMP = "volumemanagement_only_temp"; public static final String BY_VDC = "SELECT vol FROM VolumeManagement vol LEFT JOIN vol.virtualMachine vm " + "LEFT JOIN vol.virtualAppliance vapp WHERE vol.virtualDatacenter.id = :vdcId " + "AND (vol.rasd.elementName like :filterLike OR vm.name like :filterLike " + "OR vapp.name like :filterLike OR vol.virtualDatacenter.name like :filterLike " + "OR vol.storagePool.tier.name like :filterLike )"; public static final String BY_POOL = "SELECT vol FROM VolumeManagement vol LEFT JOIN vol.virtualMachine vm " + "LEFT JOIN vol.virtualAppliance vapp WHERE vol.storagePool.idStorage = :poolId " + "AND (vol.rasd.elementName like :filterLike " + "OR vol.rasd.id like :filterLike OR vm.name like :filterLike " + "OR vapp.name like :filterLike OR vol.virtualDatacenter.name like :filterLike " + "OR vol.storagePool.tier.name like :filterLike )"; public static final String ATTACHED_TO_VM = "SELECT vol FROM VolumeManagement vol LEFT JOIN vol.virtualMachine vm " + "LEFT JOIN vol.virtualAppliance vapp " + "WHERE vm.id = :vmId AND vol.state = :state " + "AND (vol.rasd.elementName like :filterLike " + "OR vm.name like :filterLike " + "OR vapp.name like :filterLike " + "OR vol.virtualDatacenter.name like :filterLike " + "OR vol.storagePool.tier.name like :filterLike)" + " AND vol.virtualMachineTemplate IS NULL"; public static final String AVAILABLES = "SELECT vol FROM VolumeManagement vol LEFT JOIN vol.virtualMachine vm " + "WHERE vol.virtualDatacenter.id = :vdcId AND vm IS NULL " + "AND vol.virtualMachineTemplate IS NULL AND vol.rasd.elementName like :filterLike " + "AND vol NOT IN (SELECT stateful.volume FROM DiskStatefulConversion stateful)"; public static final String BY_VAPP = "SELECT vol FROM VolumeManagement vol LEFT JOIN vol.virtualMachine vm " + "LEFT JOIN vol.virtualAppliance vapp WHERE vapp.id = :vappId"; // DO NOT ACCESS: present due to needs of infrastructure support. *NEVER* call from business // code public VolumeManagement() { // Just for JPA support } public VolumeManagement(final String uuid, final String name, final long sizeInMB, final String idScsi, final StoragePool pool, final VirtualDatacenter virtualDatacenter) { super(DISCRIMINATOR); // RasdManagement properties Rasd rasd = new Rasd(uuid, name, Integer.valueOf(DISCRIMINATOR)); rasd.setAddress(pool.getDevice().getIscsiIp()); rasd.setAllocationUnits(ALLOCATION_UNITS); rasd.setAutomaticAllocation(0); rasd.setAutomaticDeallocation(0); setRasd(rasd); setVirtualDatacenter(virtualDatacenter); // Volume properties setStoragePool(pool); setIdScsi(idScsi); setState(VolumeState.DETACHED); setSizeInMB(sizeInMB); } public final static String STORAGE_POOL_PROPERTY = "storagePool"; private final static boolean STORAGE_POOL_REQUIRED = true; private final static String STORAGE_POOL_ID_COLUMN = "idStorage"; @JoinColumn(name = STORAGE_POOL_ID_COLUMN) @ManyToOne(fetch = FetchType.LAZY) @ForeignKey(name = "FK_" + TABLE_NAME + "_storagePool") private StoragePool storagePool; @Required(value = STORAGE_POOL_REQUIRED) public StoragePool getStoragePool() { return this.storagePool; } public void setStoragePool(final StoragePool storagePool) { this.storagePool = storagePool; getRasd().setPoolId(storagePool.getId()); } public final static String VIRTUAL_MACHINE_TEMPLATE_PROPERTY = "virtualMachineTemplate"; private final static boolean VIRTUAL_MACHINE_TEMPLATE_REQUIRED = false; private final static String VIRTUAL_MACHINE_TEMPLATE_ID_COLUMN = "idImage"; @JoinColumn(name = VIRTUAL_MACHINE_TEMPLATE_ID_COLUMN) @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @ForeignKey(name = "FK_" + TABLE_NAME + "_virtualImage") private VirtualMachineTemplate virtualMachineTemplate; @Required(value = VIRTUAL_MACHINE_TEMPLATE_REQUIRED) public VirtualMachineTemplate getVirtualMachineTemplate() { return this.virtualMachineTemplate; } public void setVirtualMachineTemplate(final VirtualMachineTemplate virtualMachineTemplate) { this.virtualMachineTemplate = virtualMachineTemplate; if (virtualMachineTemplate != null) { this.virtualMachineTemplate.setStateful(true); this.virtualMachineTemplate.setPath(getIdScsi()); } } public boolean isStateful() { return virtualMachineTemplate != null; } public final static String ID_SCSI_PROPERTY = "idScsi"; private final static boolean ID_SCSI_REQUIRED = false; public final static int ID_SCSI_LENGTH_MIN = 0; public final static int ID_SCSI_LENGTH_MAX = 255; private final static boolean ID_SCSI_LEADING_OR_TRAILING_WHITESPACES_ALLOWED = false; private final static String ID_SCSI_COLUMN = "idSCSI"; @Column(name = ID_SCSI_COLUMN, nullable = !ID_SCSI_REQUIRED, length = ID_SCSI_LENGTH_MAX) private String idScsi; @Required(value = ID_SCSI_REQUIRED) @Length(min = ID_SCSI_LENGTH_MIN, max = ID_SCSI_LENGTH_MAX) @LeadingOrTrailingWhitespace(allowed = ID_SCSI_LEADING_OR_TRAILING_WHITESPACES_ALLOWED) @IscsiPath public String getIdScsi() { return this.idScsi; } public void setIdScsi(final String idScsi) { this.idScsi = idScsi; getRasd().setConnection(idScsi); if (isStateful()) { // If the volume is stateful update the virtual machine template too this.virtualMachineTemplate.setPath(idScsi); } } public final static String STATE_PROPERTY = "state"; private final static String STATE_COLUMN = "state"; @Enumerated(value = javax.persistence.EnumType.ORDINAL) @Column(name = STATE_COLUMN, nullable = true) private VolumeState state; public VolumeState getState() { return this.state; } // Must not be used. Use the state change methods public void setState(final VolumeState state) { this.state = state; } public final static String USED_SIZE_PROPERTY = "usedSizeInMB"; private final static String USED_SIZE_COLUMN = "usedSize"; private final static long USED_SIZE_MIN = Long.MIN_VALUE; private final static long USED_SIZE_MAX = Long.MAX_VALUE; @Column(name = USED_SIZE_COLUMN, nullable = true) @Range(min = USED_SIZE_MIN, max = USED_SIZE_MAX) private long usedSizeInMB; public long getUsedSizeInMB() { return this.usedSizeInMB; } public void setUsedSizeInMB(final long usedSizeInMB) { this.usedSizeInMB = usedSizeInMB < 0 ? 0L : usedSizeInMB; } // **************************** Rasd delegating methods *************************** public String getUuid() { return getRasd().getId(); } public String getName() { return getRasd().getElementName(); } public void setName(final String name) { getRasd().setElementName(name); } public long getSizeInMB() { Long size = getRasd().getLimit(); return size == null ? 0L : size; } public void setSizeInMB(final long sizeInMB) { getRasd().setLimit(sizeInMB < 0 ? 0L : sizeInMB); } public long getAvailableSizeInMB() { Long reservation = getRasd().getReservation(); return reservation == null ? 0L : reservation; } public void setAvailableSizeInMB(final long availableSizeInMB) { getRasd().setReservation(availableSizeInMB < 0 ? 0L : availableSizeInMB); } // ********************************** Volume state transitions ******************************** @Override public void attach(final int sequence, final VirtualMachine vm) { if (state != VolumeState.DETACHED) { throw new IllegalStateException("Volume should be in " + VolumeState.DETACHED.name() + " state"); } if (vm == null) { throw new IllegalStateException("Virtual machine can not be null"); } setSequence(sequence); setVirtualMachine(vm); setState(VolumeState.ATTACHED); } @Override public void detach() { if (state != VolumeState.ATTACHED) { throw new IllegalStateException("Volume should be in " + VolumeState.ATTACHED.name() + " state"); } getRasd().setGeneration(null); setVirtualMachine(null); setVirtualAppliance(null); setState(VolumeState.DETACHED); } @Override public boolean isAttached() { return state == VolumeState.ATTACHED && getVirtualMachine() != null; } // ********************************** Others ******************************** @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); } public static enum OrderByEnum { NAME("elementname", "vol.rasd.elementName"), ID("idman", "vol.id"), VIRTUALDATACENTER( "vdcname", "vol.virtualDatacenter.name"), VIRTUALMACHINE("vmname", "vm.name"), VIRTUALAPPLIANCE( "vaname", "vapp.name"), TIER("tier", "vol.storagePool.tier.name"), TOTALSIZE("size", "vol.rasd.limit"), AVAILABLESIZE("available", "vol.rasd.reservation"), USEDSIZE("used", "vol.usedSizeInMB"), STATE("state", "vol.state"); private String columnSQL; private String columnHQL; private OrderByEnum(final String columnSQL, final String columnHQL) { this.columnSQL = columnSQL; this.columnHQL = columnHQL; } public String getColumnSQL() { return columnSQL; } public String getColumnHQL() { return columnHQL; } } }