mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Merge branch 'master' into api_limit
Conflicts: api/src/org/apache/cloudstack/api/BaseCmd.java server/src/com/cloud/api/ApiServer.java server/src/com/cloud/api/ApiServlet.java Signed-off-by: Min Chen <min.chen@citrix.com>
This commit is contained in:
		
						commit
						c1a540c6bb
					
				
							
								
								
									
										40
									
								
								api/src/com/cloud/agent/api/storage/ResizeVolumeAnswer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								api/src/com/cloud/agent/api/storage/ResizeVolumeAnswer.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | // 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 com.cloud.agent.api.storage; | ||||||
|  | 
 | ||||||
|  | import com.cloud.agent.api.Answer; | ||||||
|  | 
 | ||||||
|  | public class ResizeVolumeAnswer extends Answer { | ||||||
|  |     private long newSize; | ||||||
|  | 
 | ||||||
|  |     protected ResizeVolumeAnswer() { | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public ResizeVolumeAnswer(ResizeVolumeCommand cmd, boolean result, String details, long newSize) { | ||||||
|  |         super(cmd, result, details); | ||||||
|  |         this.newSize = newSize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public ResizeVolumeAnswer(ResizeVolumeCommand cmd, boolean result, String details) { | ||||||
|  |         super(cmd, result, details); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public long getNewSize() { | ||||||
|  |         return newSize; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								api/src/com/cloud/agent/api/storage/ResizeVolumeCommand.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								api/src/com/cloud/agent/api/storage/ResizeVolumeCommand.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | |||||||
|  | // 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 com.cloud.agent.api.storage; | ||||||
|  | 
 | ||||||
|  | import com.cloud.agent.api.Command; | ||||||
|  | import com.cloud.agent.api.to.StorageFilerTO; | ||||||
|  | import com.cloud.storage.StoragePool; | ||||||
|  | 
 | ||||||
|  | public class ResizeVolumeCommand extends Command { | ||||||
|  |     private String path; | ||||||
|  |     private StorageFilerTO pool; | ||||||
|  |     private String vmInstance; | ||||||
|  |     private Long newSize; | ||||||
|  |     private Long currentSize; | ||||||
|  |     private boolean shrinkOk; | ||||||
|  |      | ||||||
|  |     protected ResizeVolumeCommand() { | ||||||
|  |          | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public ResizeVolumeCommand(String path, | ||||||
|  |                            StorageFilerTO pool, | ||||||
|  |                            Long currentSize, | ||||||
|  |                            Long newSize, | ||||||
|  |                            boolean shrinkOk, | ||||||
|  |                            String vmInstance)  | ||||||
|  |     { | ||||||
|  |         this.path = path; | ||||||
|  |         this.pool = pool; | ||||||
|  |         this.vmInstance = vmInstance; | ||||||
|  |         this.currentSize = currentSize; | ||||||
|  |         this.newSize = newSize; | ||||||
|  |         this.shrinkOk = shrinkOk; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getPath() { | ||||||
|  |     	return path; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getPoolUuid() { | ||||||
|  |         return pool.getUuid(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public StorageFilerTO getPool() { | ||||||
|  |         return pool; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public long getNewSize() { | ||||||
|  |         return newSize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public long getCurrentSize() { | ||||||
|  |         return currentSize; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean getShrinkOk() { | ||||||
|  |         return shrinkOk; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getInstanceName() { | ||||||
|  |         return vmInstance; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * {@inheritDoc} | ||||||
|  |      */ | ||||||
|  |     @Override | ||||||
|  |     public boolean executeInSequence() { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -108,6 +108,7 @@ public class EventTypes { | |||||||
|     public static final String EVENT_VOLUME_EXTRACT = "VOLUME.EXTRACT"; |     public static final String EVENT_VOLUME_EXTRACT = "VOLUME.EXTRACT"; | ||||||
|     public static final String EVENT_VOLUME_UPLOAD = "VOLUME.UPLOAD"; |     public static final String EVENT_VOLUME_UPLOAD = "VOLUME.UPLOAD"; | ||||||
|     public static final String EVENT_VOLUME_MIGRATE = "VOLUME.MIGRATE"; |     public static final String EVENT_VOLUME_MIGRATE = "VOLUME.MIGRATE"; | ||||||
|  |     public static final String EVENT_VOLUME_RESIZE = "VOLUME.RESIZE"; | ||||||
| 
 | 
 | ||||||
|     // Domains |     // Domains | ||||||
|     public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE"; |     public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE"; | ||||||
|  | |||||||
| @ -54,7 +54,6 @@ public class CloudException extends Exception { | |||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 	public ArrayList<String> getIdProxyList() { | 	public ArrayList<String> getIdProxyList() { | ||||||
| 		return idList; | 		return idList; | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaint | |||||||
| import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; | import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; | ||||||
| import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; | import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; | ||||||
| import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; | import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; | ||||||
|  | import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; | ||||||
| import com.cloud.exception.ConcurrentOperationException; | import com.cloud.exception.ConcurrentOperationException; | ||||||
| import com.cloud.exception.InsufficientCapacityException; | import com.cloud.exception.InsufficientCapacityException; | ||||||
| import com.cloud.exception.PermissionDeniedException; | import com.cloud.exception.PermissionDeniedException; | ||||||
| @ -70,6 +71,15 @@ public interface StorageService{ | |||||||
|     Volume createVolume(CreateVolumeCmd cmd); |     Volume createVolume(CreateVolumeCmd cmd); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Resizes the volume based on the given criteria | ||||||
|  |      *  | ||||||
|  |      * @param cmd | ||||||
|  |      *            the API command wrapping the criteria | ||||||
|  |      * @return the volume object | ||||||
|  |      */ | ||||||
|  |     Volume resizeVolume(ResizeVolumeCmd cmd); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Delete the storage pool |      * Delete the storage pool | ||||||
|      * |      * | ||||||
|  | |||||||
| @ -36,6 +36,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba | |||||||
|         Ready("The volume is ready to be used."), |         Ready("The volume is ready to be used."), | ||||||
|         Migrating("The volume is migrating to other storage pool"), |         Migrating("The volume is migrating to other storage pool"), | ||||||
|         Snapshotting("There is a snapshot created on this volume, not backed up to secondary storage yet"), |         Snapshotting("There is a snapshot created on this volume, not backed up to secondary storage yet"), | ||||||
|  |         Resizing("The volume is being resized"), | ||||||
|         Expunging("The volume is being expunging"), |         Expunging("The volume is being expunging"), | ||||||
|         Destroy("The volume is destroyed, and can't be recovered."), |         Destroy("The volume is destroyed, and can't be recovered."), | ||||||
|         UploadOp ("The volume upload operation is in progress or in short the volume is on secondary storage"); |         UploadOp ("The volume upload operation is in progress or in short the volume is on secondary storage"); | ||||||
| @ -63,6 +64,9 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba | |||||||
|             s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready); |             s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready); | ||||||
|             s_fsm.addTransition(Creating, Event.DestroyRequested, Destroy); |             s_fsm.addTransition(Creating, Event.DestroyRequested, Destroy); | ||||||
|             s_fsm.addTransition(Creating, Event.CreateRequested, Creating);   |             s_fsm.addTransition(Creating, Event.CreateRequested, Creating);   | ||||||
|  |             s_fsm.addTransition(Ready, Event.ResizeRequested, Resizing); | ||||||
|  |             s_fsm.addTransition(Resizing, Event.OperationSucceeded, Ready); | ||||||
|  |             s_fsm.addTransition(Resizing, Event.OperationFailed, Ready);           | ||||||
|             s_fsm.addTransition(Allocated, Event.UploadRequested, UploadOp); |             s_fsm.addTransition(Allocated, Event.UploadRequested, UploadOp); | ||||||
|             s_fsm.addTransition(UploadOp, Event.CopyRequested, Creating);// CopyRequested for volume from sec to primary storage |             s_fsm.addTransition(UploadOp, Event.CopyRequested, Creating);// CopyRequested for volume from sec to primary storage | ||||||
|             s_fsm.addTransition(Creating, Event.CopySucceeded, Ready); |             s_fsm.addTransition(Creating, Event.CopySucceeded, Ready); | ||||||
| @ -92,7 +96,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba | |||||||
|         MigrationRequested, |         MigrationRequested, | ||||||
|         SnapshotRequested, |         SnapshotRequested, | ||||||
|         DestroyRequested, |         DestroyRequested, | ||||||
|         ExpungingRequested; |         ExpungingRequested, | ||||||
|  |         ResizeRequested; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -387,6 +387,7 @@ public class ApiConstants { | |||||||
|     public static final String ESP_LIFETIME = "esplifetime"; |     public static final String ESP_LIFETIME = "esplifetime"; | ||||||
|     public static final String DPD = "dpd"; |     public static final String DPD = "dpd"; | ||||||
|     public static final String FOR_VPC = "forvpc"; |     public static final String FOR_VPC = "forvpc"; | ||||||
|  |     public static final String SHRINK_OK = "shrinkok"; | ||||||
|     public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; |     public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; | ||||||
|     public static final String NICIRA_NVP_TRANSPORT_ZONE_UUID = "transportzoneuuid"; |     public static final String NICIRA_NVP_TRANSPORT_ZONE_UUID = "transportzoneuuid"; | ||||||
|     public static final String NICIRA_NVP_DEVICE_NAME = "niciradevicename"; |     public static final String NICIRA_NVP_DEVICE_NAME = "niciradevicename"; | ||||||
|  | |||||||
| @ -81,7 +81,6 @@ public abstract class BaseCmd { | |||||||
|         BOOLEAN, DATE, FLOAT, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, TZDATE, UUID |         BOOLEAN, DATE, FLOAT, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, TZDATE, UUID | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     public static final DateFormat INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); |     public static final DateFormat INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); | ||||||
|     public static final DateFormat NEW_INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |     public static final DateFormat NEW_INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | ||||||
|     public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+"); |     public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+"); | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								api/src/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								api/src/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
								
								
									
										0
									
								
								api/src/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoPermissionsCmd.java
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								api/src/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoPermissionsCmd.java
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -0,0 +1,154 @@ | |||||||
|  | // 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.api.command.user.volume; | ||||||
|  | 
 | ||||||
|  | import org.apache.cloudstack.api.response.*; | ||||||
|  | import org.apache.log4j.Logger; | ||||||
|  | 
 | ||||||
|  | import org.apache.cloudstack.api.ApiConstants; | ||||||
|  | import org.apache.cloudstack.api.ApiErrorCode; | ||||||
|  | import org.apache.cloudstack.api.BaseAsyncCmd; | ||||||
|  | import org.apache.cloudstack.api.BaseCmd; | ||||||
|  | import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; | ||||||
|  | import org.apache.cloudstack.api.APICommand; | ||||||
|  | import org.apache.cloudstack.api.Parameter; | ||||||
|  | import org.apache.cloudstack.api.ServerApiException; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.regex.Pattern; | ||||||
|  | import java.util.regex.Matcher; | ||||||
|  | 
 | ||||||
|  | import com.cloud.exception.InvalidParameterValueException; | ||||||
|  | import com.cloud.exception.PermissionDeniedException; | ||||||
|  | import com.cloud.exception.ResourceAllocationException; | ||||||
|  | import com.cloud.async.AsyncJob; | ||||||
|  | import com.cloud.event.EventTypes; | ||||||
|  | import com.cloud.projects.Project; | ||||||
|  | import com.cloud.storage.Volume; | ||||||
|  | import com.cloud.user.Account; | ||||||
|  | import com.cloud.user.UserContext; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @APICommand(name="resizeVolume", description="Resizes a volume", responseObject=VolumeResponse.class) | ||||||
|  | public class ResizeVolumeCmd extends BaseAsyncCmd { | ||||||
|  |     public static final Logger s_logger = Logger.getLogger(ResizeVolumeCmd.class.getName()); | ||||||
|  | 
 | ||||||
|  |     private static final String s_name = "resizevolumeresponse"; | ||||||
|  | 
 | ||||||
|  |     ///////////////////////////////////////////////////// | ||||||
|  |     //////////////// API parameters ///////////////////// | ||||||
|  |     ///////////////////////////////////////////////////// | ||||||
|  | 
 | ||||||
|  |     @Parameter(name=ApiConstants.ID, entityType=VolumeResponse.class, type=CommandType.UUID, description="the ID of the disk volume") | ||||||
|  |     private Long id; | ||||||
|  | 
 | ||||||
|  |     @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, required=false, description="New volume size in G") | ||||||
|  |     private Long size; | ||||||
|  | 
 | ||||||
|  |     @Parameter(name=ApiConstants.SHRINK_OK, type=CommandType.BOOLEAN, required=false, description="Verify OK to Shrink") | ||||||
|  |     private boolean shrinkOk; | ||||||
|  | 
 | ||||||
|  |     @Parameter(name=ApiConstants.DISK_OFFERING_ID, entityType=DiskOfferingResponse.class, type=CommandType.UUID, required=false, description="new disk offering id") | ||||||
|  |     private Long newDiskOfferingId; | ||||||
|  | 
 | ||||||
|  |     ///////////////////////////////////////////////////// | ||||||
|  |     /////////////////// Accessors /////////////////////// | ||||||
|  |     ///////////////////////////////////////////////////// | ||||||
|  | 
 | ||||||
|  |     public Long getEntityId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Long getSize() { | ||||||
|  |         return size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean getShrinkOk() { | ||||||
|  |         return shrinkOk; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Long getNewDiskOfferingId() { | ||||||
|  |         return newDiskOfferingId; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     ///////////////////////////////////////////////////// | ||||||
|  |     /////////////// API Implementation/////////////////// | ||||||
|  |     ///////////////////////////////////////////////////// | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getCommandName() { | ||||||
|  |         return s_name; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public AsyncJob.Type getInstanceType() { | ||||||
|  |     	return AsyncJob.Type.Volume; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static String getResultObjectName() { | ||||||
|  |         return "volume"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |    @Override | ||||||
|  |     public long getEntityOwnerId() { | ||||||
|  | 
 | ||||||
|  |         Volume volume = _entityMgr.findById(Volume.class, getEntityId()); | ||||||
|  |         if (volume == null) { | ||||||
|  |                 throw new InvalidParameterValueException("Unable to find volume by id=" + id); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Account account = _accountService.getAccount(volume.getAccountId()); | ||||||
|  |         //Can resize volumes for enabled projects/accounts only | ||||||
|  |         if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { | ||||||
|  |                 Project project = _projectService.findByProjectAccountId(volume.getAccountId()); | ||||||
|  |             if (project.getState() != Project.State.Active) { | ||||||
|  |                 throw new PermissionDeniedException("Can't add resources to  project id=" + project.getId() + " in state=" + project.getState() + " as it's no longer active"); | ||||||
|  |             } | ||||||
|  |         } else if (account.getState() == Account.State.disabled) { | ||||||
|  |             throw new PermissionDeniedException("The owner of volume " + id + "  is disabled: " + account); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return volume.getAccountId(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getEventType() { | ||||||
|  |         return EventTypes.EVENT_VOLUME_RESIZE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getEventDescription() { | ||||||
|  |         return "Volume Id: " + getEntityId() + " to size " + getSize() + "G" ; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void execute(){ | ||||||
|  |         UserContext.current().setEventDetails("Volume Id: " + getEntityId() + " to size " + getSize() + "G"); | ||||||
|  |     	Volume volume = _storageService.resizeVolume(this); | ||||||
|  |     	if (volume != null) { | ||||||
|  |             VolumeResponse response = _responseGenerator.createVolumeResponse(volume); | ||||||
|  |             //FIXME - have to be moved to ApiResponseHelper | ||||||
|  |             response.setResponseName(getCommandName()); | ||||||
|  |             this.setResponseObject(response); | ||||||
|  |         } else { | ||||||
|  |             throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to resize volume"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,199 @@ | |||||||
|  | // 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 src.com.cloud.agent.api.test; | ||||||
|  | 
 | ||||||
|  | import static org.junit.Assert.assertEquals; | ||||||
|  | import static org.junit.Assert.assertTrue; | ||||||
|  | import static org.junit.Assert.assertFalse; | ||||||
|  | 
 | ||||||
|  | import java.text.ParseException; | ||||||
|  | import java.text.SimpleDateFormat; | ||||||
|  | import java.util.Date; | ||||||
|  | 
 | ||||||
|  | import org.junit.Test; | ||||||
|  | 
 | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeCommand; | ||||||
|  | import com.cloud.agent.api.to.StorageFilerTO; | ||||||
|  | import com.cloud.storage.StoragePool; | ||||||
|  | import com.cloud.storage.Storage.StoragePoolType; | ||||||
|  | import com.cloud.storage.StoragePoolStatus; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | public class ResizeVolumeCommandTest { | ||||||
|  | 
 | ||||||
|  |     public StoragePool dummypool = new StoragePool() { | ||||||
|  |         public long getId() { | ||||||
|  |             return 1L; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public String getName() { | ||||||
|  |             return "name"; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public String getUuid() { | ||||||
|  |             return "bed9f83e-cac3-11e1-ac8a-0050568b007e"; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public StoragePoolType getPoolType() { | ||||||
|  |             return StoragePoolType.Filesystem; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public Date getCreated() { | ||||||
|  |             Date date = null; | ||||||
|  |             try { | ||||||
|  |                 date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss") | ||||||
|  |                         .parse("01/01/1970 12:12:12"); | ||||||
|  |             } catch (ParseException e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } | ||||||
|  |             return date; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Date getUpdateTime() { | ||||||
|  |             return new Date(); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public long getDataCenterId() { | ||||||
|  |             return 0L; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public long getCapacityBytes() { | ||||||
|  |             return 0L; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public long getAvailableBytes() { | ||||||
|  |             return 0L; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public Long getClusterId() { | ||||||
|  |             return 0L; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public String getHostAddress() { | ||||||
|  |             return "hostAddress"; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public String getPath() { | ||||||
|  |             return "path"; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public String getUserInfo() { | ||||||
|  |             return "userInfo"; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public boolean isShared() { | ||||||
|  |             return false; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public boolean isLocal() { | ||||||
|  |             return false; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public StoragePoolStatus getStatus() { | ||||||
|  |             return StoragePoolStatus.Up; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public int getPort() { | ||||||
|  |             return 25; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         public Long getPodId() { | ||||||
|  |             return 0L; | ||||||
|  |         }; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Long newSize = 4194304L; | ||||||
|  |     Long currentSize = 1048576L; | ||||||
|  | 
 | ||||||
|  |     ResizeVolumeCommand rv = new ResizeVolumeCommand("dummydiskpath",  | ||||||
|  |             new StorageFilerTO(dummypool), currentSize, newSize, false,  | ||||||
|  |             "vmName"); | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testExecuteInSequence() { | ||||||
|  |         boolean b = rv.executeInSequence(); | ||||||
|  |         assertFalse(b); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetPath() { | ||||||
|  |         String path = rv.getPath(); | ||||||
|  |         assertTrue(path.equals("dummydiskpath")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetPoolUuid() { | ||||||
|  |         String poolUuid = rv.getPoolUuid(); | ||||||
|  |         assertTrue(poolUuid.equals("bed9f83e-cac3-11e1-ac8a-0050568b007e")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetPool() { | ||||||
|  |         StorageFilerTO pool = rv.getPool(); | ||||||
|  | 
 | ||||||
|  |         Long id = pool.getId(); | ||||||
|  |         Long expectedL = 1L; | ||||||
|  |         assertEquals(expectedL, id); | ||||||
|  | 
 | ||||||
|  |         String uuid = pool.getUuid(); | ||||||
|  |         assertTrue(uuid.equals("bed9f83e-cac3-11e1-ac8a-0050568b007e")); | ||||||
|  | 
 | ||||||
|  |         String host = pool.getHost(); | ||||||
|  |         assertTrue(host.equals("hostAddress")); | ||||||
|  | 
 | ||||||
|  |         String path = pool.getPath(); | ||||||
|  |         assertTrue(path.equals("path")); | ||||||
|  | 
 | ||||||
|  |         String userInfo = pool.getUserInfo(); | ||||||
|  |         assertTrue(userInfo.equals("userInfo")); | ||||||
|  | 
 | ||||||
|  |         Integer port = pool.getPort(); | ||||||
|  |         Integer expectedI = 25; | ||||||
|  |         assertEquals(expectedI, port); | ||||||
|  | 
 | ||||||
|  |         StoragePoolType type = pool.getType(); | ||||||
|  |         assertEquals(StoragePoolType.Filesystem, type); | ||||||
|  | 
 | ||||||
|  |         String str = pool.toString(); | ||||||
|  |         assertTrue(str.equals("Pool[" + id.toString() + "|" + host + ":" | ||||||
|  |                 + port.toString() + "|" + path + "]")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetNewSize() { | ||||||
|  |         long newSize = rv.getNewSize(); | ||||||
|  |         assertTrue(newSize == 4194304L); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetCurrentSize() { | ||||||
|  |         long currentSize = rv.getCurrentSize(); | ||||||
|  |         assertTrue(currentSize == 1048576L); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetShrinkOk() { | ||||||
|  |         assertFalse(rv.getShrinkOk()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void testGetInstanceName() { | ||||||
|  |         String vmName = rv.getInstanceName(); | ||||||
|  |         assertTrue(vmName.equals("vmName")); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -255,6 +255,7 @@ deleteVolume=15 | |||||||
| listVolumes=15 | listVolumes=15 | ||||||
| extractVolume=15 | extractVolume=15 | ||||||
| migrateVolume=15 | migrateVolume=15 | ||||||
|  | resizeVolume=15 | ||||||
| 
 | 
 | ||||||
| #### registration command:  FIXME -- this really should be something in management server that | #### registration command:  FIXME -- this really should be something in management server that | ||||||
| ####                                 generates a new key for the user and they just have to | ####                                 generates a new key for the user and they just have to | ||||||
|  | |||||||
| @ -162,6 +162,8 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; | |||||||
| import com.cloud.agent.api.storage.DestroyCommand; | import com.cloud.agent.api.storage.DestroyCommand; | ||||||
| import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; | import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; | ||||||
| import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; | import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeCommand; | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeAnswer; | ||||||
| import com.cloud.agent.api.to.IpAddressTO; | import com.cloud.agent.api.to.IpAddressTO; | ||||||
| import com.cloud.agent.api.to.NicTO; | import com.cloud.agent.api.to.NicTO; | ||||||
| import com.cloud.agent.api.to.StorageFilerTO; | import com.cloud.agent.api.to.StorageFilerTO; | ||||||
| @ -255,6 +257,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements | |||||||
|     private String _patchdomrPath; |     private String _patchdomrPath; | ||||||
|     private String _createvmPath; |     private String _createvmPath; | ||||||
|     private String _manageSnapshotPath; |     private String _manageSnapshotPath; | ||||||
|  |     private String _resizeVolumePath; | ||||||
|     private String _createTmplPath; |     private String _createTmplPath; | ||||||
|     private String _heartBeatPath; |     private String _heartBeatPath; | ||||||
|     private String _securityGroupPath; |     private String _securityGroupPath; | ||||||
| @ -534,6 +537,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements | |||||||
|                     "Unable to find the managesnapshot.sh"); |                     "Unable to find the managesnapshot.sh"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         _resizeVolumePath = Script.findScript(storageScriptsDir, "resizevolume.sh"); | ||||||
|  |         if (_resizeVolumePath == null) { | ||||||
|  |             throw new ConfigurationException( | ||||||
|  |                     "Unable to find the resizevolume.sh"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         _createTmplPath = Script |         _createTmplPath = Script | ||||||
|                 .findScript(storageScriptsDir, "createtmplt.sh"); |                 .findScript(storageScriptsDir, "createtmplt.sh"); | ||||||
|         if (_createTmplPath == null) { |         if (_createTmplPath == null) { | ||||||
| @ -1062,6 +1071,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements | |||||||
|                 return execute((CleanupNetworkRulesCmd) cmd); |                 return execute((CleanupNetworkRulesCmd) cmd); | ||||||
|             } else if (cmd instanceof CopyVolumeCommand) { |             } else if (cmd instanceof CopyVolumeCommand) { | ||||||
|                 return execute((CopyVolumeCommand) cmd); |                 return execute((CopyVolumeCommand) cmd); | ||||||
|  |             } else if (cmd instanceof ResizeVolumeCommand) { | ||||||
|  |                 return execute((ResizeVolumeCommand) cmd); | ||||||
|             } else if (cmd instanceof CheckNetworkCommand) { |             } else if (cmd instanceof CheckNetworkCommand) { | ||||||
|                 return execute((CheckNetworkCommand) cmd); |                 return execute((CheckNetworkCommand) cmd); | ||||||
|             } else { |             } else { | ||||||
| @ -1268,6 +1279,72 @@ public class LibvirtComputingResource extends ServerResourceBase implements | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private String getResizeScriptType (KVMStoragePool pool, KVMPhysicalDisk vol) { | ||||||
|  |         StoragePoolType poolType = pool.getType(); | ||||||
|  |         PhysicalDiskFormat volFormat = vol.getFormat(); | ||||||
|  |           | ||||||
|  |         if(pool.getType() == StoragePoolType.CLVM && volFormat == KVMPhysicalDisk.PhysicalDiskFormat.RAW) { | ||||||
|  |             return "CLVM"; | ||||||
|  |         } else if ((poolType == StoragePoolType.NetworkFilesystem | ||||||
|  |                   || poolType == StoragePoolType.SharedMountPoint | ||||||
|  |                   || poolType == StoragePoolType.Filesystem) | ||||||
|  |                   && volFormat == KVMPhysicalDisk.PhysicalDiskFormat.QCOW2 ) { | ||||||
|  |             return "QCOW2"; | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* uses a local script now, eventually support for virStorageVolResize() will maybe work on  | ||||||
|  |        qcow2 and lvm and we can do this in libvirt calls */ | ||||||
|  |     public Answer execute(ResizeVolumeCommand cmd) { | ||||||
|  |         String volid = cmd.getPath(); | ||||||
|  |         long newSize = cmd.getNewSize(); | ||||||
|  |         long currentSize = cmd.getCurrentSize(); | ||||||
|  |         String vmInstanceName = cmd.getInstanceName(); | ||||||
|  |         boolean shrinkOk = cmd.getShrinkOk(); | ||||||
|  |         StorageFilerTO spool = cmd.getPool(); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             KVMStoragePool pool = _storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid()); | ||||||
|  |             KVMPhysicalDisk vol = pool.getPhysicalDisk(volid); | ||||||
|  |             String path = vol.getPath(); | ||||||
|  |             String type = getResizeScriptType(pool, vol); | ||||||
|  | 
 | ||||||
|  |             if (type == null) { | ||||||
|  |                 return new ResizeVolumeAnswer(cmd, false, "Unsupported volume format: pool type '"  | ||||||
|  |                                 + pool.getType() + "' and volume format '" + vol.getFormat() + "'"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             s_logger.debug("got to the stage where we execute the volume resize, params:"  | ||||||
|  |                            + path + "," + currentSize + "," + newSize + "," + type + "," + vmInstanceName + "," + shrinkOk); | ||||||
|  |             final Script resizecmd = new Script(_resizeVolumePath, | ||||||
|  |                         _cmdsTimeout, s_logger);  | ||||||
|  |             resizecmd.add("-s",String.valueOf(newSize)); | ||||||
|  |             resizecmd.add("-c",String.valueOf(currentSize)); | ||||||
|  |             resizecmd.add("-p",path); | ||||||
|  |             resizecmd.add("-t",type); | ||||||
|  |             resizecmd.add("-r",String.valueOf(shrinkOk)); | ||||||
|  |             resizecmd.add("-v",vmInstanceName); | ||||||
|  |             String result = resizecmd.execute(); | ||||||
|  | 
 | ||||||
|  |             if (result == null) { | ||||||
|  | 
 | ||||||
|  |                 /* fetch new size as seen from libvirt, don't want to assume anything */ | ||||||
|  |                 pool = _storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid()); | ||||||
|  |                 long finalSize = pool.getPhysicalDisk(volid).getVirtualSize(); | ||||||
|  |                 s_logger.debug("after resize, size reports as " + finalSize + ", requested " + newSize); | ||||||
|  |                 return new ResizeVolumeAnswer(cmd, true, "success", finalSize); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return new ResizeVolumeAnswer(cmd, false, result); | ||||||
|  |         } catch (CloudRuntimeException e) { | ||||||
|  |             String error = "failed to resize volume: " + e; | ||||||
|  |             s_logger.debug(error); | ||||||
|  |             return new ResizeVolumeAnswer(cmd, false, error); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |     }  | ||||||
|  | 
 | ||||||
|     public Answer execute(DestroyCommand cmd) { |     public Answer execute(DestroyCommand cmd) { | ||||||
|         VolumeTO vol = cmd.getVolume(); |         VolumeTO vol = cmd.getVolume(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -182,6 +182,8 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; | |||||||
| import com.cloud.agent.api.storage.DestroyCommand; | import com.cloud.agent.api.storage.DestroyCommand; | ||||||
| import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; | import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; | ||||||
| import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; | import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeCommand; | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeAnswer; | ||||||
| import com.cloud.agent.api.to.IpAddressTO; | import com.cloud.agent.api.to.IpAddressTO; | ||||||
| import com.cloud.agent.api.to.NicTO; | import com.cloud.agent.api.to.NicTO; | ||||||
| import com.cloud.agent.api.to.PortForwardingRuleTO; | import com.cloud.agent.api.to.PortForwardingRuleTO; | ||||||
| @ -468,6 +470,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe | |||||||
|             return execute((DeleteStoragePoolCommand) cmd); |             return execute((DeleteStoragePoolCommand) cmd); | ||||||
|         } else if (clazz == CopyVolumeCommand.class) { |         } else if (clazz == CopyVolumeCommand.class) { | ||||||
|             return execute((CopyVolumeCommand) cmd); |             return execute((CopyVolumeCommand) cmd); | ||||||
|  |         } else if (clazz == ResizeVolumeCommand.class) { | ||||||
|  |             return execute((ResizeVolumeCommand) cmd); | ||||||
|         } else if (clazz == AttachVolumeCommand.class) { |         } else if (clazz == AttachVolumeCommand.class) { | ||||||
|             return execute((AttachVolumeCommand) cmd); |             return execute((AttachVolumeCommand) cmd); | ||||||
|         } else if (clazz == AttachIsoCommand.class) { |         } else if (clazz == AttachIsoCommand.class) { | ||||||
| @ -5618,6 +5622,23 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public Answer execute(ResizeVolumeCommand cmd) { | ||||||
|  |         Connection conn = getConnection(); | ||||||
|  |         StorageFilerTO pool = cmd.getPool(); | ||||||
|  |         String volid = cmd.getPath(); | ||||||
|  |         long newSize = cmd.getNewSize(); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             VDI vdi = getVDIbyUuid(conn, volid); | ||||||
|  |             vdi.resize(conn, newSize); | ||||||
|  |             return new ResizeVolumeAnswer(cmd, true, "success", newSize); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             s_logger.warn("Unable to resize volume",e); | ||||||
|  |             String error = "failed to resize volume:"  +e; | ||||||
|  |             return new ResizeVolumeAnswer(cmd, false, error ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     protected SR getISOSRbyVmName(Connection conn, String vmName) { |     protected SR getISOSRbyVmName(Connection conn, String vmName) { | ||||||
|         try { |         try { | ||||||
|             Set<SR> srs = SR.getByNameLabel(conn, vmName + "-ISO"); |             Set<SR> srs = SR.getByNameLabel(conn, vmName + "-ISO"); | ||||||
| @ -7684,4 +7705,5 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe | |||||||
|             return new SetStaticRouteAnswer(cmd, false, null); |             return new SetStaticRouteAnswer(cmd, false, null); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										253
									
								
								scripts/storage/qcow2/resizevolume.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								scripts/storage/qcow2/resizevolume.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,253 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  | # 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. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # resizevolume.sh -- resize a volume | ||||||
|  | 
 | ||||||
|  | usage() { | ||||||
|  |   printf "Usage: %s: -c <current-volume-size> -s <new-volume-size> -p <volume path> -v <vm instance name> -t <storage-type> -r <shrink-bool>\n" $(basename $0) >&2 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | getdevmappername() { | ||||||
|  |   local path=$1 | ||||||
|  |   local devmappername=`readlink -f $path |cut -d/ -f3` | ||||||
|  |   if [[ $devmappername =~ "dm-" ]] | ||||||
|  |   then | ||||||
|  |     dmname=$devmappername | ||||||
|  |     return 0 | ||||||
|  |   else | ||||||
|  |     return 1; | ||||||
|  |   fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | getdevmappersize() { | ||||||
|  |   local dm=$1 | ||||||
|  |   if [ ! -e "/sys/block/${dm}/size" ] | ||||||
|  |   then | ||||||
|  |     log "unable to find ${dm} in /sys/block" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  |   actualsize=$((`cat /sys/block/${dm}/size`*512)); | ||||||
|  | 
 | ||||||
|  |   if [[ -z "$actualsize" ]] | ||||||
|  |   then | ||||||
|  |     log "unable to find actual size of ${dm}" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  |   return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | # log "message" 1  <-- prints to stdout as well as log, to pass error up to cloudstack | ||||||
|  | # log "message" prints only to log file | ||||||
|  | # variable shouldwelog controls whether we print to log file | ||||||
|  | log() { | ||||||
|  |   local d=`date` | ||||||
|  |   local msg=${1} | ||||||
|  |   local stdout=${2} | ||||||
|  | 
 | ||||||
|  |   if [ ! -z "$stdout" ] | ||||||
|  |   then | ||||||
|  |     echo $1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if [ $shouldwelog -eq 1 ] | ||||||
|  |   then | ||||||
|  |     echo "$d - $1" >> /var/log/cloud/agent/resizevolume.log | ||||||
|  |   fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | failshrink() { | ||||||
|  |   # if this is a shrink operation, fail if commands will shrink the volume and we haven't signed of on shrinking | ||||||
|  |   if [ $actualsize -gt $newsize ] | ||||||
|  |   then | ||||||
|  |     if [ "$shrink" == "false" ] | ||||||
|  |     then | ||||||
|  |       log "result would shrink the volume from $actualsize to $newsize, but confirmation to shrink wasn't passed. Shrink='$shrink'" 1 | ||||||
|  |       exit 1 | ||||||
|  |     fi | ||||||
|  |   fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | notifyqemu() { | ||||||
|  |   #move this back into cloudstack libvirt calls once the libvirt java bindings support block resize | ||||||
|  |   #we try to inform hypervisor of new size, but don't fail if we can't | ||||||
|  |   if `virsh help 2>/dev/null | grep -q blockresize` | ||||||
|  |   then | ||||||
|  |     if `virsh domstate $vmname >/dev/null 2>&1` | ||||||
|  |     then | ||||||
|  |       sizeinkb=$(($newsize/1024)) | ||||||
|  |       virsh blockresize --domain $vmname --path $path --size $sizeinkb >/dev/null 2>&1 | ||||||
|  |       retval=$? | ||||||
|  |       if [ -z $retval ] || [ $retval -ne 0 ] | ||||||
|  |       then | ||||||
|  |         log "failed to live resize $path to size of $sizeinkb kb" 1 | ||||||
|  |       else | ||||||
|  |         liveresize='true' | ||||||
|  |       fi | ||||||
|  |     fi | ||||||
|  |   fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | resizelvm() { | ||||||
|  |   local dmname='' | ||||||
|  |   local actualsize='' | ||||||
|  |   local liveresize='false' | ||||||
|  | 
 | ||||||
|  |   ##### sanity checks ##### | ||||||
|  |   if ! `lvresize --version > /dev/null 2>&1` | ||||||
|  |   then | ||||||
|  |     log "unable to resolve executable 'lvresize'" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if ! `virsh --version > /dev/null 2>&1` | ||||||
|  |   then | ||||||
|  |     log "unable to resolve executable 'virsh'" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  |   ##### end sanity ##### | ||||||
|  | 
 | ||||||
|  |   if ! getdevmappername $path | ||||||
|  |   then | ||||||
|  |     log "unable to resolve a device mapper dev from $path" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   getdevmappersize $dmname | ||||||
|  | 
 | ||||||
|  |   if [ $actualsize -ne $currentsize ] | ||||||
|  |   then | ||||||
|  |     log "disk isn't the size we think it is: cloudstack said $currentsize, disk said $actualsize." | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   # if this is a shrink operation, fail if commands will shrink the volume and we haven't signed of on shrinking | ||||||
|  |   failshrink | ||||||
|  | 
 | ||||||
|  |   output=`lvresize -f -L ${newsize}B $path 2>&1` | ||||||
|  |   retval=$? | ||||||
|  | 
 | ||||||
|  |   if [ -z $retval ] || [ $retval -ne 0 ] | ||||||
|  |   then | ||||||
|  |     log "lvresize failed: $output " 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   #move this back into cloudstack libvirt calls once the libvirt java bindings support block resize | ||||||
|  |   #we try to inform hypervisor of new size, but don't fail if we can't | ||||||
|  |   notifyqemu | ||||||
|  | 
 | ||||||
|  |   log "performed successful resize - dm:$dmname currentsize:$currentsize newsize:$newsize path:$path type:$ptype vmname:$vmname live:$liveresize shrink:$shrink" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | resizeqcow2() { | ||||||
|  | 
 | ||||||
|  |    ##### sanity checks ##### | ||||||
|  |   if [ ! -e "$path" ] | ||||||
|  |   then | ||||||
|  |     log "unable to find file $path" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if ! `qemu-img info /dev/null > /dev/null 2>&1` | ||||||
|  |   then | ||||||
|  |     log "unable to resolve executable 'qemu-img'" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if ! `virsh --version > /dev/null 2>&1` | ||||||
|  |   then | ||||||
|  |     log "unable to resolve executable 'virsh'" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  |   ##### end sanity ##### | ||||||
|  | 
 | ||||||
|  |   $actualsize=`qemu-img info $path | grep "virtual size" | sed -re  's/^.*\(([0-9]+).*$/\1/g'` | ||||||
|  | 
 | ||||||
|  |   if [ $actualsize -ne $currentsize ] | ||||||
|  |   then | ||||||
|  |     log "disk isn't the size we think it is: cloudstack said $currentsize, disk said $actualsize." | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   # if this is a shrink operation, fail if commands will shrink the volume and we haven't signed of on shrinking | ||||||
|  |   failshrink | ||||||
|  | 
 | ||||||
|  |   output=`qemu-img resize $path $newsize 2>&1` | ||||||
|  |   retval=$? | ||||||
|  | 
 | ||||||
|  |   if [ -z $retval ] || [ $retval -ne 0 ] | ||||||
|  |   then | ||||||
|  |     log "qemu-img resize failed: $output" 1 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   #move this back into cloudstack libvirt calls once the libvirt java bindings support block resize | ||||||
|  |   #we try to inform hypervisor of new size, but don't fail if we can't | ||||||
|  |   notifyqemu | ||||||
|  | 
 | ||||||
|  |   log "performed successful resize - currentsize:$currentsize newsize:$newsize path:$path type:$ptype vmname:$vmname live:$liveresize shrink:$shrink" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sflag= | ||||||
|  | cflag= | ||||||
|  | pflag= | ||||||
|  | vflag= | ||||||
|  | tflag= | ||||||
|  | rflag= | ||||||
|  | 
 | ||||||
|  | while getopts 'c:s:v:p:t:r:' OPTION | ||||||
|  | do | ||||||
|  |   case $OPTION in | ||||||
|  |   s)	sflag=1 | ||||||
|  | 		newsize="$OPTARG" | ||||||
|  | 		;; | ||||||
|  |   c)    cflag=1 | ||||||
|  |                 currentsize="$OPTARG" | ||||||
|  |                 ;; | ||||||
|  |   v)	vflag=1 | ||||||
|  | 		vmname="$OPTARG" | ||||||
|  | 		;; | ||||||
|  |   p)	dflag=1 | ||||||
|  | 		path="$OPTARG" | ||||||
|  | 		;; | ||||||
|  |   t)    tflag=1 | ||||||
|  |                 ptype="$OPTARG" | ||||||
|  |                 ;; | ||||||
|  |   r)    rflag=1 | ||||||
|  |                 shrink="$OPTARG" | ||||||
|  |                 ;; | ||||||
|  |   ?)	usage | ||||||
|  | 		exit 2 | ||||||
|  | 		;; | ||||||
|  |   esac | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | shouldwelog=1 #set this to 1 while debugging to get output in /var/log/cloud/agent/resizevolume.log | ||||||
|  | 
 | ||||||
|  | if [ "$ptype" == "CLVM" ] | ||||||
|  | then | ||||||
|  |   resizelvm | ||||||
|  | elif [ "$ptype" == "QCOW2" ] | ||||||
|  | then | ||||||
|  |   resizeqcow2 | ||||||
|  | else | ||||||
|  |   echo "unsupported type $ptype" | ||||||
|  |   exit 1; | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | exit 0 | ||||||
| @ -138,7 +138,7 @@ import com.cloud.utils.component.Inject; | |||||||
| import com.cloud.utils.concurrency.NamedThreadFactory; | import com.cloud.utils.concurrency.NamedThreadFactory; | ||||||
| import com.cloud.utils.db.SearchCriteria; | import com.cloud.utils.db.SearchCriteria; | ||||||
| import com.cloud.utils.db.Transaction; | import com.cloud.utils.db.Transaction; | ||||||
| import com.cloud.utils.exception.CSExceptionErrorCode; | 
 | ||||||
| 
 | 
 | ||||||
| public class ApiServer implements HttpRequestHandler { | public class ApiServer implements HttpRequestHandler { | ||||||
|     private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); |     private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); | ||||||
| @ -386,6 +386,7 @@ public class ApiServer implements HttpRequestHandler { | |||||||
|             if (UserContext.current().getCaller().getType() != Account.ACCOUNT_TYPE_ADMIN){ |             if (UserContext.current().getCaller().getType() != Account.ACCOUNT_TYPE_ADMIN){ | ||||||
|                 // hide internal details to non-admin user for security reason |                 // hide internal details to non-admin user for security reason | ||||||
|                 errorMsg = BaseCmd.USER_ERROR_MESSAGE; |                 errorMsg = BaseCmd.USER_ERROR_MESSAGE; | ||||||
|  | 
 | ||||||
|             } |             } | ||||||
|             throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg, ex); |             throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg, ex); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -341,8 +341,8 @@ public class ConfigurationServerImpl implements ConfigurationServer { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // now insert the user |         // now insert the user | ||||||
|         insertSql = "INSERT INTO `cloud`.`user` (id, uuid, username, account_id, firstname, lastname, created, state) " + |         insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, created, state) " + | ||||||
|                 "VALUES (" + id + ", UUID(), '" + username + "', 2, '" + firstname + "','" + lastname + "',now(), 'disabled')"; |                 "VALUES (" + id + ",'" + username + "', RAND(), 2, '" + firstname + "','" + lastname + "',now(), 'disabled')"; | ||||||
| 
 | 
 | ||||||
|         txn = Transaction.currentTxn(); |         txn = Transaction.currentTxn(); | ||||||
|         try { |         try { | ||||||
|  | |||||||
| @ -47,6 +47,7 @@ import javax.naming.ConfigurationException; | |||||||
| import org.apache.cloudstack.api.command.admin.storage.*; | import org.apache.cloudstack.api.command.admin.storage.*; | ||||||
| import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; | import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; | ||||||
| import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; | import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; | ||||||
|  | import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; | ||||||
| import org.apache.log4j.Logger; | import org.apache.log4j.Logger; | ||||||
| 
 | 
 | ||||||
| import com.cloud.agent.AgentManager; | import com.cloud.agent.AgentManager; | ||||||
| @ -69,6 +70,8 @@ import com.cloud.agent.api.storage.CreateCommand; | |||||||
| import com.cloud.agent.api.storage.DeleteTemplateCommand; | import com.cloud.agent.api.storage.DeleteTemplateCommand; | ||||||
| import com.cloud.agent.api.storage.DeleteVolumeCommand; | import com.cloud.agent.api.storage.DeleteVolumeCommand; | ||||||
| import com.cloud.agent.api.storage.DestroyCommand; | import com.cloud.agent.api.storage.DestroyCommand; | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeCommand; | ||||||
|  | import com.cloud.agent.api.storage.ResizeVolumeAnswer; | ||||||
| import com.cloud.agent.api.to.StorageFilerTO; | import com.cloud.agent.api.to.StorageFilerTO; | ||||||
| import com.cloud.agent.api.to.VolumeTO; | import com.cloud.agent.api.to.VolumeTO; | ||||||
| import com.cloud.agent.manager.Commands; | import com.cloud.agent.manager.Commands; | ||||||
| @ -2098,6 +2101,183 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     @DB | ||||||
|  |     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true) | ||||||
|  |     public VolumeVO resizeVolume(ResizeVolumeCmd cmd) { | ||||||
|  |         VolumeVO volume = _volsDao.findById(cmd.getEntityId()); | ||||||
|  |         Long newSize = null; | ||||||
|  |         boolean shrinkOk = cmd.getShrinkOk(); | ||||||
|  |         boolean success = false; | ||||||
|  |         DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); | ||||||
|  |         DiskOfferingVO newDiskOffering = null; | ||||||
|  | 
 | ||||||
|  |         newDiskOffering = _diskOfferingDao.findById(cmd.getNewDiskOfferingId()); | ||||||
|  | 
 | ||||||
|  |         /* Volumes with no hypervisor have never been assigned, and can be resized by recreating. | ||||||
|  |            perhaps in the future we can just update the db entry for the volume */ | ||||||
|  |         if(_volsDao.getHypervisorType(volume.getId()) == HypervisorType.None){ | ||||||
|  |             throw new InvalidParameterValueException("Can't resize a volume that has never been attached, not sure which hypervisor type. Recreate volume to resize."); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* Only works for KVM/Xen for now */ | ||||||
|  |         if(_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM  | ||||||
|  |            && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer){ | ||||||
|  |             throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM or XenServer hypervisor for resize"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (volume == null) { | ||||||
|  |             throw new InvalidParameterValueException("No such volume"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (volume.getState() != Volume.State.Ready) { | ||||||
|  |             throw new InvalidParameterValueException("Volume should be in ready state before attempting a resize"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!volume.getVolumeType().equals(Volume.Type.DATADISK)) { | ||||||
|  |             throw new InvalidParameterValueException("Can only resize DATA volumes"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* figure out whether or not a new disk offering or size parameter is required, get the correct size value */ | ||||||
|  |         if (newDiskOffering == null) { | ||||||
|  |             if (diskOffering.isCustomized()) { | ||||||
|  |                 newSize = cmd.getSize(); | ||||||
|  | 
 | ||||||
|  |                 if (newSize == null) { | ||||||
|  |                     throw new InvalidParameterValueException("new offering is of custom size, need to specify a size"); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 newSize = ( newSize << 30 ); | ||||||
|  |             } else { | ||||||
|  |                 throw new InvalidParameterValueException("current offering" + volume.getDiskOfferingId()  + " cannot be resized, need to specify a disk offering"); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  | 
 | ||||||
|  |             if (newDiskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(newDiskOffering.getType())) { | ||||||
|  |                 throw new InvalidParameterValueException("Disk offering ID is missing or invalid"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(diskOffering.getTags() != null) { | ||||||
|  |                 if(!newDiskOffering.getTags().equals(diskOffering.getTags())){ | ||||||
|  |                     throw new InvalidParameterValueException("Tags on new and old disk offerings must match"); | ||||||
|  |                 } | ||||||
|  |             } else if (newDiskOffering.getTags() != null ){ | ||||||
|  |                 throw new InvalidParameterValueException("There are no tags on current disk offering, new disk offering needs to have no tags"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (newDiskOffering.getDomainId() == null) { | ||||||
|  |                 // do nothing as offering is public | ||||||
|  |             } else { | ||||||
|  |                 _configMgr.checkDiskOfferingAccess(UserContext.current().getCaller(), newDiskOffering); | ||||||
|  |             } | ||||||
|  |   | ||||||
|  |             if (newDiskOffering.isCustomized()) { | ||||||
|  |                 newSize = cmd.getSize(); | ||||||
|  | 
 | ||||||
|  |                 if (newSize == null) { | ||||||
|  |                     throw new InvalidParameterValueException("new offering is of custom size, need to specify a size"); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 newSize = ( newSize << 30 ); | ||||||
|  |             } else { | ||||||
|  |                 newSize = newDiskOffering.getDiskSize(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (newSize == null) { | ||||||
|  |             throw new InvalidParameterValueException("could not detect a size parameter or fetch one from the diskofferingid parameter"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!validateVolumeSizeRange(newSize)) { | ||||||
|  |             throw new InvalidParameterValueException("Requested size out of range"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* does the caller have the authority to act on this volume? */ | ||||||
|  |         _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, volume); | ||||||
|  | 
 | ||||||
|  |         UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); | ||||||
|  | 
 | ||||||
|  |         StoragePool pool = _storagePoolDao.findById(volume.getPoolId()); | ||||||
|  |         long currentSize = volume.getSize(); | ||||||
|  | 
 | ||||||
|  |         /* lets make certain they (think they) know what they're doing if they  | ||||||
|  |         want to shrink, by forcing them to provide the shrinkok parameter. This will | ||||||
|  |         be checked again at the hypervisor level where we can see the actual disk size */ | ||||||
|  |         if (currentSize > newSize && !shrinkOk) { | ||||||
|  |             throw new InvalidParameterValueException("Going from existing size of " + currentSize + " to size of "  | ||||||
|  |                       + newSize + " would shrink the volume, need to sign off by supplying the shrinkok parameter with value of true"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* get a list of hosts to send the commands to, try the system the  | ||||||
|  |         associated vm is running on first, then the last known place it ran. | ||||||
|  |         If not attached to a userVm, we pass 'none' and resizevolume.sh is | ||||||
|  |         ok with that since it only needs the vm name to live resize */ | ||||||
|  |         long[] hosts = null; | ||||||
|  |         String instanceName = "none"; | ||||||
|  |         if (userVm != null) { | ||||||
|  |             instanceName = userVm.getInstanceName(); | ||||||
|  |             if(userVm.getHostId() != null) { | ||||||
|  |                 hosts = new long[] { userVm.getHostId() }; | ||||||
|  |             } else if(userVm.getLastHostId() != null) { | ||||||
|  |                 hosts = new long[] { userVm.getLastHostId() }; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             /*Xen only works offline, SR does not support VDI.resizeOnline*/ | ||||||
|  |             if(_volsDao.getHypervisorType(volume.getId()) == HypervisorType.XenServer | ||||||
|  |                && ! userVm.getState().equals(State.Stopped)) { | ||||||
|  |                 throw new InvalidParameterValueException("VM must be stopped or disk detached in order to resize with the Xen HV"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             try { | ||||||
|  |                     stateTransitTo(volume, Volume.Event.ResizeRequested); | ||||||
|  |             } catch (NoTransitionException etrans) { | ||||||
|  |                     throw new CloudRuntimeException("Unable to change volume state for resize: " + etrans.toString()); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(volume.getPath(), new StorageFilerTO(pool),  | ||||||
|  |                                                     currentSize, newSize, shrinkOk, instanceName); | ||||||
|  |             ResizeVolumeAnswer answer = (ResizeVolumeAnswer) sendToPool(pool, hosts, resizeCmd); | ||||||
|  | 
 | ||||||
|  |             /* need to fetch/store new volume size in database. This value comes from  | ||||||
|  |             hypervisor rather than trusting that a success means we have a volume of the  | ||||||
|  |             size we requested */ | ||||||
|  |             if (answer != null && answer.getResult()) { | ||||||
|  |                 long finalSize = answer.getNewSize(); | ||||||
|  |                 s_logger.debug("Resize: volume started at size " + currentSize + " and ended at size " + finalSize); | ||||||
|  |                 volume.setSize(finalSize); | ||||||
|  |                 if (newDiskOffering != null) { | ||||||
|  |                     volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); | ||||||
|  |                 } | ||||||
|  |                 _volsDao.update(volume.getId(), volume); | ||||||
|  | 
 | ||||||
|  |                 success  = true; | ||||||
|  |                 return volume; | ||||||
|  |             } else if (answer != null) { | ||||||
|  |                 s_logger.debug("Resize: returned '" + answer.getDetails() + "'"); | ||||||
|  |             } | ||||||
|  |         } catch (StorageUnavailableException e) { | ||||||
|  |             s_logger.debug("volume failed to resize: "+e); | ||||||
|  |             return null; | ||||||
|  |         } finally { | ||||||
|  |             if(success) { | ||||||
|  |                 try { | ||||||
|  |                     stateTransitTo(volume, Volume.Event.OperationSucceeded); | ||||||
|  |                 } catch (NoTransitionException etrans) { | ||||||
|  |                     throw new CloudRuntimeException("Failed to change volume state: " + etrans.toString()); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 try { | ||||||
|  |                     stateTransitTo(volume, Volume.Event.OperationFailed); | ||||||
|  |                 } catch (NoTransitionException etrans) { | ||||||
|  |                     throw new CloudRuntimeException("Failed to change volume state: " + etrans.toString()); | ||||||
|  |                 } | ||||||
|  |             }  | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     @DB |     @DB | ||||||
|     public boolean destroyVolume(VolumeVO volume) throws ConcurrentOperationException { |     public boolean destroyVolume(VolumeVO volume) throws ConcurrentOperationException { | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								setup/db/create-schema-view.sql
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								setup/db/create-schema-view.sql
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -54,12 +54,20 @@ class Services: | |||||||
|                                     "cpunumber": 1, |                                     "cpunumber": 1, | ||||||
|                                     "cpuspeed": 100,    # in MHz |                                     "cpuspeed": 100,    # in MHz | ||||||
|                                     "memory": 128,       # In MBs |                                     "memory": 128,       # In MBs | ||||||
|  |                                     "storagetype": "local" | ||||||
|                         }, |                         }, | ||||||
|                         "disk_offering": { |                         "disk_offering": { | ||||||
|                                     "displaytext": "Small", |                                     "displaytext": "Small", | ||||||
|                                     "name": "Small", |                                     "name": "Small", | ||||||
|  |                                     "storagetype": "local", | ||||||
|                                     "disksize": 1 |                                     "disksize": 1 | ||||||
|                         }, |                         }, | ||||||
|  |                         'resized_disk_offering': { | ||||||
|  |                                     "displaytext": "Resized", | ||||||
|  |                                     "name": "Resized", | ||||||
|  |                                     "storagetype": "local", | ||||||
|  |                                     "disksize": 3 | ||||||
|  |                         }, | ||||||
|                         "volume_offerings": { |                         "volume_offerings": { | ||||||
|                             0: { |                             0: { | ||||||
|                                 "diskname": "TestDiskServ", |                                 "diskname": "TestDiskServ", | ||||||
| @ -77,8 +85,8 @@ class Services: | |||||||
|                         "diskdevice": "/dev/xvdb", |                         "diskdevice": "/dev/xvdb", | ||||||
|                         "ostype": 'CentOS 5.3 (64-bit)', |                         "ostype": 'CentOS 5.3 (64-bit)', | ||||||
|                         "mode": 'basic', |                         "mode": 'basic', | ||||||
|                         "sleep": 60, |                         "sleep": 10, | ||||||
|                         "timeout": 10, |                         "timeout": 600, | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -237,7 +245,7 @@ class TestCreateVolume(cloudstackTestCase): | |||||||
|                 ssh = self.virtual_machine.get_ssh_client( |                 ssh = self.virtual_machine.get_ssh_client( | ||||||
|                                                       reconnect=True |                                                       reconnect=True | ||||||
|                                                       ) |                                                       ) | ||||||
|                 c = "fdisk -l" |                 c = "/sbin/fdisk -l" | ||||||
|                 res = ssh.execute(c) |                 res = ssh.execute(c) | ||||||
| 
 | 
 | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
| @ -283,6 +291,16 @@ class TestVolumes(cloudstackTestCase): | |||||||
|                                     cls.api_client, |                                     cls.api_client, | ||||||
|                                     cls.services["disk_offering"] |                                     cls.services["disk_offering"] | ||||||
|                                     ) |                                     ) | ||||||
|  |         cls.resized_disk_offering = DiskOffering.create( | ||||||
|  |                                     cls.api_client, | ||||||
|  |                                     cls.services["resized_disk_offering"] | ||||||
|  |                                     ) | ||||||
|  |         cls.custom_resized_disk_offering = DiskOffering.create( | ||||||
|  |                                     cls.api_client, | ||||||
|  |                                     cls.services["resized_disk_offering"], | ||||||
|  |                                     custom=True | ||||||
|  |                                     ) | ||||||
|  | 
 | ||||||
|         template = get_template( |         template = get_template( | ||||||
|                             cls.api_client, |                             cls.api_client, | ||||||
|                             cls.zone.id, |                             cls.zone.id, | ||||||
| @ -292,6 +310,8 @@ class TestVolumes(cloudstackTestCase): | |||||||
|         cls.services["zoneid"] = cls.zone.id |         cls.services["zoneid"] = cls.zone.id | ||||||
|         cls.services["template"] = template.id |         cls.services["template"] = template.id | ||||||
|         cls.services["diskofferingid"] = cls.disk_offering.id |         cls.services["diskofferingid"] = cls.disk_offering.id | ||||||
|  |         cls.services['resizeddiskofferingid'] = cls.resized_disk_offering.id | ||||||
|  |         cls.services['customresizeddiskofferingid'] = cls.custom_resized_disk_offering.id | ||||||
| 
 | 
 | ||||||
|         # Create VMs, VMs etc |         # Create VMs, VMs etc | ||||||
|         cls.account = Account.create( |         cls.account = Account.create( | ||||||
| @ -321,6 +341,8 @@ class TestVolumes(cloudstackTestCase): | |||||||
|                                    domainid=cls.account.account.domainid |                                    domainid=cls.account.account.domainid | ||||||
|                                    ) |                                    ) | ||||||
|         cls._cleanup = [ |         cls._cleanup = [ | ||||||
|  |                         cls.resized_disk_offering, | ||||||
|  |                         cls.custom_resized_disk_offering, | ||||||
|                         cls.service_offering, |                         cls.service_offering, | ||||||
|                         cls.disk_offering, |                         cls.disk_offering, | ||||||
|                         cls.account |                         cls.account | ||||||
| @ -500,7 +522,102 @@ class TestVolumes(cloudstackTestCase): | |||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|     @attr(tags = ["advanced", "advancedns", "smoke"]) |     @attr(tags = ["advanced", "advancedns", "smoke"]) | ||||||
|     def test_07_delete_detached_volume(self): |     def test_07_resize_fail(self): | ||||||
|  |         """Verify invalid options fail to Resize a volume""" | ||||||
|  |         # Verify the size is the new size is what we wanted it to be. | ||||||
|  |         self.debug("Fail Resize Volume ID: %s" % self.volume.id) | ||||||
|  | 
 | ||||||
|  |         # first, an invalid id | ||||||
|  |         cmd                = resizeVolume.resizeVolumeCmd() | ||||||
|  |         cmd.id             = "invalid id" | ||||||
|  |         cmd.diskofferingid = self.services['resizeddiskofferingid'] | ||||||
|  |         success            = False | ||||||
|  |         try: | ||||||
|  |             response = self.apiClient.resizeVolume(cmd) | ||||||
|  |         except Exception as ex: | ||||||
|  |             if str(ex) == "HTTP Error 431: 431": | ||||||
|  |                 success = True | ||||||
|  |         self.assertEqual(success, True, "ResizeVolume - verify invalid id is handled appropriately") | ||||||
|  | 
 | ||||||
|  |         # Next, we'll try an invalid disk offering id | ||||||
|  |         cmd.id             = self.volume.id | ||||||
|  |         cmd.diskofferingid = "invalid id" | ||||||
|  |         success            = False | ||||||
|  |         try: | ||||||
|  |             response = self.apiClient.resizeVolume(cmd) | ||||||
|  |         except Exception as ex: | ||||||
|  |             if "need to specify a disk offering" in str(ex): | ||||||
|  |                 success = True | ||||||
|  |         self.assertEqual(success, True, "ResizeVolume - verify disk offering is handled appropriately") | ||||||
|  | 
 | ||||||
|  |         # Ok, now let's try and resize a volume that is not custom. | ||||||
|  |         cmd.id             = self.volume.id | ||||||
|  |         cmd.diskofferingid = self.services['diskofferingid'] | ||||||
|  |         cmd.size           = 4 | ||||||
|  |         currentSize        = self.volume.size | ||||||
|  | 
 | ||||||
|  |         self.apiClient.resizeVolume(cmd) | ||||||
|  |         count = 0 | ||||||
|  |         success = True | ||||||
|  |         while count < 10: | ||||||
|  |             list_volume_response = list_volumes( | ||||||
|  |                                                 self.apiClient, | ||||||
|  |                                                 id=self.volume.id, | ||||||
|  |                                                 type='DATADISK' | ||||||
|  |                                                 ) | ||||||
|  |             for vol in list_volume_response: | ||||||
|  |                 if vol.id == self.volume.id and vol.size != currentSize: | ||||||
|  |                     success = False | ||||||
|  |             if success: | ||||||
|  |                 break | ||||||
|  |             else: | ||||||
|  |                 time.sleep(1) | ||||||
|  |                 count += 1 | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |                          success, | ||||||
|  |                          True, | ||||||
|  |                          "Verify the volume did not resize" | ||||||
|  |                          ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @attr(tags = ["advanced", "advancedns", "smoke"]) | ||||||
|  |     def test_08_resize_volume(self): | ||||||
|  |         """Resize a volume""" | ||||||
|  |         # Verify the size is the new size is what we wanted it to be. | ||||||
|  |         self.debug("Resize Volume ID: %s" % self.volume.id) | ||||||
|  | 
 | ||||||
|  |         cmd                = resizeVolume.resizeVolumeCmd() | ||||||
|  |         cmd.id             = self.volume.id | ||||||
|  |         cmd.diskofferingid = self.services['resizeddiskofferingid'] | ||||||
|  | 
 | ||||||
|  |         self.apiClient.resizeVolume(cmd) | ||||||
|  | 
 | ||||||
|  |         count = 0 | ||||||
|  |         success = False | ||||||
|  |         while count < 3: | ||||||
|  |             list_volume_response = list_volumes( | ||||||
|  |                                                 self.apiClient, | ||||||
|  |                                                 id=self.volume.id, | ||||||
|  |                                                 type='DATADISK' | ||||||
|  |                                                 ) | ||||||
|  |             for vol in list_volume_response: | ||||||
|  |                 if vol.id == self.volume.id and vol.size == 3221225472L: | ||||||
|  |                     success = True | ||||||
|  |             if success: | ||||||
|  |                 break | ||||||
|  |             else: | ||||||
|  |                 time.sleep(10) | ||||||
|  |                 count += 1 | ||||||
|  | 
 | ||||||
|  |         self.assertEqual( | ||||||
|  |                          success, | ||||||
|  |                          True, | ||||||
|  |                          "Check if the volume resized appropriately" | ||||||
|  |                          ) | ||||||
|  | 
 | ||||||
|  |     @attr(tags = ["advanced", "advancedns", "smoke"]) | ||||||
|  |     def test_09_delete_detached_volume(self): | ||||||
|         """Delete a Volume unattached to an VM |         """Delete a Volume unattached to an VM | ||||||
|         """ |         """ | ||||||
|         # Validate the following |         # Validate the following | ||||||
|  | |||||||
| @ -509,6 +509,12 @@ class Volume: | |||||||
|         [setattr(cmd, k, v) for k, v in kwargs.items()] |         [setattr(cmd, k, v) for k, v in kwargs.items()] | ||||||
|         return(apiclient.listVolumes(cmd)) |         return(apiclient.listVolumes(cmd)) | ||||||
| 
 | 
 | ||||||
|  |     def resize(cls, apiclient, **kwargs): | ||||||
|  |         """Resize a volume""" | ||||||
|  |         cmd = resizeVolume.resizeVolumeCmd() | ||||||
|  |         cmd.id = self.id | ||||||
|  |         [setattr(cmd, k, v) for k, v in kwargs.items()] | ||||||
|  |         return(apiclient.resizeVolume(cmd)) | ||||||
| 
 | 
 | ||||||
| class Snapshot: | class Snapshot: | ||||||
|     """Manage Snapshot Lifecycle |     """Manage Snapshot Lifecycle | ||||||
|  | |||||||
| @ -546,6 +546,7 @@ | |||||||
|         if(checkedSecurityGroupIdArray.length > 0) |         if(checkedSecurityGroupIdArray.length > 0) | ||||||
|           array1.push("&securitygroupids=" + checkedSecurityGroupIdArray.join(",")); |           array1.push("&securitygroupids=" + checkedSecurityGroupIdArray.join(",")); | ||||||
| 				 | 				 | ||||||
|  |         /*				 | ||||||
| 				if(selectedZoneObj.networktype ==	"Advanced" && selectedZoneObj.securitygroupsenabled == true) { // Advanced SG-enabled zone 		
 | 				if(selectedZoneObj.networktype ==	"Advanced" && selectedZoneObj.securitygroupsenabled == true) { // Advanced SG-enabled zone 		
 | ||||||
| 					var networkData = { | 					var networkData = { | ||||||
| 						zoneId: selectedZoneObj.id, | 						zoneId: selectedZoneObj.id, | ||||||
| @ -577,6 +578,8 @@ | |||||||
| 						return;  | 						return;  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 				*/ | ||||||
|  | 				 | ||||||
|       } |       } | ||||||
|       else if (step5ContainerType == 'nothing-to-select') {	   |       else if (step5ContainerType == 'nothing-to-select') {	   | ||||||
| 				if(args.context.networks != null) { //from VPC tier
 | 				if(args.context.networks != null) { //from VPC tier
 | ||||||
|  | |||||||
| @ -1148,6 +1148,7 @@ | |||||||
|                               var array1 = [];															 |                               var array1 = [];															 | ||||||
| 															if(args.context.zones[0].networktype == "Advanced" && args.context.zones[0].securitygroupsenabled	== true) { | 															if(args.context.zones[0].networktype == "Advanced" && args.context.zones[0].securitygroupsenabled	== true) { | ||||||
| 															  array1.push({id: 'account-specific', description: 'Account'}); | 															  array1.push({id: 'account-specific', description: 'Account'}); | ||||||
|  | 																array1.push({id: 'zone-wide', description: 'All'}); | ||||||
| 															} | 															} | ||||||
| 															else {															 | 															else {															 | ||||||
| 																array1.push({id: 'zone-wide', description: 'All'}); | 																array1.push({id: 'zone-wide', description: 'All'}); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user