160160import com .cloud .hypervisor .Hypervisor .HypervisorType ;
161161import com .cloud .hypervisor .HypervisorCapabilitiesVO ;
162162import com .cloud .hypervisor .dao .HypervisorCapabilitiesDao ;
163+ import com .cloud .network .Network .Provider ;
164+ import com .cloud .network .Network .Service ;
165+ import com .cloud .network .NetworkModel ;
163166import com .cloud .network .element .ConfigDriveNetworkElement ;
164167import com .cloud .offering .DiskOffering ;
165168import com .cloud .org .Cluster ;
220223import com .cloud .utils .fsm .NoTransitionException ;
221224import com .cloud .utils .fsm .StateMachine2 ;
222225import com .cloud .vm .DiskProfile ;
226+ import com .cloud .vm .NicVO ;
223227import com .cloud .vm .VMInstanceDetailVO ;
224228import com .cloud .vm .UserVmManager ;
225229import com .cloud .vm .UserVmService ;
241245import com .cloud .vm .VmWorkResizeVolume ;
242246import com .cloud .vm .VmWorkSerializer ;
243247import com .cloud .vm .VmWorkTakeVolumeSnapshot ;
248+ import com .cloud .vm .dao .NicDao ;
244249import com .cloud .vm .dao .UserVmDao ;
245250import com .cloud .vm .dao .VMInstanceDetailsDao ;
246251import com .cloud .vm .dao .VMInstanceDao ;
@@ -294,6 +299,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
294299 @ Inject
295300 private VMInstanceDetailsDao vmInstanceDetailsDao ;
296301 @ Inject
302+ private NicDao _nicDao ;
303+ @ Inject
304+ private NetworkModel _networkModel ;
305+ @ Inject
297306 private UserVmService _userVmService ;
298307 @ Inject
299308 private VolumeDataStoreDao _volumeStoreDao ;
@@ -5038,16 +5047,69 @@ protected String getMinimumHypervisorVersionInDatacenter(long datacenterId, Hype
50385047 return StringUtils .isBlank (minHwVersion ) ? defaultHypervisorVersion : minHwVersion ;
50395048 }
50405049
5050+ /**
5051+ * The number of CD-ROM devices of the Instance. Each CD-ROM device occupies a device id, starting from
5052+ * {@link TemplateManager#CDROM_PRIMARY_DEVICE_SEQ}.
5053+ */
5054+ protected int getVmCdromCount (VirtualMachine vm ) {
5055+ return 1 ;
5056+ }
5057+
5058+ /**
5059+ * Checks if a config drive ISO is (or will be) attached to the Instance, which is the case when any of its networks
5060+ * has ConfigDrive as the provider of the Dhcp, Dns or UserData service. Checking the network providers (instead of
5061+ * only the config drive location detail, which is added when the ISO is created) also covers Instances which have
5062+ * never been started.
5063+ */
5064+ protected boolean isConfigDriveUsed (VirtualMachine vm ) {
5065+ if (vmInstanceDetailsDao .findDetail (vm .getId (), VmDetailConstants .CONFIG_DRIVE_LOCATION ) != null ) {
5066+ return true ;
5067+ }
5068+ for (NicVO nic : _nicDao .listByVmId (vm .getId ())) {
5069+ for (Service service : Arrays .asList (Service .UserData , Service .Dhcp , Service .Dns )) {
5070+ if (_networkModel .isProviderSupportServiceInNetwork (nic .getNetworkId (), service , Provider .ConfigDrive )) {
5071+ return true ;
5072+ }
5073+ }
5074+ }
5075+ return false ;
5076+ }
5077+
5078+ @ Override
5079+ public Set <Long > getReservedDeviceIds (VirtualMachine vm ) {
5080+ Set <Long > reservedDeviceIds = new HashSet <>();
5081+ for (int i = 0 ; i < getVmCdromCount (vm ); i ++) {
5082+ reservedDeviceIds .add ((long ) TemplateManager .CDROM_PRIMARY_DEVICE_SEQ + i );
5083+ }
5084+ if (isConfigDriveUsed (vm )) {
5085+ reservedDeviceIds .add (ConfigDriveNetworkElement .CONFIGDRIVEDISKSEQ .longValue ());
5086+ }
5087+ return reservedDeviceIds ;
5088+ }
5089+
5090+ /**
5091+ * Returns the first {@code maxDataVolumes} device ids (starting from 1) which are not reserved.
5092+ */
5093+ private List <Long > getAvailableDeviceIds (int maxDataVolumes , Set <Long > reservedDeviceIds ) {
5094+ List <Long > availableDeviceIds = new ArrayList <>();
5095+ for (long id = 1 ; availableDeviceIds .size () < maxDataVolumes ; id ++) {
5096+ if (!reservedDeviceIds .contains (id )) {
5097+ availableDeviceIds .add (id );
5098+ }
5099+ }
5100+ return availableDeviceIds ;
5101+ }
5102+
50415103 private Long getDeviceId (UserVmVO vm , Long deviceId ) {
50425104 // allocate deviceId
5043- int maxDevices = getMaxDataVolumesSupported (vm ) + 2 ; // add 2 to consider devices root volume and cdrom
5044- int maxDeviceId = maxDevices - 1 ;
5105+ Set <Long > reservedDeviceIds = getReservedDeviceIds (vm );
5106+ List <Long > availableDeviceIds = getAvailableDeviceIds (getMaxDataVolumesSupported (vm ), reservedDeviceIds );
5107+ long maxDeviceId = availableDeviceIds .get (availableDeviceIds .size () - 1 );
50455108 List <VolumeVO > vols = _volsDao .findByInstance (vm .getId ());
5046- boolean vmHasConfigDrive = vmInstanceDetailsDao .findDetail (vm .getId (), VmDetailConstants .CONFIG_DRIVE_LOCATION ) != null ;
50475109 if (deviceId != null ) {
5048- if (deviceId .longValue () < 0 || deviceId .longValue () > maxDeviceId || deviceId . longValue () == 3
5049- || ( vmHasConfigDrive && deviceId . longValue () == ConfigDriveNetworkElement . CONFIGDRIVEDISKSEQ )) {
5050- throw new RuntimeException ( "deviceId should be 0,1,2,4-" + maxDeviceId );
5110+ if (deviceId .longValue () < 0 || deviceId .longValue () > maxDeviceId || reservedDeviceIds . contains ( deviceId )) {
5111+ throw new RuntimeException ( String . format ( "deviceId should be 0 or between 1 and %d, except the ones reserved for CD-ROM/config drive devices: %s" ,
5112+ maxDeviceId , reservedDeviceIds ) );
50515113 }
50525114 for (VolumeVO vol : vols ) {
50535115 if (vol .getDeviceId ().equals (deviceId )) {
@@ -5056,21 +5118,13 @@ private Long getDeviceId(UserVmVO vm, Long deviceId) {
50565118 }
50575119 } else {
50585120 // allocate deviceId here
5059- List <String > devIds = new ArrayList <String >();
5060- for (int i = 1 ; i <= maxDeviceId ; i ++) {
5061- devIds .add (String .valueOf (i ));
5062- }
5063- devIds .remove ("3" );
5064- if (vmHasConfigDrive ) {
5065- devIds .remove (ConfigDriveNetworkElement .CONFIGDRIVEDISKSEQ .toString ());
5066- }
50675121 for (VolumeVO vol : vols ) {
5068- devIds .remove (vol .getDeviceId (). toString (). trim ());
5122+ availableDeviceIds .remove (vol .getDeviceId ());
50695123 }
5070- if (devIds .isEmpty ()) {
5124+ if (availableDeviceIds .isEmpty ()) {
50715125 throw new RuntimeException (String .format ("All device Ids are used by vm %s" , vm ));
50725126 }
5073- deviceId = Long . parseLong ( devIds . iterator (). next () );
5127+ deviceId = availableDeviceIds . get ( 0 );
50745128 }
50755129
50765130 return deviceId ;
0 commit comments