Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix limitation on tag matching in 'migrateVolume' with disk offering replacement#2636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
8bbeb464f59b934c9a94548dddbfFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -19,6 +19,7 @@ | ||
| import java.net.MalformedURLException; | ||
| import java.net.URL; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.Date; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| @@ -2161,9 +2162,9 @@ private DiskOfferingVO retrieveAndValidateNewDiskOffering(MigrateVolumeCmd cmd) | ||
| * Performs the validations required for replacing the disk offering while migrating the volume of storage. If no new disk offering is provided, we do not execute any validation. | ||
| * If a disk offering is informed, we then proceed with the following checks. | ||
| * <ul> | ||
| * <li>We check if the given volume is of ROOT type. We cannot change the disk offering of a ROOT volume. Therefore, we thrown an {@link InvalidParameterValueException}. | ||
| * <li>We the disk is being migrated to shared storage and the new disk offering is for local storage (or vice versa), we throw an {@link InvalidParameterValueException}. Bear in mind that we are validating only the new disk offering. If none is provided we can override the current disk offering. This means, placing a volume with shared disk offering in local storage and vice versa. | ||
| * <li>We then proceed checking if the tags of the new disk offerings match the tags of the target storage. If they do not match an {@link InvalidParameterValueException} is thrown. | ||
| * <li>We check if the given volume is of ROOT type. We cannot change the disk offering of a ROOT volume. Therefore, we thrown an {@link InvalidParameterValueException}; | ||
| * <li>We the disk is being migrated to shared storage and the new disk offering is for local storage (or vice versa), we throw an {@link InvalidParameterValueException}. Bear in mind that we are validating only the new disk offering. If none is provided we can override the current disk offering. This means, placing a volume with shared disk offering in local storage and vice versa; | ||
| * <li>We then proceed checking the target storage pool supports the new disk offering {@link #doesTargetStorageSupportNewDiskOffering(StoragePool, DiskOfferingVO)}. | ||
| * </ul> | ||
| * | ||
| * If all of the above validations pass, we check if the size of the new disk offering is different from the volume. If it is, we log a warning message. | ||
| @@ -2175,10 +2176,9 @@ protected void validateConditionsToReplaceDiskOfferingOfVolume(VolumeVO volume, | ||
| if ((destPool.isShared() && newDiskOffering.getUseLocalStorage()) || destPool.isLocal() && newDiskOffering.isShared()) { | ||
| throw new InvalidParameterValueException("You cannot move the volume to a shared storage and assing a disk offering for local storage and vice versa."); | ||
| } | ||
| String storageTags = getStoragePoolTags(destPool); | ||
| if (!StringUtils.areTagsEqual(storageTags, newDiskOffering.getTags())) { | ||
| throw new InvalidParameterValueException(String.format("Target Storage [id=%s] tags [%s] does not match new disk offering [id=%s] tags [%s].", destPool.getUuid(), storageTags, | ||
| newDiskOffering.getUuid(), newDiskOffering.getTags())); | ||
| if (!doesTargetStorageSupportNewDiskOffering(destPool, newDiskOffering)) { | ||
| throw new InvalidParameterValueException(String.format("Target Storage [id=%s] tags [%s] does not match new disk offering [id=%s] tags [%s].", destPool.getUuid(), | ||
| getStoragePoolTags(destPool), newDiskOffering.getUuid(), newDiskOffering.getTags())); | ||
| } | ||
| if (volume.getSize() != newDiskOffering.getDiskSize()) { | ||
| DiskOfferingVO oldDiskOffering = this._diskOfferingDao.findById(volume.getDiskOfferingId()); | ||
| @@ -2189,6 +2189,58 @@ protected void validateConditionsToReplaceDiskOfferingOfVolume(VolumeVO volume, | ||
| s_logger.info(String.format("Changing disk offering to [uuid=%s] while migrating volume [uuid=%s, name=%s].", newDiskOffering.getUuid(), volume.getUuid(), volume.getName())); | ||
| } | ||
| /** | ||
| * Checks if the target storage supports the new disk offering. | ||
| * This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a new data disk is allocated. | ||
| * | ||
| * The scenarios when this method returns true or false is presented in the following table. | ||
| * | ||
| * <table border="1"> | ||
| * <tr> | ||
| * <th>#</th><th>Disk offering tags</th><th>Storage tags</th><th>Does the storage support the disk offering?</th> | ||
| * </tr> | ||
| * <body> | ||
| * <tr> | ||
| * <td>1</td><td>A,B</td><td>A</td><td>NO</td> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>2</td><td>A,B,C</td><td>A,B,C,D,X</td><td>YES</td> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>3</td><td>A,B,C</td><td>X,Y,Z</td><td>NO</td> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>4</td><td>null</td><td>A,S,D</td><td>YES</td> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>5</td><td>A</td><td>null</td><td>NO</td> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>6</td><td>null</td><td>null</td><td>YES</td> | ||
| * </tr> | ||
| * </body> | ||
| * </table> | ||
| */ | ||
| protected boolean doesTargetStorageSupportNewDiskOffering(StoragePool destPool, DiskOfferingVO newDiskOffering) { | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a protected method. When we want to let a hypervisor do the entire migration it would be convenient to call this method from elsewhere. Does it make sense to put it on the interface? I am thinking about the VirtualMachineManager or the HypervisorGurus. MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I am not sure if we have some other places that can use this method. I would normally only set a method to public and declare it in an interface if I need really need it. I can declare it if you think it is important though. P.S.: I find our interface usage sometimes quite silly with single implementations. A good example is using interface for POJOs. It does not provide benefits, the only thing it does is adding dozens more lines for every object.
| ||
| String newDiskOfferingTags = newDiskOffering.getTags(); | ||
| return doesTargetStorageSupportDiskOffering(destPool, newDiskOfferingTags); | ||
| } | ||
| @Override | ||
| public boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags) { | ||
| if (org.apache.commons.lang.StringUtils.isBlank(diskOfferingTags)) { | ||
| return true; | ||
| } | ||
| String storagePoolTags = getStoragePoolTags(destPool); | ||
| if (org.apache.commons.lang.StringUtils.isBlank(storagePoolTags)) { | ||
| return false; | ||
| } | ||
| String[] storageTagsAsStringArray = org.apache.commons.lang.StringUtils.split(storagePoolTags, ","); | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there is a StringUtils.csvTagsToList() MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you talking about Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we agreed to move all string operation in it so it we have a small interface plain to external libraries. MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we?! That means we would create a layer over a library. I think we had a discussion, but I was under the impression that the idea was not to redo the wheel. Of course, my memory is not as good as it was before, so I am probably mistaken.
| ||
| String[] newDiskOfferingTagsAsStringArray = org.apache.commons.lang.StringUtils.split(diskOfferingTags, ","); | ||
| return CollectionUtils.isSubCollection(Arrays.asList(newDiskOfferingTagsAsStringArray), Arrays.asList(storageTagsAsStringArray)); | ||
| } | ||
| /** | ||
| * Retrieves the storage pool tags as a {@link String}. If the storage pool does not have tags we return a null value. | ||
| */ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i see the parameter appearing in the method name. why not isSupported(pool, offering);?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good point. I normally try to be very descriptive with method names. When I see names such as
isSupported, even though we see the parameters there, we have no clues what the method is checking. It could be that the method is checking if the storage has enough IOPs, or the method has enough space, or any other business logic. Maybe, a better name here would bedoesTargetStorageSupportNewDiskOfferingTags.Of course, here I have documented the method in detail. However, this should not be an excuse not to try to work out a good and descriptive method name.