/************************************************************************* * Copyright 2009-2016 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. ************************************************************************/ package com.eucalyptus.compute.common.internal.vmtypes; import java.util.Map; import java.util.Set; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.PersistenceContext; import javax.persistence.Table; import javax.persistence.Transient; import com.eucalyptus.auth.principal.Principals; import com.eucalyptus.compute.common.CloudMetadata.VmTypeMetadata; import com.eucalyptus.component.ComponentIds; import com.eucalyptus.component.id.Eucalyptus; import com.eucalyptus.crypto.Crypto; import com.eucalyptus.crypto.Digest; import com.eucalyptus.entities.AbstractPersistent; import com.eucalyptus.compute.common.internal.images.DeviceMapping; import com.eucalyptus.compute.common.internal.images.Images; import com.eucalyptus.auth.principal.FullName; import com.eucalyptus.util.HasFullName; import com.eucalyptus.auth.principal.OwnerFullName; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /** * Definition of the VM resource type.<br/> * Currently reflects resource allocations in the following dimensions and kinds: * <ul> * <li>CPU: only in terms of quantity of VCPUs allocated on the hypervisor * <li>Memory: the number of memories * <li>Disk: * <ol> * <li>Root: size of the root disk * <li>Ephemeral: size of the possible ephemeral partitions attached (For epheremeral disk * information see the below referenced documents.) * </ol> * </ul> * Continue to be missing: * <ul> * <li>Placement affinity * <li>Placement restrictions * <li>EBS optimized storage * <li>SSDs * <li>GPUs * <li>32bit/64bit restrictions * <li>Network capacity characteristics (may never be feasible) * </ul> * * @see <a * href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#StorageOnInstanceTypes">Storage * on Instance Types</a> * @see <a * href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#InstanceStoreDeviceNames">Instance * Store Device Names</a> * @see DeviceMapping */ @Entity @PersistenceContext( name = "eucalyptus_cloud" ) @Table( name = "cloud_vm_type" ) public class VmType extends AbstractPersistent implements VmTypeMetadata, HasFullName<VmTypeMetadata> { @Transient private static final long serialVersionUID = 1L; @Column( name = "config_vm_type_name" ) private String name; @Column( name = "config_vm_type_cpu" ) private Integer cpu; @Column( name = "config_vm_type_disk" ) private Integer disk; @Column( name = "config_vm_type_memory" ) private Integer memory; @Column( name = "config_vm_type_ebs_only" ) private Boolean ebsOnly; @Column( name = "config_vm_type_ebs_iops" ) private Integer ebsIopsLimit; @Column( name = "config_vm_type_64bit_only" ) private Boolean x86_64only; @Column( name = "config_vm_type_enis" ) private Integer networkInterfaces; @ElementCollection @CollectionTable( name = "config_vm_types_ephemeral_disks" ) private Set<EphemeralDisk> ephemeralDisks = Sets.newHashSet( ); private VmType( ) {} private VmType( final String name ) { this.name = name; this.setNaturalId( Crypto.getDigestBase64( name, Digest.SHA1 ) );//this ensures that natural ids are used when unique queries are performed. } private VmType( final String name, final Integer cpu, final Integer disk, final Integer memory, final Integer networkInterfaces ) { this( name ); this.cpu = cpu; this.disk = disk; this.memory = memory; this.networkInterfaces = networkInterfaces; } public static VmType create( ) { return new VmType( ); } @Override public String getDisplayName( ) { return this.name; } @Override public String getName( ) { return this.name; } public void setName( final String name ) { this.name = name; } @Override public Integer getCpu( ) { return this.cpu; } public void setCpu( final Integer cpu ) { this.cpu = cpu; } @Override public Integer getDisk( ) { return this.disk; } public void setDisk( final Integer disk ) { this.disk = disk; } @Override public Integer getMemory( ) { return this.memory; } public void setMemory( final Integer memory ) { this.memory = memory; } @SuppressWarnings( "RedundantIfStatement" ) @Override public boolean equals( final Object o ) { if ( this == o ) return true; if ( ( o == null ) || ( this.getClass( ) != o.getClass( ) ) ) return false; final VmType vmType = ( VmType ) o; if ( !this.cpu.equals( vmType.cpu ) ) return false; if ( !this.disk.equals( vmType.disk ) ) return false; if ( !this.memory.equals( vmType.memory ) ) return false; if ( !this.name.equals( vmType.name ) ) return false; return true; } @Override public int hashCode( ) { int result = this.name.hashCode( ); result = ( 31 * result ) + this.cpu.hashCode( ); result = ( 31 * result ) + this.disk.hashCode( ); result = ( 31 * result ) + this.memory.hashCode( ); return result; } @Override public int compareTo( final VmTypeMetadata that ) { if ( this.equals( that ) ) return 0; if ( this.getDisk( ) < that.getDisk( ) ) { return -1; } else if ( this.getDisk( ) > that.getDisk( ) ) { return 1; } if ( this.getMemory( ) < that.getMemory( ) ) { return -1; } else if ( this.getMemory( ) > that.getMemory( ) ) { return 1; } if ( this.getCpu( ) < that.getCpu( ) ) { return -1; } else if ( this.getCpu( ) > that.getCpu( ) ) { return 1; } return this.getDisplayName( ).compareTo( that.getDisplayName( ) ); } @Override public String toString( ) { return "VmType " + this.name + " cores=" + this.cpu + " disk=" + this.disk + " mem=" + this.memory; } @Override public String getPartition( ) { return ComponentIds.lookup( Eucalyptus.class ).name( ); } @Override public FullName getFullName( ) { return FullName.create.vendor( "euca" ) .region( ComponentIds.lookup( Eucalyptus.class ).name( ) ) .namespace( Principals.systemFullName( ).getAccountNumber( ) ) .relativeId( "vm-type", this.getName( ) ); } @Override public OwnerFullName getOwner( ) { return Principals.nobodyFullName( ); } public Supplier<VmType> allocator( ) { return new Supplier<VmType>( ) { @Override public VmType get( ) { return VmType.this; } }; } public enum SizeProperties implements Function<VmType, Integer> { Cpu { @Override public Integer apply( final VmType vmType ) { return vmType.getCpu( ); } }, Disk { @Override public Integer apply( final VmType vmType ) { return vmType.getDisk( ); } }, Memory { @Override public Integer apply( final VmType vmType ) { return vmType.getMemory( ); } } } public static class EphemeralBuilder { private Integer index = 0; private Map<Integer, String> deviceNames = Maps.newHashMap( ); private Set<EphemeralDisk> disks = Sets.newHashSet( ); private VmType parent; EphemeralBuilder( VmType parent ) { super( ); this.parent = parent; } private String getDiskName( String deviceName ) { if ( Images.DEFAULT_PARTITIONED_ROOT_DEVICE.equals( deviceName ) ) { throw new IllegalArgumentException( "Attempt to assign restricted device name: " + deviceName ); } else if ( deviceNames.containsValue( deviceName ) ) { throw new IllegalArgumentException( "Attempt to assign same device name to multiple devices: " + deviceName + " with " + deviceNames.entrySet( ) ); } else { int idx = index++; deviceNames.put( idx, deviceName ); return "ephemeral" + idx; } } public EphemeralBuilder addDisk( EphemeralDisk disk ) { String diskName = getDiskName( disk.getDeviceName( ) ); EphemeralDisk ephemeral = EphemeralDisk.create( this.parent, diskName, disk.getDeviceName( ), disk.getSize( ), disk.getFormat( ) ); disks.add( ephemeral ); return this; } public VmType commit( ) { this.parent.getEphemeralDisks().addAll( disks ); return this.parent; } } Boolean getEbsOnly( ) { return this.ebsOnly; } void setEbsOnly( Boolean ebsOnly ) { this.ebsOnly = ebsOnly; } Integer getEbsIopsLimit( ) { return this.ebsIopsLimit; } void setEbsIopsLimit( Integer ebsIopsLimit ) { this.ebsIopsLimit = ebsIopsLimit; } Boolean getX86_64only( ) { return this.x86_64only; } void setX86_64only( Boolean x86_64only ) { this.x86_64only = x86_64only; } public Integer getNetworkInterfaces( ) { return networkInterfaces; } public void setNetworkInterfaces( final Integer networkInterfaces ) { this.networkInterfaces = networkInterfaces; } public Set<EphemeralDisk> getEphemeralDisks() { return this.ephemeralDisks; } public void addEphemeralDisks( EphemeralDisk... disks ) { EphemeralBuilder builder = this.withEphemeralDisks( ); for ( EphemeralDisk d : disks ) { builder.addDisk( d ); } } public VmType.EphemeralBuilder withEphemeralDisks( ) { this.getEphemeralDisks().clear(); return new VmType.EphemeralBuilder( this ); } public static VmType create( String name, Integer cpu, Integer disk, Integer memory, Integer networkInterfaces ) { return new VmType( name, cpu, disk, memory, networkInterfaces ); } public static VmType named( String name ) { return new VmType( name ); } public Predicate<VmType> orderedPredicate( ) { return new Predicate<VmType>( ) { @Override public boolean apply( VmType vm ) { boolean gtcpu = vm.cpu > VmType.this.cpu, eqcpu = vm.cpu == VmType.this.cpu, ltcpu = vm.cpu < VmType.this.cpu; boolean gtdisk = vm.disk > VmType.this.disk, eqdisk = vm.disk == VmType.this.disk, ltdisk = vm.disk < VmType.this.disk; boolean gtmemory = vm.memory > VmType.this.memory, eqmem = vm.memory == VmType.this.memory, ltmem = vm.memory < VmType.this.memory; boolean singleOrder = ( gtcpu && gtdisk && ltmem ) || ( gtcpu && gtmemory && ltdisk ) || ( gtdisk && gtmemory && ltcpu ); boolean doubleOrder = ( gtmemory && ltcpu && ltdisk ) || ( gtdisk && ltcpu && ltmem ) || ( gtcpu && ltdisk && ltmem ); return !this.equals( vm ) && ( singleOrder || doubleOrder ); } }; } }