mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
New feature: Add support to destroy/recover volumes (#3688)
* server: fix resource count of primary storage if some volumes are Expunged but not removed Steps to reproduce the issue (1) create a vm and stop it. check resource count of primary storage (2) download volume. resource count of primary storage is not changed. (3) expunge the vm, the volume will be Expunged state as there is a volume snapshot on secondary storage. The resource count of primary storage decreased. (4) update resource count of the account (or domain), the resource count of primary storage is reset to the value in step (2). * New feature: Add support to destroy/recover volumes * Add integration test for volume destroy/recover * marvin: check resource count of more types * messages translate to JP * Update messages for CN * translate message for NL * fix two issues per Daan's comments Co-authored-by: Andrija Panic <45762285+andrijapanicsb@users.noreply.github.com>
This commit is contained in:
parent
d8906d3d8b
commit
fd5bea838b
@ -239,6 +239,8 @@ public class EventTypes {
|
|||||||
public static final String EVENT_VOLUME_DETAIL_ADD = "VOLUME.DETAIL.ADD";
|
public static final String EVENT_VOLUME_DETAIL_ADD = "VOLUME.DETAIL.ADD";
|
||||||
public static final String EVENT_VOLUME_DETAIL_REMOVE = "VOLUME.DETAIL.REMOVE";
|
public static final String EVENT_VOLUME_DETAIL_REMOVE = "VOLUME.DETAIL.REMOVE";
|
||||||
public static final String EVENT_VOLUME_UPDATE = "VOLUME.UPDATE";
|
public static final String EVENT_VOLUME_UPDATE = "VOLUME.UPDATE";
|
||||||
|
public static final String EVENT_VOLUME_DESTROY = "VOLUME.DESTROY";
|
||||||
|
public static final String EVENT_VOLUME_RECOVER = "VOLUME.RECOVER";
|
||||||
|
|
||||||
// Domains
|
// Domains
|
||||||
public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE";
|
public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE";
|
||||||
@ -706,6 +708,8 @@ public class EventTypes {
|
|||||||
entityEventDetails.put(EVENT_VOLUME_UPLOAD, Volume.class);
|
entityEventDetails.put(EVENT_VOLUME_UPLOAD, Volume.class);
|
||||||
entityEventDetails.put(EVENT_VOLUME_MIGRATE, Volume.class);
|
entityEventDetails.put(EVENT_VOLUME_MIGRATE, Volume.class);
|
||||||
entityEventDetails.put(EVENT_VOLUME_RESIZE, Volume.class);
|
entityEventDetails.put(EVENT_VOLUME_RESIZE, Volume.class);
|
||||||
|
entityEventDetails.put(EVENT_VOLUME_DESTROY, Volume.class);
|
||||||
|
entityEventDetails.put(EVENT_VOLUME_RECOVER, Volume.class);
|
||||||
|
|
||||||
// Domains
|
// Domains
|
||||||
entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class);
|
entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class);
|
||||||
|
|||||||
@ -124,6 +124,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Ready, Event.AttachRequested, Attaching, null));
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Ready, Event.AttachRequested, Attaching, null));
|
||||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Attaching, Event.OperationSucceeded, Ready, null));
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Attaching, Event.OperationSucceeded, Ready, null));
|
||||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Attaching, Event.OperationFailed, Ready, null));
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Attaching, Event.OperationFailed, Ready, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Destroy, Event.RecoverRequested, Ready, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +144,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||||||
SnapshotRequested,
|
SnapshotRequested,
|
||||||
RevertSnapshotRequested,
|
RevertSnapshotRequested,
|
||||||
DestroyRequested,
|
DestroyRequested,
|
||||||
|
RecoverRequested,
|
||||||
ExpungingRequested,
|
ExpungingRequested,
|
||||||
ResizeRequested,
|
ResizeRequested,
|
||||||
AttachRequested,
|
AttachRequested,
|
||||||
|
|||||||
@ -148,4 +148,8 @@ public interface VolumeApiService {
|
|||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags);
|
boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags);
|
||||||
}
|
|
||||||
|
Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge);
|
||||||
|
|
||||||
|
Volume recoverVolume(long volumeId);
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
// 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.admin.volume;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.volume.DestroyVolumeCmd;
|
||||||
|
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
|
|
||||||
|
@APICommand(name = "destroyVolume", description = "Destroys a Volume.", responseObject = VolumeResponse.class, responseView = ResponseView.Full, entityType = {Volume.class},
|
||||||
|
since = "4.14.0",
|
||||||
|
authorized = {RoleType.Admin},
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
responseHasSensitiveInfo = true)
|
||||||
|
public class DestroyVolumeCmdByAdmin extends DestroyVolumeCmd implements AdminCmd {
|
||||||
|
|
||||||
|
public static final Logger s_logger = Logger.getLogger(DestroyVolumeCmdByAdmin.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
CallContext.current().setEventDetails("Volume Id: " + getId());
|
||||||
|
Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false);
|
||||||
|
if (result != null) {
|
||||||
|
VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy volume");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
// 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.admin.volume;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.volume.RecoverVolumeCmd;
|
||||||
|
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
|
|
||||||
|
@APICommand(name = "recoverVolume", description = "Recovers a Destroy volume.", responseObject = VolumeResponse.class, responseView = ResponseView.Full, entityType = {Volume.class},
|
||||||
|
since = "4.14.0",
|
||||||
|
authorized = {RoleType.Admin},
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
responseHasSensitiveInfo = true)
|
||||||
|
public class RecoverVolumeCmdByAdmin extends RecoverVolumeCmd implements AdminCmd {
|
||||||
|
public static final Logger s_logger = Logger.getLogger(RecoverVolumeCmdByAdmin.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
CallContext.current().setEventDetails("Volume Id: " + getId());
|
||||||
|
Volume result = _volumeService.recoverVolume(getId());
|
||||||
|
if (result != null) {
|
||||||
|
VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to recover volume");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -59,6 +59,7 @@ public class ListCapabilitiesCmd extends BaseCmd {
|
|||||||
response.setKVMSnapshotEnabled((Boolean)capabilities.get("KVMSnapshotEnabled"));
|
response.setKVMSnapshotEnabled((Boolean)capabilities.get("KVMSnapshotEnabled"));
|
||||||
response.setAllowUserViewDestroyedVM((Boolean)capabilities.get("allowUserViewDestroyedVM"));
|
response.setAllowUserViewDestroyedVM((Boolean)capabilities.get("allowUserViewDestroyedVM"));
|
||||||
response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM"));
|
response.setAllowUserExpungeRecoverVM((Boolean)capabilities.get("allowUserExpungeRecoverVM"));
|
||||||
|
response.setAllowUserExpungeRecoverVolume((Boolean)capabilities.get("allowUserExpungeRecoverVolume"));
|
||||||
response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts"));
|
response.setAllowUserViewAllDomainAccounts((Boolean)capabilities.get("allowUserViewAllDomainAccounts"));
|
||||||
if (capabilities.containsKey("apiLimitInterval")) {
|
if (capabilities.containsKey("apiLimitInterval")) {
|
||||||
response.setApiLimitInterval((Integer)capabilities.get("apiLimitInterval"));
|
response.setApiLimitInterval((Integer)capabilities.get("apiLimitInterval"));
|
||||||
|
|||||||
@ -82,8 +82,8 @@ public class DeleteVolumeCmd extends BaseCmd {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() throws ConcurrentOperationException {
|
public void execute() throws ConcurrentOperationException {
|
||||||
CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId()));
|
CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId()));
|
||||||
boolean result = _volumeService.deleteVolume(id, CallContext.current().getCallingAccount());
|
Volume result = _volumeService.destroyVolume(id, CallContext.current().getCallingAccount(), true, false);
|
||||||
if (result) {
|
if (result != null) {
|
||||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
setResponseObject(response);
|
setResponseObject(response);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -0,0 +1,130 @@
|
|||||||
|
// 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.log4j.Logger;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
|
import org.apache.cloudstack.api.ACL;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiCommandJobType;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
|
@APICommand(name = "destroyVolume", description = "Destroys a Volume.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {Volume.class},
|
||||||
|
since = "4.14.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
responseHasSensitiveInfo = true)
|
||||||
|
public class DestroyVolumeCmd extends BaseAsyncCmd {
|
||||||
|
public static final Logger s_logger = Logger.getLogger(DestroyVolumeCmd.class.getName());
|
||||||
|
|
||||||
|
private static final String s_name = "destroyvolumeresponse";
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ACL(accessType = AccessType.OperateEntry)
|
||||||
|
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=VolumeResponse.class,
|
||||||
|
required=true, description="The ID of the volume")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.EXPUNGE,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
description = "If true is passed, the volume is expunged immediately. False by default.",
|
||||||
|
since = "4.6.0")
|
||||||
|
private Boolean expunge;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getExpunge() {
|
||||||
|
if (expunge == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return expunge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return s_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
Volume volume = _entityMgr.findById(Volume.class, getId());
|
||||||
|
if (volume != null) {
|
||||||
|
return volume.getAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_VOLUME_DESTROY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "destroying volume: " + getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApiCommandJobType getInstanceType() {
|
||||||
|
return ApiCommandJobType.Volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getInstanceId() {
|
||||||
|
return getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
CallContext.current().setEventDetails("Volume Id: " + getId());
|
||||||
|
Volume result = _volumeService.destroyVolume(getId(), CallContext.current().getCallingAccount(), getExpunge(), false);
|
||||||
|
if (result != null) {
|
||||||
|
VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Restricted, result);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy volume");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,6 +24,7 @@ import org.apache.cloudstack.api.ApiCommandJobType;
|
|||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
|
import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
|
||||||
import org.apache.cloudstack.api.Parameter;
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd.CommandType;
|
||||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
import org.apache.cloudstack.api.response.ClusterResponse;
|
import org.apache.cloudstack.api.response.ClusterResponse;
|
||||||
@ -88,6 +89,9 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd implements UserCm
|
|||||||
RoleType.Admin})
|
RoleType.Admin})
|
||||||
private Boolean display;
|
private Boolean display;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the volume. Possible values are: Ready, Allocated, Destroy, Expunging, Expunged.")
|
||||||
|
private String state;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////////// Accessors ///////////////////////
|
/////////////////// Accessors ///////////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
@ -139,6 +143,10 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd implements UserCm
|
|||||||
}
|
}
|
||||||
return super.getDisplay();
|
return super.getDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
/////////////// API Implementation///////////////////
|
/////////////// API Implementation///////////////////
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|||||||
@ -0,0 +1,91 @@
|
|||||||
|
// 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.log4j.Logger;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
|
@APICommand(name = "recoverVolume", description = "Recovers a Destroy volume.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {Volume.class},
|
||||||
|
since = "4.14.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
responseHasSensitiveInfo = true)
|
||||||
|
public class RecoverVolumeCmd extends BaseCmd {
|
||||||
|
public static final Logger s_logger = Logger.getLogger(RecoverVolumeCmd.class.getName());
|
||||||
|
|
||||||
|
private static final String s_name = "recovervolumeresponse";
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "The ID of the volume")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return s_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
Volume volume = _entityMgr.findById(Volume.class, getId());
|
||||||
|
if (volume != null) {
|
||||||
|
return volume.getAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
CallContext.current().setEventDetails("Volume Id: " + getId());
|
||||||
|
Volume result = _volumeService.recoverVolume(getId());
|
||||||
|
if (result != null) {
|
||||||
|
VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Full, result);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to recover volume");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -84,6 +84,10 @@ public class CapabilitiesResponse extends BaseResponse {
|
|||||||
@Param(description = "true if the user can recover and expunge virtualmachines, false otherwise", since = "4.6.0")
|
@Param(description = "true if the user can recover and expunge virtualmachines, false otherwise", since = "4.6.0")
|
||||||
private boolean allowUserExpungeRecoverVM;
|
private boolean allowUserExpungeRecoverVM;
|
||||||
|
|
||||||
|
@SerializedName("allowuserexpungerecovervolume")
|
||||||
|
@Param(description = "true if the user can recover and expunge volumes, false otherwise", since = "4.14.0")
|
||||||
|
private boolean allowUserExpungeRecoverVolume;
|
||||||
|
|
||||||
@SerializedName("allowuserviewalldomainaccounts")
|
@SerializedName("allowuserviewalldomainaccounts")
|
||||||
@Param(description = "true if users can see all accounts within the same domain, false otherwise")
|
@Param(description = "true if users can see all accounts within the same domain, false otherwise")
|
||||||
private boolean allowUserViewAllDomainAccounts;
|
private boolean allowUserViewAllDomainAccounts;
|
||||||
@ -148,7 +152,11 @@ public class CapabilitiesResponse extends BaseResponse {
|
|||||||
this.allowUserExpungeRecoverVM = allowUserExpungeRecoverVM;
|
this.allowUserExpungeRecoverVM = allowUserExpungeRecoverVM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAllowUserExpungeRecoverVolume(boolean allowUserExpungeRecoverVolume) {
|
||||||
|
this.allowUserExpungeRecoverVolume = allowUserExpungeRecoverVolume;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAllowUserViewAllDomainAccounts(boolean allowUserViewAllDomainAccounts) {
|
public void setAllowUserViewAllDomainAccounts(boolean allowUserViewAllDomainAccounts) {
|
||||||
this.allowUserViewAllDomainAccounts = allowUserViewAllDomainAccounts;
|
this.allowUserViewAllDomainAccounts = allowUserViewAllDomainAccounts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1578,6 +1578,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||||||
if (volume.getState() == Volume.State.Allocated) {
|
if (volume.getState() == Volume.State.Allocated) {
|
||||||
_volsDao.remove(volume.getId());
|
_volsDao.remove(volume.getId());
|
||||||
stateTransitTo(volume, Volume.Event.DestroyRequested);
|
stateTransitTo(volume, Volume.Event.DestroyRequested);
|
||||||
|
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplay());
|
||||||
|
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplay(), new Long(volume.getSize()));
|
||||||
} else {
|
} else {
|
||||||
volService.destroyVolume(volume.getId());
|
volService.destroyVolume(volume.getId());
|
||||||
}
|
}
|
||||||
@ -1585,8 +1587,6 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||||||
// publish usage event for the volume
|
// publish usage event for the volume
|
||||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), Volume.class.getName(),
|
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), Volume.class.getName(),
|
||||||
volume.getUuid(), volume.isDisplayVolume());
|
volume.getUuid(), volume.isDisplayVolume());
|
||||||
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplay());
|
|
||||||
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplay(), new Long(volume.getSize()));
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
s_logger.debug("Failed to destroy volume" + volume.getId(), e);
|
s_logger.debug("Failed to destroy volume" + volume.getId(), e);
|
||||||
throw new CloudRuntimeException("Failed to destroy volume" + volume.getId(), e);
|
throw new CloudRuntimeException("Failed to destroy volume" + volume.getId(), e);
|
||||||
|
|||||||
@ -119,6 +119,7 @@ import com.cloud.utils.Pair;
|
|||||||
import com.cloud.utils.db.DB;
|
import com.cloud.utils.db.DB;
|
||||||
import com.cloud.utils.db.GlobalLock;
|
import com.cloud.utils.db.GlobalLock;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class VolumeServiceImpl implements VolumeService {
|
public class VolumeServiceImpl implements VolumeService {
|
||||||
@ -325,7 +326,10 @@ public class VolumeServiceImpl implements VolumeService {
|
|||||||
VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volume.getId());
|
VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volume.getId());
|
||||||
if (volumeStore != null) {
|
if (volumeStore != null) {
|
||||||
if (volumeStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) {
|
if (volumeStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) {
|
||||||
s_logger.debug("Volume: " + volume.getName() + " is currently being uploaded; cant' delete it.");
|
String msg = "Volume: " + volume.getName() + " is currently being uploaded; cant' delete it.";
|
||||||
|
s_logger.debug(msg);
|
||||||
|
result.setSuccess(false);
|
||||||
|
result.setResult(msg);
|
||||||
future.complete(result);
|
future.complete(result);
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
@ -1213,6 +1217,12 @@ public class VolumeServiceImpl implements VolumeService {
|
|||||||
snapshotMgr.deletePoliciesForVolume(volumeId);
|
snapshotMgr.deletePoliciesForVolume(volumeId);
|
||||||
|
|
||||||
vol.stateTransit(Volume.Event.OperationSucceeded);
|
vol.stateTransit(Volume.Event.OperationSucceeded);
|
||||||
|
|
||||||
|
if (vol.getAttachedVM() == null || vol.getAttachedVM().getType() == VirtualMachine.Type.User) {
|
||||||
|
// Decrement the resource count for volumes and primary storage belonging user VM's only
|
||||||
|
_resourceLimitMgr.decrementResourceCount(vol.getAccountId(), ResourceType.volume, vol.isDisplay());
|
||||||
|
_resourceLimitMgr.decrementResourceCount(vol.getAccountId(), ResourceType.primary_storage, vol.isDisplay(), new Long(vol.getSize()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -1809,6 +1809,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
Long clusterId = cmd.getClusterId();
|
Long clusterId = cmd.getClusterId();
|
||||||
Long diskOffId = cmd.getDiskOfferingId();
|
Long diskOffId = cmd.getDiskOfferingId();
|
||||||
Boolean display = cmd.getDisplay();
|
Boolean display = cmd.getDisplay();
|
||||||
|
String state = cmd.getState();
|
||||||
|
|
||||||
Long zoneId = cmd.getZoneId();
|
Long zoneId = cmd.getZoneId();
|
||||||
Long podId = cmd.getPodId();
|
Long podId = cmd.getPodId();
|
||||||
@ -1844,8 +1845,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
sb.and("storageId", sb.entity().getPoolUuid(), SearchCriteria.Op.EQ);
|
sb.and("storageId", sb.entity().getPoolUuid(), SearchCriteria.Op.EQ);
|
||||||
sb.and("diskOfferingId", sb.entity().getDiskOfferingId(), SearchCriteria.Op.EQ);
|
sb.and("diskOfferingId", sb.entity().getDiskOfferingId(), SearchCriteria.Op.EQ);
|
||||||
sb.and("display", sb.entity().isDisplayVolume(), SearchCriteria.Op.EQ);
|
sb.and("display", sb.entity().isDisplayVolume(), SearchCriteria.Op.EQ);
|
||||||
// Only return volumes that are not destroyed
|
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
|
||||||
sb.and("state", sb.entity().getState(), SearchCriteria.Op.NEQ);
|
sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ);
|
||||||
sb.and("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ);
|
sb.and("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ);
|
||||||
// display UserVM volumes only
|
// display UserVM volumes only
|
||||||
sb.and().op("type", sb.entity().getVmType(), SearchCriteria.Op.NIN);
|
sb.and().op("type", sb.entity().getVmType(), SearchCriteria.Op.NIN);
|
||||||
@ -1860,6 +1861,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
SearchCriteria<VolumeJoinVO> ssc = _volumeJoinDao.createSearchCriteria();
|
SearchCriteria<VolumeJoinVO> ssc = _volumeJoinDao.createSearchCriteria();
|
||||||
ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
||||||
ssc.addOr("volumeType", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
ssc.addOr("volumeType", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
||||||
|
ssc.addOr("state", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
||||||
|
|
||||||
sc.addAnd("name", SearchCriteria.Op.SC, ssc);
|
sc.addAnd("name", SearchCriteria.Op.SC, ssc);
|
||||||
}
|
}
|
||||||
@ -1918,8 +1920,11 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||||||
// Don't return DomR and ConsoleProxy volumes
|
// Don't return DomR and ConsoleProxy volumes
|
||||||
sc.setParameters("type", VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter);
|
sc.setParameters("type", VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter);
|
||||||
|
|
||||||
// Only return volumes that are not destroyed
|
if (state != null) {
|
||||||
sc.setParameters("state", Volume.State.Destroy);
|
sc.setParameters("state", state);
|
||||||
|
} else if (!_accountMgr.isAdmin(caller.getId())) {
|
||||||
|
sc.setParameters("stateNEQ", Volume.State.Expunged);
|
||||||
|
}
|
||||||
|
|
||||||
// search Volume details by ids
|
// search Volume details by ids
|
||||||
Pair<List<VolumeJoinVO>, Integer> uniqueVolPair = _volumeJoinDao.searchAndCount(sc, searchFilter);
|
Pair<List<VolumeJoinVO>, Integer> uniqueVolPair = _volumeJoinDao.searchAndCount(sc, searchFilter);
|
||||||
|
|||||||
@ -265,9 +265,11 @@ import org.apache.cloudstack.api.command.admin.vm.UpgradeVMCmdByAdmin;
|
|||||||
import org.apache.cloudstack.api.command.admin.vmsnapshot.RevertToVMSnapshotCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.vmsnapshot.RevertToVMSnapshotCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.AttachVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.AttachVolumeCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.CreateVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.CreateVolumeCmdByAdmin;
|
||||||
|
import org.apache.cloudstack.api.command.admin.volume.DestroyVolumeCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.DetachVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.DetachVolumeCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.ListVolumesCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.ListVolumesCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin;
|
||||||
|
import org.apache.cloudstack.api.command.admin.volume.RecoverVolumeCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.ResizeVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.ResizeVolumeCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.UpdateVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.UpdateVolumeCmdByAdmin;
|
||||||
import org.apache.cloudstack.api.command.admin.volume.UploadVolumeCmdByAdmin;
|
import org.apache.cloudstack.api.command.admin.volume.UploadVolumeCmdByAdmin;
|
||||||
@ -483,12 +485,14 @@ import org.apache.cloudstack.api.command.user.volume.AddResourceDetailCmd;
|
|||||||
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
|
||||||
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.DeleteVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.volume.DestroyVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.GetUploadParamsForVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.GetUploadParamsForVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd;
|
import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
|
import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.volume.RecoverVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.RemoveResourceDetailCmd;
|
import org.apache.cloudstack.api.command.user.volume.RemoveResourceDetailCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
|
||||||
import org.apache.cloudstack.api.command.user.volume.UpdateVolumeCmd;
|
import org.apache.cloudstack.api.command.user.volume.UpdateVolumeCmd;
|
||||||
@ -643,6 +647,7 @@ import com.cloud.storage.ScopeType;
|
|||||||
import com.cloud.storage.StorageManager;
|
import com.cloud.storage.StorageManager;
|
||||||
import com.cloud.storage.StoragePool;
|
import com.cloud.storage.StoragePool;
|
||||||
import com.cloud.storage.Volume;
|
import com.cloud.storage.Volume;
|
||||||
|
import com.cloud.storage.VolumeApiServiceImpl;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.dao.DiskOfferingDao;
|
import com.cloud.storage.dao.DiskOfferingDao;
|
||||||
import com.cloud.storage.dao.GuestOSCategoryDao;
|
import com.cloud.storage.dao.GuestOSCategoryDao;
|
||||||
@ -2948,6 +2953,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
cmdList.add(MigrateVolumeCmd.class);
|
cmdList.add(MigrateVolumeCmd.class);
|
||||||
cmdList.add(ResizeVolumeCmd.class);
|
cmdList.add(ResizeVolumeCmd.class);
|
||||||
cmdList.add(UploadVolumeCmd.class);
|
cmdList.add(UploadVolumeCmd.class);
|
||||||
|
cmdList.add(DestroyVolumeCmd.class);
|
||||||
|
cmdList.add(RecoverVolumeCmd.class);
|
||||||
cmdList.add(CreateStaticRouteCmd.class);
|
cmdList.add(CreateStaticRouteCmd.class);
|
||||||
cmdList.add(CreateVPCCmd.class);
|
cmdList.add(CreateVPCCmd.class);
|
||||||
cmdList.add(DeleteStaticRouteCmd.class);
|
cmdList.add(DeleteStaticRouteCmd.class);
|
||||||
@ -3093,6 +3100,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
cmdList.add(UpdateVolumeCmdByAdmin.class);
|
cmdList.add(UpdateVolumeCmdByAdmin.class);
|
||||||
cmdList.add(UploadVolumeCmdByAdmin.class);
|
cmdList.add(UploadVolumeCmdByAdmin.class);
|
||||||
cmdList.add(ListVolumesCmdByAdmin.class);
|
cmdList.add(ListVolumesCmdByAdmin.class);
|
||||||
|
cmdList.add(DestroyVolumeCmdByAdmin.class);
|
||||||
|
cmdList.add(RecoverVolumeCmdByAdmin.class);
|
||||||
cmdList.add(AssociateIPAddrCmdByAdmin.class);
|
cmdList.add(AssociateIPAddrCmdByAdmin.class);
|
||||||
cmdList.add(ListPublicIpAddressesCmdByAdmin.class);
|
cmdList.add(ListPublicIpAddressesCmdByAdmin.class);
|
||||||
cmdList.add(CreateNetworkCmdByAdmin.class);
|
cmdList.add(CreateNetworkCmdByAdmin.class);
|
||||||
@ -3501,6 +3510,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
|
|
||||||
final boolean allowUserViewDestroyedVM = (QueryService.AllowUserViewDestroyedVM.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
|
final boolean allowUserViewDestroyedVM = (QueryService.AllowUserViewDestroyedVM.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
|
||||||
final boolean allowUserExpungeRecoverVM = (UserVmManager.AllowUserExpungeRecoverVm.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
|
final boolean allowUserExpungeRecoverVM = (UserVmManager.AllowUserExpungeRecoverVm.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
|
||||||
|
final boolean allowUserExpungeRecoverVolume = (VolumeApiServiceImpl.AllowUserExpungeRecoverVolume.valueIn(caller.getId()) | _accountService.isAdmin(caller.getId()));
|
||||||
|
|
||||||
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(caller.getDomainId()));
|
final boolean allowUserViewAllDomainAccounts = (QueryService.AllowUserViewAllDomainAccounts.valueIn(caller.getDomainId()));
|
||||||
|
|
||||||
@ -3523,6 +3533,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
capabilities.put("KVMSnapshotEnabled", KVMSnapshotEnabled);
|
capabilities.put("KVMSnapshotEnabled", KVMSnapshotEnabled);
|
||||||
capabilities.put("allowUserViewDestroyedVM", allowUserViewDestroyedVM);
|
capabilities.put("allowUserViewDestroyedVM", allowUserViewDestroyedVM);
|
||||||
capabilities.put("allowUserExpungeRecoverVM", allowUserExpungeRecoverVM);
|
capabilities.put("allowUserExpungeRecoverVM", allowUserExpungeRecoverVM);
|
||||||
|
capabilities.put("allowUserExpungeRecoverVolume", allowUserExpungeRecoverVolume);
|
||||||
capabilities.put("allowUserViewAllDomainAccounts", allowUserViewAllDomainAccounts);
|
capabilities.put("allowUserViewAllDomainAccounts", allowUserViewAllDomainAccounts);
|
||||||
if (apiLimitEnabled) {
|
if (apiLimitEnabled) {
|
||||||
capabilities.put("apiLimitInterval", apiLimitInterval);
|
capabilities.put("apiLimitInterval", apiLimitInterval);
|
||||||
|
|||||||
@ -275,6 +275,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
static final ConfigKey<Boolean> VolumeUrlCheck = new ConfigKey<Boolean>("Advanced", Boolean.class, "volume.url.check", "true",
|
static final ConfigKey<Boolean> VolumeUrlCheck = new ConfigKey<Boolean>("Advanced", Boolean.class, "volume.url.check", "true",
|
||||||
"Check the url for a volume before downloading it from the management server. Set to false when you managment has no internet access.", true);
|
"Check the url for a volume before downloading it from the management server. Set to false when you managment has no internet access.", true);
|
||||||
|
|
||||||
|
public static final ConfigKey<Boolean> AllowUserExpungeRecoverVolume = new ConfigKey<Boolean>("Advanced", Boolean.class, "allow.user.expunge.recover.volume", "true",
|
||||||
|
"Determines whether users can expunge or recover their volume", true, ConfigKey.Scope.Account);
|
||||||
|
|
||||||
private long _maxVolumeSizeInGb;
|
private long _maxVolumeSizeInGb;
|
||||||
private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
|
private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
|
||||||
|
|
||||||
@ -1261,20 +1264,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
* Otherwise, after the removal in the database, we will try to remove the volume from both primary and secondary storage.
|
* Otherwise, after the removal in the database, we will try to remove the volume from both primary and secondary storage.
|
||||||
*/
|
*/
|
||||||
public boolean deleteVolume(long volumeId, Account caller) throws ConcurrentOperationException {
|
public boolean deleteVolume(long volumeId, Account caller) throws ConcurrentOperationException {
|
||||||
VolumeVO volume = retrieveAndValidateVolume(volumeId, caller);
|
Volume volume = destroyVolume(volumeId, caller, true, true);
|
||||||
|
return (volume != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deleteVolumeFromStorage(VolumeVO volume, Account caller) throws ConcurrentOperationException {
|
||||||
try {
|
try {
|
||||||
destroyVolumeIfPossible(volume);
|
|
||||||
// Mark volume as removed if volume has not been created on primary or secondary
|
|
||||||
if (volume.getState() == Volume.State.Allocated) {
|
|
||||||
_volsDao.remove(volumeId);
|
|
||||||
stateTransitTo(volume, Volume.Event.DestroyRequested);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
expungeVolumesInPrimaryStorageIfNeeded(volume);
|
expungeVolumesInPrimaryStorageIfNeeded(volume);
|
||||||
expungeVolumesInSecondaryStorageIfNeeded(volume);
|
expungeVolumesInSecondaryStorageIfNeeded(volume);
|
||||||
cleanVolumesCache(volume);
|
cleanVolumesCache(volume);
|
||||||
return true;
|
return true;
|
||||||
} catch (InterruptedException | ExecutionException | NoTransitionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
s_logger.warn("Failed to expunge volume: " + volume.getUuid(), e);
|
s_logger.warn("Failed to expunge volume: " + volume.getUuid(), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1301,7 +1301,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
if (!_snapshotMgr.canOperateOnVolume(volume)) {
|
if (!_snapshotMgr.canOperateOnVolume(volume)) {
|
||||||
throw new InvalidParameterValueException("There are snapshot operations in progress on the volume, unable to delete it");
|
throw new InvalidParameterValueException("There are snapshot operations in progress on the volume, unable to delete it");
|
||||||
}
|
}
|
||||||
if (volume.getInstanceId() != null) {
|
if (volume.getInstanceId() != null && volume.getState() != Volume.State.Expunged) {
|
||||||
throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
|
throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
|
||||||
}
|
}
|
||||||
if (volume.getState() == Volume.State.UploadOp) {
|
if (volume.getState() == Volume.State.UploadOp) {
|
||||||
@ -1334,12 +1334,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
* The volume is destroyed via {@link VolumeService#destroyVolume(long)} method.
|
* The volume is destroyed via {@link VolumeService#destroyVolume(long)} method.
|
||||||
*/
|
*/
|
||||||
protected void destroyVolumeIfPossible(VolumeVO volume) {
|
protected void destroyVolumeIfPossible(VolumeVO volume) {
|
||||||
if (volume.getState() != Volume.State.Destroy && volume.getState() != Volume.State.Expunging && volume.getState() != Volume.State.Expunged) {
|
if (volume.getState() != Volume.State.Destroy && volume.getState() != Volume.State.Expunging && volume.getState() != Volume.State.Expunged && volume.getState() != Volume.State.Allocated && volume.getState() != Volume.State.Uploaded) {
|
||||||
volService.destroyVolume(volume.getId());
|
volService.destroyVolume(volume.getId());
|
||||||
|
|
||||||
// Decrement the resource count for volumes and primary storage belonging user VM's only
|
|
||||||
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplayVolume());
|
|
||||||
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1389,6 +1385,89 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
return _volStateMachine.transitTo(vol, event, null, _volsDao);
|
return _volStateMachine.transitTo(vol, event, null, _volsDao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_DESTROY, eventDescription = "destroying a volume")
|
||||||
|
public Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge) {
|
||||||
|
VolumeVO volume = retrieveAndValidateVolume(volumeId, caller);
|
||||||
|
|
||||||
|
if (expunge) {
|
||||||
|
// When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVolume is false for the caller.
|
||||||
|
final Long userId = caller.getAccountId();
|
||||||
|
if (!forceExpunge && !_accountMgr.isAdmin(userId) && !AllowUserExpungeRecoverVolume.valueIn(userId)) {
|
||||||
|
throw new PermissionDeniedException("Expunging a volume can only be done by an Admin. Or when the allow.user.expunge.recover.volume key is set.");
|
||||||
|
}
|
||||||
|
} else if (volume.getState() == Volume.State.Allocated || volume.getState() == Volume.State.Uploaded) {
|
||||||
|
throw new InvalidParameterValueException("The volume in Allocated/Uploaded state can only be expunged not destroyed/recovered");
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyVolumeIfPossible(volume);
|
||||||
|
|
||||||
|
if (expunge) {
|
||||||
|
// Mark volume as removed if volume has not been created on primary or secondary
|
||||||
|
if (volume.getState() == Volume.State.Allocated) {
|
||||||
|
_volsDao.remove(volume.getId());
|
||||||
|
try {
|
||||||
|
stateTransitTo(volume, Volume.Event.DestroyRequested);
|
||||||
|
} catch (NoTransitionException e) {
|
||||||
|
s_logger.debug("Failed to destroy volume" + volume.getId(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplay());
|
||||||
|
_resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplay(), new Long(volume.getSize()));
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
if (!deleteVolumeFromStorage(volume, caller)) {
|
||||||
|
s_logger.warn("Failed to expunge volume: " + volumeId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_RECOVER, eventDescription = "recovering a volume in Destroy state")
|
||||||
|
public Volume recoverVolume(long volumeId) {
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
final Long userId = caller.getAccountId();
|
||||||
|
|
||||||
|
// Verify input parameters
|
||||||
|
final VolumeVO volume = _volsDao.findById(volumeId);
|
||||||
|
|
||||||
|
if (volume == null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find a volume with id " + volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVolume is false for the caller.
|
||||||
|
if (!_accountMgr.isAdmin(userId) && !AllowUserExpungeRecoverVolume.valueIn(userId)) {
|
||||||
|
throw new PermissionDeniedException("Recovering a volume can only be done by an Admin. Or when the allow.user.expunge.recover.volume key is set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_accountMgr.checkAccess(caller, null, true, volume);
|
||||||
|
|
||||||
|
if (volume.getState() != Volume.State.Destroy) {
|
||||||
|
throw new InvalidParameterValueException("Please specify a volume in Destroy state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize());
|
||||||
|
} catch (ResourceAllocationException e) {
|
||||||
|
s_logger.error("primary storage resource limit check failed", e);
|
||||||
|
throw new InvalidParameterValueException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
stateTransitTo(volume, Volume.Event.RecoverRequested);
|
||||||
|
} catch (NoTransitionException e) {
|
||||||
|
s_logger.debug("Failed to recover volume" + volume.getId(), e);
|
||||||
|
throw new CloudRuntimeException("Failed to recover volume" + volume.getId(), e);
|
||||||
|
}
|
||||||
|
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplay());
|
||||||
|
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplay(), new Long(volume.getSize()));
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true)
|
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true)
|
||||||
public Volume attachVolumeToVM(AttachVolumeCmd command) {
|
public Volume attachVolumeToVM(AttachVolumeCmd command) {
|
||||||
@ -3409,6 +3488,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[] {ConcurrentMigrationsThresholdPerDatastore};
|
return new ConfigKey<?>[] {ConcurrentMigrationsThresholdPerDatastore, AllowUserExpungeRecoverVolume};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,7 +132,6 @@ import com.cloud.region.ha.GlobalLoadBalancingRulesService;
|
|||||||
import com.cloud.server.auth.UserAuthenticator;
|
import com.cloud.server.auth.UserAuthenticator;
|
||||||
import com.cloud.server.auth.UserAuthenticator.ActionOnFailedAuthentication;
|
import com.cloud.server.auth.UserAuthenticator.ActionOnFailedAuthentication;
|
||||||
import com.cloud.storage.VMTemplateVO;
|
import com.cloud.storage.VMTemplateVO;
|
||||||
import com.cloud.storage.Volume;
|
|
||||||
import com.cloud.storage.VolumeApiService;
|
import com.cloud.storage.VolumeApiService;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.dao.VMTemplateDao;
|
import com.cloud.storage.dao.VMTemplateDao;
|
||||||
@ -782,13 +781,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
// Mark the account's volumes as destroyed
|
// Mark the account's volumes as destroyed
|
||||||
List<VolumeVO> volumes = _volumeDao.findDetachedByAccount(accountId);
|
List<VolumeVO> volumes = _volumeDao.findDetachedByAccount(accountId);
|
||||||
for (VolumeVO volume : volumes) {
|
for (VolumeVO volume : volumes) {
|
||||||
if (!volume.getState().equals(Volume.State.Destroy)) {
|
try {
|
||||||
try {
|
volumeService.deleteVolume(volume.getId(), caller);
|
||||||
volumeService.deleteVolume(volume.getId(), caller);
|
} catch (Exception ex) {
|
||||||
} catch (Exception ex) {
|
s_logger.warn("Failed to cleanup volumes as a part of account id=" + accountId + " cleanup due to Exception: ", ex);
|
||||||
s_logger.warn("Failed to cleanup volumes as a part of account id=" + accountId + " cleanup due to Exception: ", ex);
|
accountCleanupNeeded = true;
|
||||||
accountCleanupNeeded = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2234,11 +2234,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
List<VolumeVO> rootVol = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
|
List<VolumeVO> rootVol = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
|
||||||
// expunge the vm
|
// expunge the vm
|
||||||
_itMgr.advanceExpunge(vm.getUuid());
|
_itMgr.advanceExpunge(vm.getUuid());
|
||||||
// Update Resource count
|
|
||||||
if (vm.getAccountId() != Account.ACCOUNT_ID_SYSTEM && !rootVol.isEmpty()) {
|
|
||||||
_resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.volume);
|
|
||||||
_resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, new Long(rootVol.get(0).getSize()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only if vm is not expunged already, cleanup it's resources
|
// Only if vm is not expunged already, cleanup it's resources
|
||||||
if (vm.getRemoved() == null) {
|
if (vm.getRemoved() == null) {
|
||||||
|
|||||||
@ -735,8 +735,6 @@ public class VolumeApiServiceImplTest {
|
|||||||
volumeApiServiceImpl.destroyVolumeIfPossible(volumeVoMock);
|
volumeApiServiceImpl.destroyVolumeIfPossible(volumeVoMock);
|
||||||
|
|
||||||
Mockito.verify(volumeServiceMock, Mockito.times(1)).destroyVolume(volumeMockId);
|
Mockito.verify(volumeServiceMock, Mockito.times(1)).destroyVolume(volumeMockId);
|
||||||
Mockito.verify(resourceLimitServiceMock, Mockito.times(1)).decrementResourceCount(accountMockId, ResourceType.volume, true);
|
|
||||||
Mockito.verify(resourceLimitServiceMock, Mockito.times(1)).decrementResourceCount(accountMockId, ResourceType.primary_storage, true, volumeSizeMock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyMocksForTestDestroyVolumeWhenVolumeIsNotInRightState() {
|
private void verifyMocksForTestDestroyVolumeWhenVolumeIsNotInRightState() {
|
||||||
@ -969,26 +967,6 @@ public class VolumeApiServiceImplTest {
|
|||||||
Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested);
|
Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void deleteVolumeTestVolumeStateReadyThrowingNoTransitionException() throws InterruptedException, ExecutionException, NoTransitionException {
|
|
||||||
Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState();
|
|
||||||
|
|
||||||
Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock);
|
|
||||||
Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock);
|
|
||||||
Mockito.doThrow(NoTransitionException.class).when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock);
|
|
||||||
|
|
||||||
Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId);
|
|
||||||
Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested);
|
|
||||||
|
|
||||||
boolean result = volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock);
|
|
||||||
|
|
||||||
Assert.assertFalse(result);
|
|
||||||
Mockito.verify(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock);
|
|
||||||
Mockito.verify(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock);
|
|
||||||
Mockito.verify(volumeDaoMock, Mockito.times(0)).remove(volumeMockId);
|
|
||||||
Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = RuntimeException.class)
|
@Test(expected = RuntimeException.class)
|
||||||
public void deleteVolumeTestVolumeStateReadyThrowingRuntimeException() throws InterruptedException, ExecutionException, NoTransitionException {
|
public void deleteVolumeTestVolumeStateReadyThrowingRuntimeException() throws InterruptedException, ExecutionException, NoTransitionException {
|
||||||
Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState();
|
Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState();
|
||||||
|
|||||||
507
test/integration/component/test_volume_destroy_recover.py
Normal file
507
test/integration/component/test_volume_destroy_recover.py
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
# 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.
|
||||||
|
""" tests for Volume improvement (Destroy/Recover) in cloudstack 4.14.0.0
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Import Local Modules
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||||
|
from marvin.cloudstackAPI import (deleteVolume, extractVolume, recoverVolume)
|
||||||
|
from marvin.lib.utils import (validateList,
|
||||||
|
cleanup_resources)
|
||||||
|
from marvin.lib.base import (Resources,
|
||||||
|
Volume,
|
||||||
|
Account,
|
||||||
|
Domain,
|
||||||
|
Network,
|
||||||
|
NetworkOffering,
|
||||||
|
VirtualMachine,
|
||||||
|
ServiceOffering,
|
||||||
|
DiskOffering,
|
||||||
|
Zone)
|
||||||
|
from marvin.lib.common import (get_domain,
|
||||||
|
get_zone,
|
||||||
|
get_template,
|
||||||
|
matchResourceCount,
|
||||||
|
isAccountResourceCountEqualToExpectedCount)
|
||||||
|
from marvin.codes import (PASS, FAILED, RESOURCE_PRIMARY_STORAGE, RESOURCE_VOLUME)
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
class TestVolumeDestroyRecover(cloudstackTestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.testClient = super(
|
||||||
|
TestVolumeDestroyRecover,
|
||||||
|
cls).getClsTestClient()
|
||||||
|
cls.apiclient = cls.testClient.getApiClient()
|
||||||
|
cls.services = cls.testClient.getParsedTestDataConfig()
|
||||||
|
zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
||||||
|
cls.zone = Zone(zone.__dict__)
|
||||||
|
cls._cleanup = []
|
||||||
|
|
||||||
|
cls.logger = logging.getLogger("TestVolumeDestroyRecover")
|
||||||
|
cls.stream_handler = logging.StreamHandler()
|
||||||
|
cls.logger.setLevel(logging.DEBUG)
|
||||||
|
cls.logger.addHandler(cls.stream_handler)
|
||||||
|
|
||||||
|
# Get Domain and templates
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
|
||||||
|
cls.template = get_template(cls.apiclient, cls.zone.id, hypervisor="KVM")
|
||||||
|
if cls.template == FAILED:
|
||||||
|
sys.exit(1)
|
||||||
|
cls.templatesize = (cls.template.size / (1024 ** 3))
|
||||||
|
|
||||||
|
cls.services['mode'] = cls.zone.networktype
|
||||||
|
# Create Account
|
||||||
|
cls.account = Account.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["account"],
|
||||||
|
admin=True,
|
||||||
|
domainid=cls.domain.id
|
||||||
|
)
|
||||||
|
accounts = Account.list(cls.apiclient, id=cls.account.id)
|
||||||
|
cls.expectedCount = int(accounts[0].primarystoragetotal)
|
||||||
|
cls.volumeTotal = int(accounts[0].volumetotal)
|
||||||
|
|
||||||
|
if cls.zone.securitygroupsenabled:
|
||||||
|
cls.services["shared_network_offering"]["specifyVlan"] = 'True'
|
||||||
|
cls.services["shared_network_offering"]["specifyIpRanges"] = 'True'
|
||||||
|
|
||||||
|
cls.network_offering = NetworkOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["shared_network_offering"]
|
||||||
|
)
|
||||||
|
cls.network_offering.update(cls.apiclient, state='Enabled')
|
||||||
|
|
||||||
|
cls.account_network = Network.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["network2"],
|
||||||
|
networkofferingid=cls.network_offering.id,
|
||||||
|
zoneid=cls.zone.id,
|
||||||
|
accountid=cls.account.name,
|
||||||
|
domainid=cls.account.domainid
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
cls.network_offering = NetworkOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["isolated_network_offering"],
|
||||||
|
)
|
||||||
|
# Enable Network offering
|
||||||
|
cls.network_offering.update(cls.apiclient, state='Enabled')
|
||||||
|
|
||||||
|
# Create account network
|
||||||
|
cls.services["network"]["zoneid"] = cls.zone.id
|
||||||
|
cls.services["network"]["networkoffering"] = cls.network_offering.id
|
||||||
|
cls.account_network = Network.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["network"],
|
||||||
|
cls.account.name,
|
||||||
|
cls.account.domainid
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create small service offering
|
||||||
|
cls.service_offering = ServiceOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["service_offerings"]["small"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create disk offering
|
||||||
|
cls.disk_offering = DiskOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["disk_offering"],
|
||||||
|
)
|
||||||
|
|
||||||
|
cls._cleanup.append(cls.disk_offering)
|
||||||
|
cls._cleanup.append(cls.service_offering)
|
||||||
|
cls._cleanup.append(cls.account);
|
||||||
|
cls._cleanup.append(cls.network_offering)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(self):
|
||||||
|
try:
|
||||||
|
cleanup_resources(self.apiclient, self._cleanup)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||||
|
return
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.apiclient = self.testClient.getApiClient()
|
||||||
|
self.cleanup = []
|
||||||
|
return
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
try:
|
||||||
|
cleanup_resources(self.apiclient, self.cleanup)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||||
|
return
|
||||||
|
|
||||||
|
def verify_resource_count_primary_storage(self, expectedCount, volumeTotal):
|
||||||
|
response = matchResourceCount(
|
||||||
|
self.apiclient, expectedCount,
|
||||||
|
RESOURCE_PRIMARY_STORAGE,
|
||||||
|
accountid=self.account.id)
|
||||||
|
self.assertEqual(response[0], PASS, response[1])
|
||||||
|
|
||||||
|
result = isAccountResourceCountEqualToExpectedCount(
|
||||||
|
self.apiclient, self.account.domainid, self.account.name,
|
||||||
|
expectedCount, RESOURCE_PRIMARY_STORAGE)
|
||||||
|
self.assertFalse(result[0], result[1])
|
||||||
|
self.assertTrue(result[2], "Resource count of primary storage does not match")
|
||||||
|
|
||||||
|
response = matchResourceCount(
|
||||||
|
self.apiclient, volumeTotal,
|
||||||
|
RESOURCE_VOLUME,
|
||||||
|
accountid=self.account.id)
|
||||||
|
self.assertEqual(response[0], PASS, response[1])
|
||||||
|
|
||||||
|
result = isAccountResourceCountEqualToExpectedCount(
|
||||||
|
self.apiclient, self.account.domainid, self.account.name,
|
||||||
|
volumeTotal, RESOURCE_VOLUME)
|
||||||
|
self.assertFalse(result[0], result[1])
|
||||||
|
self.assertTrue(result[2], "Resource count of volume does not match")
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
|
||||||
|
def test_01_create_vm_with_data_disk(self):
|
||||||
|
"""Create VM with DATA disk, then destroy it (expunge=False) and expunge it
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
# 1. create vm with root disk and data disk
|
||||||
|
# 2. destroy vm, resource count of primary storage is not changed
|
||||||
|
# 3. expunge vm, resource count of primary storage decreased with size of root disk.
|
||||||
|
# 4. delete volume (data disk), resource count of primary storage decreased with size of data disk
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
virtual_machine_1 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
diskofferingid=self.disk_offering.id,
|
||||||
|
templateid=self.template.id,
|
||||||
|
zoneid=self.zone.id
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail("Exception while deploying virtual machine: %s" % e)
|
||||||
|
|
||||||
|
self.expectedCount = self.expectedCount + self.templatesize + self.disk_offering.disksize
|
||||||
|
self.volumeTotal = self.volumeTotal + 2
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
root_volumes_list = Volume.list(
|
||||||
|
self.apiclient,
|
||||||
|
virtualmachineid=virtual_machine_1.id,
|
||||||
|
type='ROOT',
|
||||||
|
listall=True
|
||||||
|
)
|
||||||
|
status = validateList(root_volumes_list)
|
||||||
|
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
|
||||||
|
root_volume_id = root_volumes_list[0].id
|
||||||
|
|
||||||
|
data_volumes_list = Volume.list(
|
||||||
|
self.apiclient,
|
||||||
|
virtualmachineid=virtual_machine_1.id,
|
||||||
|
type='DATADISK',
|
||||||
|
listall=True
|
||||||
|
)
|
||||||
|
status = validateList(data_volumes_list)
|
||||||
|
self.assertEqual(status[0], PASS, "DATADISK Volume List Validation Failed")
|
||||||
|
data_volume_id = data_volumes_list[0].id
|
||||||
|
|
||||||
|
# destroy vm
|
||||||
|
virtual_machine_1.delete(self.apiclient, expunge=False)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
|
||||||
|
|
||||||
|
# expunge vm
|
||||||
|
virtual_machine_1.expunge(self.apiclient)
|
||||||
|
self.expectedCount = self.expectedCount - self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
|
||||||
|
|
||||||
|
# delete datadisk
|
||||||
|
cmd = deleteVolume.deleteVolumeCmd()
|
||||||
|
cmd.id = data_volume_id
|
||||||
|
self.apiclient.deleteVolume(cmd)
|
||||||
|
self.expectedCount = self.expectedCount - self.disk_offering.disksize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
|
||||||
|
def test_02_destroy_allocated_volume(self):
|
||||||
|
"""Create volume, destroy it when expunge=false and expunge=true
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
# 1. create volume, resource count increases.
|
||||||
|
# 2. destroy volume (expunge = false), Exception happened. resource count no changes
|
||||||
|
# 3. destroy volume (expunge = True), resource count of primary storage decreased with size of volume.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create volume
|
||||||
|
volume = Volume.create(
|
||||||
|
self.apiclient, self.services["volume"],
|
||||||
|
zoneid=self.zone.id, account=self.account.name,
|
||||||
|
domainid=self.account.domainid, diskofferingid=self.disk_offering.id
|
||||||
|
)
|
||||||
|
self.expectedCount = self.expectedCount + self.disk_offering.disksize
|
||||||
|
self.volumeTotal = self.volumeTotal + 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Destroy volume (expunge=False)
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
volume.destroy(self.apiclient)
|
||||||
|
|
||||||
|
# Destroy volume (expunge=True)
|
||||||
|
volume.destroy(self.apiclient, expunge=True)
|
||||||
|
|
||||||
|
self.expectedCount = self.expectedCount - self.disk_offering.disksize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
|
||||||
|
def test_03_destroy_detached_volume(self):
|
||||||
|
"""Create volume, attach/detach it, then destroy it when expunge=false and expunge=true
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
# 1. create vm without data disk, resource count increases.
|
||||||
|
# 2. create volume, resource count increases.
|
||||||
|
# 3. attach volume to a vm. resource count no changes.
|
||||||
|
# 4. detach volume from a vm. resource count no changes.
|
||||||
|
# 5. destroy volume (expunge = false), volume is Destroy. resource count decreased with size of volume.
|
||||||
|
# 6. destroy volume (expunge = true), volume is not found. resource count no changes.
|
||||||
|
# 7. destroy vm (expunge=True). resource count decreased with size of root disk
|
||||||
|
"""
|
||||||
|
# Create vm
|
||||||
|
try:
|
||||||
|
virtual_machine_2 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
templateid=self.template.id,
|
||||||
|
zoneid=self.zone.id
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail("Exception while deploying virtual machine: %s" % e)
|
||||||
|
|
||||||
|
self.expectedCount = self.expectedCount + self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal + 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Create volume
|
||||||
|
volume = Volume.create(
|
||||||
|
self.apiclient, self.services["volume"],
|
||||||
|
zoneid=self.zone.id, account=self.account.name,
|
||||||
|
domainid=self.account.domainid, diskofferingid=self.disk_offering.id
|
||||||
|
)
|
||||||
|
self.expectedCount = self.expectedCount + self.disk_offering.disksize
|
||||||
|
self.volumeTotal = self.volumeTotal + 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Attach volume to vm
|
||||||
|
virtual_machine_2.attach_volume(self.apiclient, volume)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Detach volume from vm
|
||||||
|
virtual_machine_2.detach_volume(self.apiclient, volume)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Destroy volume (expunge=False)
|
||||||
|
volume.destroy(self.apiclient)
|
||||||
|
self.expectedCount = self.expectedCount - self.disk_offering.disksize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Destroy volume (expunge=True)
|
||||||
|
volume.destroy(self.apiclient, expunge=True)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Destroy VM (expunge=True)
|
||||||
|
virtual_machine_2.delete(self.apiclient, expunge=True)
|
||||||
|
self.expectedCount = self.expectedCount - self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
|
||||||
|
def test_04_recover_root_volume_after_restorevm(self):
|
||||||
|
"""Restore VM, recover/delete old root disk
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
# 1. create vm without data disk, resource count increases.
|
||||||
|
# 2. restore vm. resource count no changes.
|
||||||
|
# 3. check old root disk , should be Destroy state
|
||||||
|
# 4. recover old root disk. resource count increases.
|
||||||
|
# 5. delete old root disk . resource count decreases.
|
||||||
|
# 6. destroy vm (expunge=True). resource count decreased with size of root disk
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create vm
|
||||||
|
try:
|
||||||
|
virtual_machine_3 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
templateid=self.template.id,
|
||||||
|
zoneid=self.zone.id
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail("Exception while deploying virtual machine: %s" % e)
|
||||||
|
|
||||||
|
self.expectedCount = self.expectedCount + self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal + 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Get id of root disk
|
||||||
|
root_volumes_list = Volume.list(
|
||||||
|
self.apiclient,
|
||||||
|
virtualmachineid=virtual_machine_3.id,
|
||||||
|
type='ROOT',
|
||||||
|
listall=True
|
||||||
|
)
|
||||||
|
status = validateList(root_volumes_list)
|
||||||
|
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
|
||||||
|
root_volume_id = root_volumes_list[0].id
|
||||||
|
|
||||||
|
# restore vm
|
||||||
|
virtual_machine_3.restore(self.apiclient)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# check old root disk state
|
||||||
|
root_volumes_list = Volume.list(
|
||||||
|
self.apiclient,
|
||||||
|
id=root_volume_id,
|
||||||
|
listall=True
|
||||||
|
)
|
||||||
|
status = validateList(root_volumes_list)
|
||||||
|
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
|
||||||
|
root_volume = root_volumes_list[0]
|
||||||
|
self.assertEqual(root_volume['state'], 'Destroy', "ROOT volume should be Destroy after restorevm")
|
||||||
|
|
||||||
|
# recover old root disk
|
||||||
|
cmd = recoverVolume.recoverVolumeCmd()
|
||||||
|
cmd.id = root_volume.id
|
||||||
|
self.apiclient.recoverVolume(cmd)
|
||||||
|
self.expectedCount = self.expectedCount + self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal + 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# delete old root disk
|
||||||
|
cmd = deleteVolume.deleteVolumeCmd()
|
||||||
|
cmd.id = root_volume.id
|
||||||
|
self.apiclient.deleteVolume(cmd)
|
||||||
|
self.expectedCount = self.expectedCount - self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
|
||||||
|
|
||||||
|
# Destroy VM (expunge=True)
|
||||||
|
virtual_machine_3.delete(self.apiclient, expunge=True)
|
||||||
|
self.expectedCount = self.expectedCount - self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
|
||||||
|
def test_05_extract_root_volume_and_destroy_vm(self):
|
||||||
|
"""Create VM, extract root volume, then destroy vm and volume
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
# 1. create vm without data disk, resource count increases.
|
||||||
|
# 2. stop vm
|
||||||
|
# 3. extract root volume
|
||||||
|
# 4. expunge vm, root volume in Expunged state. resource count decreased with size of root disk.
|
||||||
|
# 5. destroy volume (expunge = false), Exception happened. resource count no changes
|
||||||
|
# 6. destroy volume (expunge = true). volume is not found. resource count no changes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create vm
|
||||||
|
try:
|
||||||
|
virtual_machine_4 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
templateid=self.template.id,
|
||||||
|
zoneid=self.zone.id
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail("Exception while deploying virtual machine: %s" % e)
|
||||||
|
|
||||||
|
self.expectedCount = self.expectedCount + self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal + 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# Get id of root disk
|
||||||
|
root_volumes_list = Volume.list(
|
||||||
|
self.apiclient,
|
||||||
|
virtualmachineid=virtual_machine_4.id,
|
||||||
|
type='ROOT',
|
||||||
|
listall=True
|
||||||
|
)
|
||||||
|
status = validateList(root_volumes_list)
|
||||||
|
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
|
||||||
|
root_volume_id = root_volumes_list[0].id
|
||||||
|
|
||||||
|
# Stop vm
|
||||||
|
virtual_machine_4.stop(self.apiclient)
|
||||||
|
|
||||||
|
# extract root volume
|
||||||
|
cmd = extractVolume.extractVolumeCmd()
|
||||||
|
cmd.id = root_volume_id
|
||||||
|
cmd.mode = "HTTP_DOWNLOAD"
|
||||||
|
cmd.zoneid = self.zone.id
|
||||||
|
self.apiclient.extractVolume(cmd)
|
||||||
|
|
||||||
|
# Destroy VM (expunge=True)
|
||||||
|
virtual_machine_4.delete(self.apiclient, expunge=True)
|
||||||
|
self.expectedCount = self.expectedCount - self.templatesize
|
||||||
|
self.volumeTotal = self.volumeTotal - 1
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal);
|
||||||
|
|
||||||
|
# check root disk state
|
||||||
|
root_volumes_list = Volume.list(
|
||||||
|
self.apiclient,
|
||||||
|
id=root_volume_id,
|
||||||
|
listall=True
|
||||||
|
)
|
||||||
|
status = validateList(root_volumes_list)
|
||||||
|
self.assertEqual(status[0], PASS, "ROOT Volume List Validation Failed")
|
||||||
|
root_volume = root_volumes_list[0]
|
||||||
|
self.assertEqual(root_volume['state'], 'Expunged', "ROOT volume should be Destroy after restorevm")
|
||||||
|
|
||||||
|
# delete root disk
|
||||||
|
cmd = deleteVolume.deleteVolumeCmd()
|
||||||
|
cmd.id = root_volume.id
|
||||||
|
self.apiclient.deleteVolume(cmd)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
|
||||||
|
|
||||||
|
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
|
||||||
|
def test_06_delete_network(self):
|
||||||
|
"""Delete account network, resource count should not be changed
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
# 1. Delete account network
|
||||||
|
# 2. resource count should not be changed
|
||||||
|
"""
|
||||||
|
self.account_network.delete(self.apiclient)
|
||||||
|
self.verify_resource_count_primary_storage(self.expectedCount, self.volumeTotal)
|
||||||
@ -1080,6 +1080,19 @@ class Volume:
|
|||||||
cmd.id = self.id
|
cmd.id = self.id
|
||||||
apiclient.deleteVolume(cmd)
|
apiclient.deleteVolume(cmd)
|
||||||
|
|
||||||
|
def destroy(self, apiclient, expunge=False):
|
||||||
|
"""Destroy Volume"""
|
||||||
|
cmd = destroyVolume.destroyVolumeCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
cmd.expunge = expunge
|
||||||
|
apiclient.destroyVolume(cmd)
|
||||||
|
|
||||||
|
def recover(self, apiclient):
|
||||||
|
"""Recover Volume"""
|
||||||
|
cmd = recoverVolume.recoverVolumeCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
apiclient.recoverVolume(cmd)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list(cls, apiclient, **kwargs):
|
def list(cls, apiclient, **kwargs):
|
||||||
"""List all volumes matching criteria"""
|
"""List all volumes matching criteria"""
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Destroying Instance....",
|
"label.action.destroy.instance.processing": "Destroying Instance....",
|
||||||
"label.action.destroy.systemvm": "Destroy System VM",
|
"label.action.destroy.systemvm": "Destroy System VM",
|
||||||
"label.action.destroy.systemvm.processing": "Destroying System VM....",
|
"label.action.destroy.systemvm.processing": "Destroying System VM....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Detach Disk",
|
"label.action.detach.disk": "Detach Disk",
|
||||||
"label.action.detach.disk.processing": "Detaching Disk....",
|
"label.action.detach.disk.processing": "Detaching Disk....",
|
||||||
"label.action.detach.iso": "Detach ISO",
|
"label.action.detach.iso": "Detach ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Rebooting Router....",
|
"label.action.reboot.router.processing": "Rebooting Router....",
|
||||||
"label.action.reboot.systemvm": "Reboot System VM",
|
"label.action.reboot.systemvm": "Reboot System VM",
|
||||||
"label.action.reboot.systemvm.processing": "Rebooting System VM....",
|
"label.action.reboot.systemvm.processing": "Rebooting System VM....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Recurring Snapshots",
|
"label.action.recurring.snapshot": "Recurring Snapshots",
|
||||||
"label.action.register.iso": "Register ISO",
|
"label.action.register.iso": "Register ISO",
|
||||||
"label.action.register.template": "Register Template from URL",
|
"label.action.register.template": "Register Template from URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
||||||
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
||||||
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
||||||
"message.action.disable.nexusVswitch": "Please confirm that you want to disable this nexus 1000v",
|
"message.action.disable.nexusVswitch": "Please confirm that you want to disable this nexus 1000v",
|
||||||
"message.action.disable.physical.network": "فضلا ، أكّد أنك تريد تعطيل هذه الشبكة الفيزيائية",
|
"message.action.disable.physical.network": "فضلا ، أكّد أنك تريد تعطيل هذه الشبكة الفيزيائية",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
||||||
"message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
"message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
||||||
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
||||||
"message.action.remove.host": "Please confirm that you want to remove this host.",
|
"message.action.remove.host": "Please confirm that you want to remove this host.",
|
||||||
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Destroying Instance....",
|
"label.action.destroy.instance.processing": "Destroying Instance....",
|
||||||
"label.action.destroy.systemvm": "Destroy System VM",
|
"label.action.destroy.systemvm": "Destroy System VM",
|
||||||
"label.action.destroy.systemvm.processing": "Destroying System VM....",
|
"label.action.destroy.systemvm.processing": "Destroying System VM....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Detach Disk",
|
"label.action.detach.disk": "Detach Disk",
|
||||||
"label.action.detach.disk.processing": "Detaching Disk....",
|
"label.action.detach.disk.processing": "Detaching Disk....",
|
||||||
"label.action.detach.iso": "Detach ISO",
|
"label.action.detach.iso": "Detach ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Rebooting Router....",
|
"label.action.reboot.router.processing": "Rebooting Router....",
|
||||||
"label.action.reboot.systemvm": "Reboot System VM",
|
"label.action.reboot.systemvm": "Reboot System VM",
|
||||||
"label.action.reboot.systemvm.processing": "Rebooting System VM....",
|
"label.action.reboot.systemvm.processing": "Rebooting System VM....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Recurring Snapshots",
|
"label.action.recurring.snapshot": "Recurring Snapshots",
|
||||||
"label.action.register.iso": "Register ISO",
|
"label.action.register.iso": "Register ISO",
|
||||||
"label.action.register.template": "Register Template from URL",
|
"label.action.register.template": "Register Template from URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
||||||
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
||||||
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
||||||
"message.action.disable.nexusVswitch": "Please confirm that you want to disable this nexus 1000v",
|
"message.action.disable.nexusVswitch": "Please confirm that you want to disable this nexus 1000v",
|
||||||
"message.action.disable.physical.network": "Please confirm that you want to disable this physical network.",
|
"message.action.disable.physical.network": "Please confirm that you want to disable this physical network.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
||||||
"message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
"message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
||||||
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
||||||
"message.action.remove.host": "Please confirm that you want to remove this host.",
|
"message.action.remove.host": "Please confirm that you want to remove this host.",
|
||||||
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Instanz wird zerstört....",
|
"label.action.destroy.instance.processing": "Instanz wird zerstört....",
|
||||||
"label.action.destroy.systemvm": "System-VM vernichten",
|
"label.action.destroy.systemvm": "System-VM vernichten",
|
||||||
"label.action.destroy.systemvm.processing": "System-VM wird zerstört....",
|
"label.action.destroy.systemvm.processing": "System-VM wird zerstört....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Festplatte loslösen",
|
"label.action.detach.disk": "Festplatte loslösen",
|
||||||
"label.action.detach.disk.processing": "Festplatte wird losgelöst...",
|
"label.action.detach.disk.processing": "Festplatte wird losgelöst...",
|
||||||
"label.action.detach.iso": "ISO loslösen",
|
"label.action.detach.iso": "ISO loslösen",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Router wird neu gebootet....",
|
"label.action.reboot.router.processing": "Router wird neu gebootet....",
|
||||||
"label.action.reboot.systemvm": "System-VM neu starten",
|
"label.action.reboot.systemvm": "System-VM neu starten",
|
||||||
"label.action.reboot.systemvm.processing": "System-VM wird neu gebootet....",
|
"label.action.reboot.systemvm.processing": "System-VM wird neu gebootet....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Wiederkehrende Schnappschüsse",
|
"label.action.recurring.snapshot": "Wiederkehrende Schnappschüsse",
|
||||||
"label.action.register.iso": "ISO registrieren",
|
"label.action.register.iso": "ISO registrieren",
|
||||||
"label.action.register.template": "Vorlage von URL registrieren",
|
"label.action.register.template": "Vorlage von URL registrieren",
|
||||||
@ -1849,6 +1851,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Bitte bestätigen Sie, dass Sie diese Zone löschen möchten.",
|
"message.action.delete.zone": "Bitte bestätigen Sie, dass Sie diese Zone löschen möchten.",
|
||||||
"message.action.destroy.instance": "Bitte bestätigen Sie, dass Sie diese Instanz löschen möchten.",
|
"message.action.destroy.instance": "Bitte bestätigen Sie, dass Sie diese Instanz löschen möchten.",
|
||||||
"message.action.destroy.systemvm": "Bitte bestätigen Sie, dass Sie diese System-VM zerstören möchten.",
|
"message.action.destroy.systemvm": "Bitte bestätigen Sie, dass Sie diese System-VM zerstören möchten.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Bitte bestätigen Sie, dass Sie diesen Cluster deaktivieren möchten.",
|
"message.action.disable.cluster": "Bitte bestätigen Sie, dass Sie diesen Cluster deaktivieren möchten.",
|
||||||
"message.action.disable.nexusVswitch": "Bitte bestätigen Sie, dass sie diesen nexus 1000v deaktivieren möchten.",
|
"message.action.disable.nexusVswitch": "Bitte bestätigen Sie, dass sie diesen nexus 1000v deaktivieren möchten.",
|
||||||
"message.action.disable.physical.network": "Bitte bestätigen Sie, dass Sie dieses physikalische Netzwerk deaktivieren möchten.",
|
"message.action.disable.physical.network": "Bitte bestätigen Sie, dass Sie dieses physikalische Netzwerk deaktivieren möchten.",
|
||||||
@ -1873,6 +1876,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Bitte bestätigen Sie, dass Sie diese Instanz neu starten möchten.",
|
"message.action.reboot.instance": "Bitte bestätigen Sie, dass Sie diese Instanz neu starten möchten.",
|
||||||
"message.action.reboot.router": "Alle angebotenen Dienste dieses Routers werden unterbrochen. Bitte bestätigen Sie, dass Sie den Router neu starten möchten.",
|
"message.action.reboot.router": "Alle angebotenen Dienste dieses Routers werden unterbrochen. Bitte bestätigen Sie, dass Sie den Router neu starten möchten.",
|
||||||
"message.action.reboot.systemvm": "Bitte bestätigen Sie, dass Sie diese System-VM neu starten möchten.",
|
"message.action.reboot.systemvm": "Bitte bestätigen Sie, dass Sie diese System-VM neu starten möchten.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Bitte bestätigen Sie, dass Sie diese IP freigeben möchten.",
|
"message.action.release.ip": "Bitte bestätigen Sie, dass Sie diese IP freigeben möchten.",
|
||||||
"message.action.remove.host": "Bitte bestätigen Sie, dass Sie diesen Host entfernen möchten.",
|
"message.action.remove.host": "Bitte bestätigen Sie, dass Sie diesen Host entfernen möchten.",
|
||||||
"message.action.reset.password.off": "Ihre Instanz unterschützt derzeitig nicht dieses Feature.",
|
"message.action.reset.password.off": "Ihre Instanz unterschützt derzeitig nicht dieses Feature.",
|
||||||
|
|||||||
@ -182,6 +182,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing":"Destroying Instance....",
|
"label.action.destroy.instance.processing":"Destroying Instance....",
|
||||||
"label.action.destroy.systemvm":"Destroy System VM",
|
"label.action.destroy.systemvm":"Destroy System VM",
|
||||||
"label.action.destroy.systemvm.processing":"Destroying System VM....",
|
"label.action.destroy.systemvm.processing":"Destroying System VM....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk":"Detach Disk",
|
"label.action.detach.disk":"Detach Disk",
|
||||||
"label.action.detach.disk.processing":"Detaching Disk....",
|
"label.action.detach.disk.processing":"Detaching Disk....",
|
||||||
"label.action.detach.iso":"Detach ISO",
|
"label.action.detach.iso":"Detach ISO",
|
||||||
@ -261,6 +262,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing":"Rebooting Router....",
|
"label.action.reboot.router.processing":"Rebooting Router....",
|
||||||
"label.action.reboot.systemvm":"Reboot System VM",
|
"label.action.reboot.systemvm":"Reboot System VM",
|
||||||
"label.action.reboot.systemvm.processing":"Rebooting System VM....",
|
"label.action.reboot.systemvm.processing":"Rebooting System VM....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot":"Recurring Snapshots",
|
"label.action.recurring.snapshot":"Recurring Snapshots",
|
||||||
"label.action.register.iso":"Register ISO",
|
"label.action.register.iso":"Register ISO",
|
||||||
"label.action.register.ncc":"Register NCC",
|
"label.action.register.ncc":"Register NCC",
|
||||||
@ -1945,6 +1947,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone":"Please confirm that you want to delete this zone.",
|
"message.action.delete.zone":"Please confirm that you want to delete this zone.",
|
||||||
"message.action.destroy.instance":"Please confirm that you want to destroy this instance.",
|
"message.action.destroy.instance":"Please confirm that you want to destroy this instance.",
|
||||||
"message.action.destroy.systemvm":"Please confirm that you want to destroy this System VM.",
|
"message.action.destroy.systemvm":"Please confirm that you want to destroy this System VM.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster":"Please confirm that you want to disable this cluster.",
|
"message.action.disable.cluster":"Please confirm that you want to disable this cluster.",
|
||||||
"message.action.disable.nexusVswitch":"Please confirm that you want to disable this nexus 1000v",
|
"message.action.disable.nexusVswitch":"Please confirm that you want to disable this nexus 1000v",
|
||||||
"message.action.disable.physical.network":"Please confirm that you want to disable this physical network.",
|
"message.action.disable.physical.network":"Please confirm that you want to disable this physical network.",
|
||||||
@ -1969,6 +1972,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance":"Please confirm that you want to reboot this instance.",
|
"message.action.reboot.instance":"Please confirm that you want to reboot this instance.",
|
||||||
"message.action.reboot.router":"All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
"message.action.reboot.router":"All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
||||||
"message.action.reboot.systemvm":"Please confirm that you want to reboot this system VM.",
|
"message.action.reboot.systemvm":"Please confirm that you want to reboot this system VM.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip":"Please confirm that you want to release this IP.",
|
"message.action.release.ip":"Please confirm that you want to release this IP.",
|
||||||
"message.action.remove.host":"Please confirm that you want to remove this host.",
|
"message.action.remove.host":"Please confirm that you want to remove this host.",
|
||||||
"message.action.reset.password.off":"Your instance currently does not support this feature.",
|
"message.action.reset.password.off":"Your instance currently does not support this feature.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Destruyendo Instancia ....",
|
"label.action.destroy.instance.processing": "Destruyendo Instancia ....",
|
||||||
"label.action.destroy.systemvm": "Destruye MV de Sistema",
|
"label.action.destroy.systemvm": "Destruye MV de Sistema",
|
||||||
"label.action.destroy.systemvm.processing": "Destruyendo MV de Sistema...",
|
"label.action.destroy.systemvm.processing": "Destruyendo MV de Sistema...",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Desconectar Disco",
|
"label.action.detach.disk": "Desconectar Disco",
|
||||||
"label.action.detach.disk.processing": "Desconectando Disco ....",
|
"label.action.detach.disk.processing": "Desconectando Disco ....",
|
||||||
"label.action.detach.iso": "Desconectar ISO",
|
"label.action.detach.iso": "Desconectar ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Reiniciando Router ....",
|
"label.action.reboot.router.processing": "Reiniciando Router ....",
|
||||||
"label.action.reboot.systemvm": "Reiniciar MV de Sistema",
|
"label.action.reboot.systemvm": "Reiniciar MV de Sistema",
|
||||||
"label.action.reboot.systemvm.processing": "Reinicando MV de Sistema...",
|
"label.action.reboot.systemvm.processing": "Reinicando MV de Sistema...",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Instantáneas Recurrentes",
|
"label.action.recurring.snapshot": "Instantáneas Recurrentes",
|
||||||
"label.action.register.iso": "Registrar ISO",
|
"label.action.register.iso": "Registrar ISO",
|
||||||
"label.action.register.template": "Registrar Plantilla desde una URL",
|
"label.action.register.template": "Registrar Plantilla desde una URL",
|
||||||
@ -1848,6 +1850,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Por favor, confirme que desea eliminar esta Zona. ",
|
"message.action.delete.zone": "Por favor, confirme que desea eliminar esta Zona. ",
|
||||||
"message.action.destroy.instance": "Por favor, confirme que desea destruir esta Instancia.",
|
"message.action.destroy.instance": "Por favor, confirme que desea destruir esta Instancia.",
|
||||||
"message.action.destroy.systemvm": "Por favor, confirme que desea destruir esta MV de Sistema.",
|
"message.action.destroy.systemvm": "Por favor, confirme que desea destruir esta MV de Sistema.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Por favor, confirme que desea deshabilitar este clúster.",
|
"message.action.disable.cluster": "Por favor, confirme que desea deshabilitar este clúster.",
|
||||||
"message.action.disable.nexusVswitch": "Por favor confirme que usted quiere deshabilitar este nexus 1000v",
|
"message.action.disable.nexusVswitch": "Por favor confirme que usted quiere deshabilitar este nexus 1000v",
|
||||||
"message.action.disable.physical.network": "Por favor confirmar que usted quiere deshabilitar esta red física",
|
"message.action.disable.physical.network": "Por favor confirmar que usted quiere deshabilitar esta red física",
|
||||||
@ -1872,6 +1875,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Por favor, confirme que desea reiniciar esta Instancia.",
|
"message.action.reboot.instance": "Por favor, confirme que desea reiniciar esta Instancia.",
|
||||||
"message.action.reboot.router": "Todos los servicios provistos por este router virtual serán interrumpidos. Por favor confirmar que desea reiniciarlo.",
|
"message.action.reboot.router": "Todos los servicios provistos por este router virtual serán interrumpidos. Por favor confirmar que desea reiniciarlo.",
|
||||||
"message.action.reboot.systemvm": "Por favor, confirme que desea reiniciar esta MV de Sistema.",
|
"message.action.reboot.systemvm": "Por favor, confirme que desea reiniciar esta MV de Sistema.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Por favor, confirme que desea liberar esta IP ",
|
"message.action.release.ip": "Por favor, confirme que desea liberar esta IP ",
|
||||||
"message.action.remove.host": "Por favor confirme que desea borrar este anfitrión.",
|
"message.action.remove.host": "Por favor confirme que desea borrar este anfitrión.",
|
||||||
"message.action.reset.password.off": "Su instancia no soporta esta característica actualmente.",
|
"message.action.reset.password.off": "Su instancia no soporta esta característica actualmente.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Suppression de l'instance...",
|
"label.action.destroy.instance.processing": "Suppression de l'instance...",
|
||||||
"label.action.destroy.systemvm": "Supprimer VM Système",
|
"label.action.destroy.systemvm": "Supprimer VM Système",
|
||||||
"label.action.destroy.systemvm.processing": "Suppression de la VM Système...",
|
"label.action.destroy.systemvm.processing": "Suppression de la VM Système...",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Détacher le disque",
|
"label.action.detach.disk": "Détacher le disque",
|
||||||
"label.action.detach.disk.processing": "Détachement du disque...",
|
"label.action.detach.disk.processing": "Détachement du disque...",
|
||||||
"label.action.detach.iso": "Détacher l'image ISO",
|
"label.action.detach.iso": "Détacher l'image ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Redémarrage du routeur...",
|
"label.action.reboot.router.processing": "Redémarrage du routeur...",
|
||||||
"label.action.reboot.systemvm": "Redémarrer VM Système",
|
"label.action.reboot.systemvm": "Redémarrer VM Système",
|
||||||
"label.action.reboot.systemvm.processing": "Redémarrage de la VM Système...",
|
"label.action.reboot.systemvm.processing": "Redémarrage de la VM Système...",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Instantanés récurrents",
|
"label.action.recurring.snapshot": "Instantanés récurrents",
|
||||||
"label.action.register.iso": "Enregistrer ISO",
|
"label.action.register.iso": "Enregistrer ISO",
|
||||||
"label.action.register.template": "Enregistrer modèle depuis une URL",
|
"label.action.register.template": "Enregistrer modèle depuis une URL",
|
||||||
@ -1849,6 +1851,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Supprimer cette zone ?",
|
"message.action.delete.zone": "Supprimer cette zone ?",
|
||||||
"message.action.destroy.instance": "Supprimer cette instance ?",
|
"message.action.destroy.instance": "Supprimer cette instance ?",
|
||||||
"message.action.destroy.systemvm": "Supprimer cette VM Système ?",
|
"message.action.destroy.systemvm": "Supprimer cette VM Système ?",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Désactiver ce cluster ?",
|
"message.action.disable.cluster": "Désactiver ce cluster ?",
|
||||||
"message.action.disable.nexusVswitch": "Confirmer la désactivation de ce Nexus 1000v",
|
"message.action.disable.nexusVswitch": "Confirmer la désactivation de ce Nexus 1000v",
|
||||||
"message.action.disable.physical.network": "Confirmer l'activation de ce réseau physique.",
|
"message.action.disable.physical.network": "Confirmer l'activation de ce réseau physique.",
|
||||||
@ -1873,6 +1876,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Redémarrer cette instance ?",
|
"message.action.reboot.instance": "Redémarrer cette instance ?",
|
||||||
"message.action.reboot.router": "Tous les services fournit par ce routeur virtuel vont être interrompus. Confirmer le ré-amorçage de ce routeur.",
|
"message.action.reboot.router": "Tous les services fournit par ce routeur virtuel vont être interrompus. Confirmer le ré-amorçage de ce routeur.",
|
||||||
"message.action.reboot.systemvm": "Redémarrer cette VM Système ?",
|
"message.action.reboot.systemvm": "Redémarrer cette VM Système ?",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Libérer cette adresse IP ?",
|
"message.action.release.ip": "Libérer cette adresse IP ?",
|
||||||
"message.action.remove.host": "Êtes-vous sûr que vous voulez supprimer cet hôte.",
|
"message.action.remove.host": "Êtes-vous sûr que vous voulez supprimer cet hôte.",
|
||||||
"message.action.reset.password.off": "Votre instance ne supporte pas pour le moment cette fonctionnalité.",
|
"message.action.reset.password.off": "Votre instance ne supporte pas pour le moment cette fonctionnalité.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Példány elpusztítása...",
|
"label.action.destroy.instance.processing": "Példány elpusztítása...",
|
||||||
"label.action.destroy.systemvm": "Rendszer VM elpusztítása",
|
"label.action.destroy.systemvm": "Rendszer VM elpusztítása",
|
||||||
"label.action.destroy.systemvm.processing": "Rendszer VM elpusztítása...",
|
"label.action.destroy.systemvm.processing": "Rendszer VM elpusztítása...",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Merevlemez leválasztása",
|
"label.action.detach.disk": "Merevlemez leválasztása",
|
||||||
"label.action.detach.disk.processing": "Merevlemez leválasztása...",
|
"label.action.detach.disk.processing": "Merevlemez leválasztása...",
|
||||||
"label.action.detach.iso": "ISO leválasztása",
|
"label.action.detach.iso": "ISO leválasztása",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Router újraindítása...",
|
"label.action.reboot.router.processing": "Router újraindítása...",
|
||||||
"label.action.reboot.systemvm": "Rendszer VM újraindítása",
|
"label.action.reboot.systemvm": "Rendszer VM újraindítása",
|
||||||
"label.action.reboot.systemvm.processing": "Rendszer VM újraindítása",
|
"label.action.reboot.systemvm.processing": "Rendszer VM újraindítása",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Ismétlődő pillanatfelvételek",
|
"label.action.recurring.snapshot": "Ismétlődő pillanatfelvételek",
|
||||||
"label.action.register.iso": "ISO regisztrációja",
|
"label.action.register.iso": "ISO regisztrációja",
|
||||||
"label.action.register.template": "Sablon regisztrációja URL-ről",
|
"label.action.register.template": "Sablon regisztrációja URL-ről",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Erősítsd meg, hogy törölni akarod ezt a zónát!",
|
"message.action.delete.zone": "Erősítsd meg, hogy törölni akarod ezt a zónát!",
|
||||||
"message.action.destroy.instance": "Erősítsd meg, hogy el akarod pusztítani ezt a példányt!",
|
"message.action.destroy.instance": "Erősítsd meg, hogy el akarod pusztítani ezt a példányt!",
|
||||||
"message.action.destroy.systemvm": "Erősítsd meg, hogy el akarod pusztítani ezt a rendszer VM-et!",
|
"message.action.destroy.systemvm": "Erősítsd meg, hogy el akarod pusztítani ezt a rendszer VM-et!",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Erősítsd meg, hogy ki akarod kapcsolni ezt a fürtöt!",
|
"message.action.disable.cluster": "Erősítsd meg, hogy ki akarod kapcsolni ezt a fürtöt!",
|
||||||
"message.action.disable.nexusVswitch": "Erősítsd meg, hogy ki akarod kapcsolni ezt a nexus 1000v-t!",
|
"message.action.disable.nexusVswitch": "Erősítsd meg, hogy ki akarod kapcsolni ezt a nexus 1000v-t!",
|
||||||
"message.action.disable.physical.network": "Erősítsd meg, hogy ki akarod kapcsolni ezt a fizikai hálózatot!",
|
"message.action.disable.physical.network": "Erősítsd meg, hogy ki akarod kapcsolni ezt a fizikai hálózatot!",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Erősítsd meg, hogy újra akarod indítani ezt a példányt!",
|
"message.action.reboot.instance": "Erősítsd meg, hogy újra akarod indítani ezt a példányt!",
|
||||||
"message.action.reboot.router": "Minden a router által nyújtott szolgáltatás megszakad. Erősítsd meg, hogy újra akarod indítani a routert!",
|
"message.action.reboot.router": "Minden a router által nyújtott szolgáltatás megszakad. Erősítsd meg, hogy újra akarod indítani a routert!",
|
||||||
"message.action.reboot.systemvm": "Erősítsd meg, hogy újra akarod indítani ezt a rendszer VM-et!",
|
"message.action.reboot.systemvm": "Erősítsd meg, hogy újra akarod indítani ezt a rendszer VM-et!",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Erősítsd meg, hogy el akarod engedni ezt az IP címet!",
|
"message.action.release.ip": "Erősítsd meg, hogy el akarod engedni ezt az IP címet!",
|
||||||
"message.action.remove.host": "Erősítsd meg, hogy törölni akarod ezt a kiszolgálót!",
|
"message.action.remove.host": "Erősítsd meg, hogy törölni akarod ezt a kiszolgálót!",
|
||||||
"message.action.reset.password.off": "A példány nem támogatja ezt a lehetőséget.",
|
"message.action.reset.password.off": "A példány nem támogatja ezt a lehetőséget.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Rimozione Instanza in corso....",
|
"label.action.destroy.instance.processing": "Rimozione Instanza in corso....",
|
||||||
"label.action.destroy.systemvm": "Rimozione VM di sistema",
|
"label.action.destroy.systemvm": "Rimozione VM di sistema",
|
||||||
"label.action.destroy.systemvm.processing": "Rimozione VM di Sistema in corso....",
|
"label.action.destroy.systemvm.processing": "Rimozione VM di Sistema in corso....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Scollegamento di un Disco",
|
"label.action.detach.disk": "Scollegamento di un Disco",
|
||||||
"label.action.detach.disk.processing": "Scollegamento Disco in corso....",
|
"label.action.detach.disk.processing": "Scollegamento Disco in corso....",
|
||||||
"label.action.detach.iso": "Scollegamento immagine ISO",
|
"label.action.detach.iso": "Scollegamento immagine ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Riavvio Router in corso....",
|
"label.action.reboot.router.processing": "Riavvio Router in corso....",
|
||||||
"label.action.reboot.systemvm": "Riavvio VM di Sistema",
|
"label.action.reboot.systemvm": "Riavvio VM di Sistema",
|
||||||
"label.action.reboot.systemvm.processing": "Riavvio VM di Sistema in corso....",
|
"label.action.reboot.systemvm.processing": "Riavvio VM di Sistema in corso....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Snapshot Ricorrenti",
|
"label.action.recurring.snapshot": "Snapshot Ricorrenti",
|
||||||
"label.action.register.iso": "Registrare una ISO",
|
"label.action.register.iso": "Registrare una ISO",
|
||||||
"label.action.register.template": "Registra un Template da URL",
|
"label.action.register.template": "Registra un Template da URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
||||||
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
||||||
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
||||||
"message.action.disable.nexusVswitch": "Si prega di confermare di voler disabilitare questo nexus 1000v",
|
"message.action.disable.nexusVswitch": "Si prega di confermare di voler disabilitare questo nexus 1000v",
|
||||||
"message.action.disable.physical.network": "Si prega di confermare di voler disabilitare questa rete fisica.",
|
"message.action.disable.physical.network": "Si prega di confermare di voler disabilitare questa rete fisica.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
||||||
"message.action.reboot.router": "Tutti i servizi forniti da questo router virtuale saranno interrotti. Si prega di confermare di voler riavviare questo router.",
|
"message.action.reboot.router": "Tutti i servizi forniti da questo router virtuale saranno interrotti. Si prega di confermare di voler riavviare questo router.",
|
||||||
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
||||||
"message.action.remove.host": "Si prega di confermare di voler rimuovere questo host.",
|
"message.action.remove.host": "Si prega di confermare di voler rimuovere questo host.",
|
||||||
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "インスタンスを破棄しています...",
|
"label.action.destroy.instance.processing": "インスタンスを破棄しています...",
|
||||||
"label.action.destroy.systemvm": "システム VM の破棄",
|
"label.action.destroy.systemvm": "システム VM の破棄",
|
||||||
"label.action.destroy.systemvm.processing": "システム VM を破棄しています...",
|
"label.action.destroy.systemvm.processing": "システム VM を破棄しています...",
|
||||||
|
"label.action.destroy.volume":"ボリュームの破棄",
|
||||||
"label.action.detach.disk": "ディスクのデタッチ",
|
"label.action.detach.disk": "ディスクのデタッチ",
|
||||||
"label.action.detach.disk.processing": "ディスクをデタッチしています...",
|
"label.action.detach.disk.processing": "ディスクをデタッチしています...",
|
||||||
"label.action.detach.iso": "ISO のデタッチ",
|
"label.action.detach.iso": "ISO のデタッチ",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "ルーターを再起動しています...",
|
"label.action.reboot.router.processing": "ルーターを再起動しています...",
|
||||||
"label.action.reboot.systemvm": "システム VM の再起動",
|
"label.action.reboot.systemvm": "システム VM の再起動",
|
||||||
"label.action.reboot.systemvm.processing": "システム VM を再起動しています...",
|
"label.action.reboot.systemvm.processing": "システム VM を再起動しています...",
|
||||||
|
"label.action.recover.volume":"ボリュームの復元",
|
||||||
"label.action.recurring.snapshot": "定期スナップショット",
|
"label.action.recurring.snapshot": "定期スナップショット",
|
||||||
"label.action.register.iso": "ISO の登録",
|
"label.action.register.iso": "ISO の登録",
|
||||||
"label.action.register.template": "URL からのテンプレートの登録",
|
"label.action.register.template": "URL からのテンプレートの登録",
|
||||||
@ -1849,6 +1851,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "このゾーンを削除してもよろしいですか?",
|
"message.action.delete.zone": "このゾーンを削除してもよろしいですか?",
|
||||||
"message.action.destroy.instance": "このインスタンスを破棄してもよろしいですか?",
|
"message.action.destroy.instance": "このインスタンスを破棄してもよろしいですか?",
|
||||||
"message.action.destroy.systemvm": "このシステム VM を破棄してもよろしいですか?",
|
"message.action.destroy.systemvm": "このシステム VM を破棄してもよろしいですか?",
|
||||||
|
"message.action.destroy.volume":"このボリュームを破棄してもよろしいですか?",
|
||||||
"message.action.disable.cluster": "このクラスターを無効にしてもよろしいですか?",
|
"message.action.disable.cluster": "このクラスターを無効にしてもよろしいですか?",
|
||||||
"message.action.disable.nexusVswitch": "この Nexus 1000V を無効にしてもよろしいですか?",
|
"message.action.disable.nexusVswitch": "この Nexus 1000V を無効にしてもよろしいですか?",
|
||||||
"message.action.disable.physical.network": "この物理ネットワークを無効にしてもよろしいですか?",
|
"message.action.disable.physical.network": "この物理ネットワークを無効にしてもよろしいですか?",
|
||||||
@ -1873,6 +1876,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "このインスタンスを再起動してもよろしいですか?",
|
"message.action.reboot.instance": "このインスタンスを再起動してもよろしいですか?",
|
||||||
"message.action.reboot.router": "この仮想ルーターで提供するすべてのサービスが中断されます。このルーターを再起動してもよろしいですか?",
|
"message.action.reboot.router": "この仮想ルーターで提供するすべてのサービスが中断されます。このルーターを再起動してもよろしいですか?",
|
||||||
"message.action.reboot.systemvm": "このシステム VM を再起動してもよろしいですか?",
|
"message.action.reboot.systemvm": "このシステム VM を再起動してもよろしいですか?",
|
||||||
|
"message.action.recover.volume":"このボリュームを復元してもよろしいですか?",
|
||||||
"message.action.release.ip": "この IP アドレスを解放してもよろしいですか?",
|
"message.action.release.ip": "この IP アドレスを解放してもよろしいですか?",
|
||||||
"message.action.remove.host": "このホストを削除してもよろしいですか?",
|
"message.action.remove.host": "このホストを削除してもよろしいですか?",
|
||||||
"message.action.reset.password.off": "インスタンスは現在この機能をサポートしていません。",
|
"message.action.reset.password.off": "インスタンスは現在この機能をサポートしていません。",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "인스턴스를 파기하는 중...",
|
"label.action.destroy.instance.processing": "인스턴스를 파기하는 중...",
|
||||||
"label.action.destroy.systemvm": "시스템 VM 파기",
|
"label.action.destroy.systemvm": "시스템 VM 파기",
|
||||||
"label.action.destroy.systemvm.processing": "시스템 VM를 파기하는 중...",
|
"label.action.destroy.systemvm.processing": "시스템 VM를 파기하는 중...",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "디스크 분리",
|
"label.action.detach.disk": "디스크 분리",
|
||||||
"label.action.detach.disk.processing": "디스크를 분리 하는 중...",
|
"label.action.detach.disk.processing": "디스크를 분리 하는 중...",
|
||||||
"label.action.detach.iso": "ISO 분리",
|
"label.action.detach.iso": "ISO 분리",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "라우터를 재시작하는 중...",
|
"label.action.reboot.router.processing": "라우터를 재시작하는 중...",
|
||||||
"label.action.reboot.systemvm": "시스템 VM 재시작",
|
"label.action.reboot.systemvm": "시스템 VM 재시작",
|
||||||
"label.action.reboot.systemvm.processing": "시스템 VM를 재시작하는 중...",
|
"label.action.reboot.systemvm.processing": "시스템 VM를 재시작하는 중...",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "정기 스냅샷",
|
"label.action.recurring.snapshot": "정기 스냅샷",
|
||||||
"label.action.register.iso": "ISO 등록",
|
"label.action.register.iso": "ISO 등록",
|
||||||
"label.action.register.template": "Register Template from URL",
|
"label.action.register.template": "Register Template from URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "현재 Zone을 삭제하시겠습니까?",
|
"message.action.delete.zone": "현재 Zone을 삭제하시겠습니까?",
|
||||||
"message.action.destroy.instance": "현재 인스턴스를 파기하시겠습니까?",
|
"message.action.destroy.instance": "현재 인스턴스를 파기하시겠습니까?",
|
||||||
"message.action.destroy.systemvm": "현재 시스템 VM를 파기하시겠습니까?",
|
"message.action.destroy.systemvm": "현재 시스템 VM를 파기하시겠습니까?",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "현재 클러스터를 사용 안 함으로 하시겠습니까?",
|
"message.action.disable.cluster": "현재 클러스터를 사용 안 함으로 하시겠습니까?",
|
||||||
"message.action.disable.nexusVswitch": "현재 Nexus 1000V를 사용 안 함으로 하시겠습니까?",
|
"message.action.disable.nexusVswitch": "현재 Nexus 1000V를 사용 안 함으로 하시겠습니까?",
|
||||||
"message.action.disable.physical.network": "현재 물리 네트워크를 사용 안 함으로 하시겠습니까?",
|
"message.action.disable.physical.network": "현재 물리 네트워크를 사용 안 함으로 하시겠습니까?",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "현재 인스턴스를 재시작하시겠습니까?",
|
"message.action.reboot.instance": "현재 인스턴스를 재시작하시겠습니까?",
|
||||||
"message.action.reboot.router": "현재 가상 라우터로 제공하는 모든 서비스가 중단됩니다. 이 라우터를 재시작하시겠습니까?",
|
"message.action.reboot.router": "현재 가상 라우터로 제공하는 모든 서비스가 중단됩니다. 이 라우터를 재시작하시겠습니까?",
|
||||||
"message.action.reboot.systemvm": "현재 시스템 VM을 재시작하시겠습니까?",
|
"message.action.reboot.systemvm": "현재 시스템 VM을 재시작하시겠습니까?",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "현재 IP 주소를 해제하시겠습니까?",
|
"message.action.release.ip": "현재 IP 주소를 해제하시겠습니까?",
|
||||||
"message.action.remove.host": "현재 호스트를 삭제하시겠습니까?",
|
"message.action.remove.host": "현재 호스트를 삭제하시겠습니까?",
|
||||||
"message.action.reset.password.off": "인스턴스는 현재 기능을 지원 하지 않습니다.",
|
"message.action.reset.password.off": "인스턴스는 현재 기능을 지원 하지 않습니다.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Ødelegger instans....",
|
"label.action.destroy.instance.processing": "Ødelegger instans....",
|
||||||
"label.action.destroy.systemvm": "Slett system VM",
|
"label.action.destroy.systemvm": "Slett system VM",
|
||||||
"label.action.destroy.systemvm.processing": "Sletter system VM....",
|
"label.action.destroy.systemvm.processing": "Sletter system VM....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Frakoble disk",
|
"label.action.detach.disk": "Frakoble disk",
|
||||||
"label.action.detach.disk.processing": "Kobler fra disk....",
|
"label.action.detach.disk.processing": "Kobler fra disk....",
|
||||||
"label.action.detach.iso": "Frakoble ISO",
|
"label.action.detach.iso": "Frakoble ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Omstaer Instans....",
|
"label.action.reboot.router.processing": "Omstaer Instans....",
|
||||||
"label.action.reboot.systemvm": "Omstart System VM",
|
"label.action.reboot.systemvm": "Omstart System VM",
|
||||||
"label.action.reboot.systemvm.processing": "Omstarter System VM",
|
"label.action.reboot.systemvm.processing": "Omstarter System VM",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Gjentagende øyeblikksbilder",
|
"label.action.recurring.snapshot": "Gjentagende øyeblikksbilder",
|
||||||
"label.action.register.iso": "Registrer ISO",
|
"label.action.register.iso": "Registrer ISO",
|
||||||
"label.action.register.template": "Registrer mal fra en URL",
|
"label.action.register.template": "Registrer mal fra en URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Vennligst bekreft at du ønsker å slette denne sone.",
|
"message.action.delete.zone": "Vennligst bekreft at du ønsker å slette denne sone.",
|
||||||
"message.action.destroy.instance": "Vennligst bekreft at du ønsker å fjerne denne instansen.",
|
"message.action.destroy.instance": "Vennligst bekreft at du ønsker å fjerne denne instansen.",
|
||||||
"message.action.destroy.systemvm": "Vennligst bekreft at du ønsker å ødelegge denne System VM.",
|
"message.action.destroy.systemvm": "Vennligst bekreft at du ønsker å ødelegge denne System VM.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Vennligst bekreft at du ønsker å detaktivere denne klyngen.",
|
"message.action.disable.cluster": "Vennligst bekreft at du ønsker å detaktivere denne klyngen.",
|
||||||
"message.action.disable.nexusVswitch": "Vennligst bekreft at du ønsker å deaktivere denne nexus 1000v",
|
"message.action.disable.nexusVswitch": "Vennligst bekreft at du ønsker å deaktivere denne nexus 1000v",
|
||||||
"message.action.disable.physical.network": "Vennligst bekreft at du ønsker å deaktivere dette fysiske nettverket.",
|
"message.action.disable.physical.network": "Vennligst bekreft at du ønsker å deaktivere dette fysiske nettverket.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Vennligst bekreft at du vill restarte denne instansen.",
|
"message.action.reboot.instance": "Vennligst bekreft at du vill restarte denne instansen.",
|
||||||
"message.action.reboot.router": "Alle tjenester levert fra denne virtuelle ruter vil bli avbrutt. Vennligst bekreft at du ønsker å restarte denne ruteren.",
|
"message.action.reboot.router": "Alle tjenester levert fra denne virtuelle ruter vil bli avbrutt. Vennligst bekreft at du ønsker å restarte denne ruteren.",
|
||||||
"message.action.reboot.systemvm": "Vennligst bekreft at du vil restarte denne system VM",
|
"message.action.reboot.systemvm": "Vennligst bekreft at du vil restarte denne system VM",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Vennligst bekreft at du ønsker å frigi denne IP.",
|
"message.action.release.ip": "Vennligst bekreft at du ønsker å frigi denne IP.",
|
||||||
"message.action.remove.host": "Vennligst bekreft at du vil gjerne denne tjeneren.",
|
"message.action.remove.host": "Vennligst bekreft at du vil gjerne denne tjeneren.",
|
||||||
"message.action.reset.password.off": "Din instans støtter foreløpig ikke denne funksjonen.",
|
"message.action.reset.password.off": "Din instans støtter foreløpig ikke denne funksjonen.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Bezig met vernietigen van Instantie....",
|
"label.action.destroy.instance.processing": "Bezig met vernietigen van Instantie....",
|
||||||
"label.action.destroy.systemvm": "Vernietig Systeem VM",
|
"label.action.destroy.systemvm": "Vernietig Systeem VM",
|
||||||
"label.action.destroy.systemvm.processing": "Bezig met vernietigen van Systeem VM....",
|
"label.action.destroy.systemvm.processing": "Bezig met vernietigen van Systeem VM....",
|
||||||
|
"label.action.destroy.volume":"Vernietig schijf",
|
||||||
"label.action.detach.disk": "Ontkoppel Schijf",
|
"label.action.detach.disk": "Ontkoppel Schijf",
|
||||||
"label.action.detach.disk.processing": "Bezig met ontkoppelen van Schijf....",
|
"label.action.detach.disk.processing": "Bezig met ontkoppelen van Schijf....",
|
||||||
"label.action.detach.iso": "Ontkoppel ISO",
|
"label.action.detach.iso": "Ontkoppel ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Bezig met herstarten van Router....",
|
"label.action.reboot.router.processing": "Bezig met herstarten van Router....",
|
||||||
"label.action.reboot.systemvm": "Herstart Systeem VM",
|
"label.action.reboot.systemvm": "Herstart Systeem VM",
|
||||||
"label.action.reboot.systemvm.processing": "Bezig met herstarten van Systeem VM....",
|
"label.action.reboot.systemvm.processing": "Bezig met herstarten van Systeem VM....",
|
||||||
|
"label.action.recover.volume":"Herstel schijf",
|
||||||
"label.action.recurring.snapshot": "Terugkerende Snapshots",
|
"label.action.recurring.snapshot": "Terugkerende Snapshots",
|
||||||
"label.action.register.iso": "Registreer ISO",
|
"label.action.register.iso": "Registreer ISO",
|
||||||
"label.action.register.template": "Registreer een template van een URL",
|
"label.action.register.template": "Registreer een template van een URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Bevestig dat u deze zone wilt verwijderen",
|
"message.action.delete.zone": "Bevestig dat u deze zone wilt verwijderen",
|
||||||
"message.action.destroy.instance": "Bevestig dat u deze instantie wilt vernietigen",
|
"message.action.destroy.instance": "Bevestig dat u deze instantie wilt vernietigen",
|
||||||
"message.action.destroy.systemvm": "Bevestig dat u deze Systeem VM wilt vernietigen",
|
"message.action.destroy.systemvm": "Bevestig dat u deze Systeem VM wilt vernietigen",
|
||||||
|
"message.action.destroy.volume":"Bevestig alstublieft dat U deze schijf wilt vernietigen?",
|
||||||
"message.action.disable.cluster": "Bevestig dat u dit cluster wilt uitschakelen.",
|
"message.action.disable.cluster": "Bevestig dat u dit cluster wilt uitschakelen.",
|
||||||
"message.action.disable.nexusVswitch": "Bevestig dat u deze nexus 1000v wilt uitschakelen.",
|
"message.action.disable.nexusVswitch": "Bevestig dat u deze nexus 1000v wilt uitschakelen.",
|
||||||
"message.action.disable.physical.network": "Bevestig dat u dit fysieke netwerk wilt uitschakelen.",
|
"message.action.disable.physical.network": "Bevestig dat u dit fysieke netwerk wilt uitschakelen.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Bevestig dat u deze instantie wilt herstarten.",
|
"message.action.reboot.instance": "Bevestig dat u deze instantie wilt herstarten.",
|
||||||
"message.action.reboot.router": "Als u deze router herstarten zullen de diensten op de router verstoord worden. Weet u zeker dat u deze actie wil uitvoeren?",
|
"message.action.reboot.router": "Als u deze router herstarten zullen de diensten op de router verstoord worden. Weet u zeker dat u deze actie wil uitvoeren?",
|
||||||
"message.action.reboot.systemvm": "Bevestig dat u deze Systeem VM wilt herstarten.",
|
"message.action.reboot.systemvm": "Bevestig dat u deze Systeem VM wilt herstarten.",
|
||||||
|
"message.action.recover.volume":"Bevestig alstublieft dat U deze schijf wilt herstellen?",
|
||||||
"message.action.release.ip": "Bevestigd dat u dit IP adres wilt los koppelen.",
|
"message.action.release.ip": "Bevestigd dat u dit IP adres wilt los koppelen.",
|
||||||
"message.action.remove.host": "Bevestig dat u deze host wilt verwijderen.",
|
"message.action.remove.host": "Bevestig dat u deze host wilt verwijderen.",
|
||||||
"message.action.reset.password.off": "Uw instantie ondersteunt deze functie momenteel niet.",
|
"message.action.reset.password.off": "Uw instantie ondersteunt deze functie momenteel niet.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Usuwam instancję",
|
"label.action.destroy.instance.processing": "Usuwam instancję",
|
||||||
"label.action.destroy.systemvm": "Destroy System VM",
|
"label.action.destroy.systemvm": "Destroy System VM",
|
||||||
"label.action.destroy.systemvm.processing": "Destroying System VM....",
|
"label.action.destroy.systemvm.processing": "Destroying System VM....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Odłącz dysk",
|
"label.action.detach.disk": "Odłącz dysk",
|
||||||
"label.action.detach.disk.processing": "Odłączanie dysku....",
|
"label.action.detach.disk.processing": "Odłączanie dysku....",
|
||||||
"label.action.detach.iso": "Odłącz obraz ISO",
|
"label.action.detach.iso": "Odłącz obraz ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Restartuje router.....",
|
"label.action.reboot.router.processing": "Restartuje router.....",
|
||||||
"label.action.reboot.systemvm": "Restartuj system VM",
|
"label.action.reboot.systemvm": "Restartuj system VM",
|
||||||
"label.action.reboot.systemvm.processing": "Restartuje system VM....",
|
"label.action.reboot.systemvm.processing": "Restartuje system VM....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Recurring Snapshots",
|
"label.action.recurring.snapshot": "Recurring Snapshots",
|
||||||
"label.action.register.iso": "Rejestruj ISO",
|
"label.action.register.iso": "Rejestruj ISO",
|
||||||
"label.action.register.template": "Register Template from URL",
|
"label.action.register.template": "Register Template from URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
"message.action.delete.zone": "Please confirm that you want to delete this zone.",
|
||||||
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
"message.action.destroy.instance": "Please confirm that you want to destroy this instance.",
|
||||||
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
"message.action.destroy.systemvm": "Please confirm that you want to destroy this System VM.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
"message.action.disable.cluster": "Please confirm that you want to disable this cluster.",
|
||||||
"message.action.disable.nexusVswitch": "Please confirm that you want to disable this nexus 1000v",
|
"message.action.disable.nexusVswitch": "Please confirm that you want to disable this nexus 1000v",
|
||||||
"message.action.disable.physical.network": "Please confirm that you want to disable this physical network.",
|
"message.action.disable.physical.network": "Please confirm that you want to disable this physical network.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
"message.action.reboot.instance": "Please confirm that you want to reboot this instance.",
|
||||||
"message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
"message.action.reboot.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to reboot this router.",
|
||||||
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
"message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
"message.action.release.ip": "Please confirm that you want to release this IP.",
|
||||||
"message.action.remove.host": "Please confirm that you want to remove this host.",
|
"message.action.remove.host": "Please confirm that you want to remove this host.",
|
||||||
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
"message.action.reset.password.off": "Your instance currently does not support this feature.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Apagando Instância....",
|
"label.action.destroy.instance.processing": "Apagando Instância....",
|
||||||
"label.action.destroy.systemvm": "Apagar VM de Sistema",
|
"label.action.destroy.systemvm": "Apagar VM de Sistema",
|
||||||
"label.action.destroy.systemvm.processing": "Apagando VM de Sistema....",
|
"label.action.destroy.systemvm.processing": "Apagando VM de Sistema....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Desplugar Disco",
|
"label.action.detach.disk": "Desplugar Disco",
|
||||||
"label.action.detach.disk.processing": "Desplugando Disco....",
|
"label.action.detach.disk.processing": "Desplugando Disco....",
|
||||||
"label.action.detach.iso": "Desplugar ISO",
|
"label.action.detach.iso": "Desplugar ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Reiniciando Roteador....",
|
"label.action.reboot.router.processing": "Reiniciando Roteador....",
|
||||||
"label.action.reboot.systemvm": "Reiniciar VM de Sistema",
|
"label.action.reboot.systemvm": "Reiniciar VM de Sistema",
|
||||||
"label.action.reboot.systemvm.processing": "Reiniciando VM de Sistema....",
|
"label.action.reboot.systemvm.processing": "Reiniciando VM de Sistema....",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Snapshots recorrentes",
|
"label.action.recurring.snapshot": "Snapshots recorrentes",
|
||||||
"label.action.register.iso": "Registrar ISO",
|
"label.action.register.iso": "Registrar ISO",
|
||||||
"label.action.register.template": "Registrar Template da URL",
|
"label.action.register.template": "Registrar Template da URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Confirme que você deseja remover esta Zona.",
|
"message.action.delete.zone": "Confirme que você deseja remover esta Zona.",
|
||||||
"message.action.destroy.instance": "Por favor, confirme que você deseja excluir esta Instância.",
|
"message.action.destroy.instance": "Por favor, confirme que você deseja excluir esta Instância.",
|
||||||
"message.action.destroy.systemvm": "Confirme que você deseja excluir esta VM de Sistema.",
|
"message.action.destroy.systemvm": "Confirme que você deseja excluir esta VM de Sistema.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Confirma a desativação do cluster.",
|
"message.action.disable.cluster": "Confirma a desativação do cluster.",
|
||||||
"message.action.disable.nexusVswitch": "Por favor confirme que voc<6F> deseja desabilitar este nexusVswitch",
|
"message.action.disable.nexusVswitch": "Por favor confirme que voc<6F> deseja desabilitar este nexusVswitch",
|
||||||
"message.action.disable.physical.network": "Por favor confirme que você deseja desabilitar esta rede física.",
|
"message.action.disable.physical.network": "Por favor confirme que você deseja desabilitar esta rede física.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Por favor, confirme que você deseja reiniciar esta instância.",
|
"message.action.reboot.instance": "Por favor, confirme que você deseja reiniciar esta instância.",
|
||||||
"message.action.reboot.router": "Confirme que voc<6F> deseja reiniciar este roteador.",
|
"message.action.reboot.router": "Confirme que voc<6F> deseja reiniciar este roteador.",
|
||||||
"message.action.reboot.systemvm": "Confirme que você deseja reiniciar esta VM de sistema.",
|
"message.action.reboot.systemvm": "Confirme que você deseja reiniciar esta VM de sistema.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Confirme que você deseja liberar este IP.",
|
"message.action.release.ip": "Confirme que você deseja liberar este IP.",
|
||||||
"message.action.remove.host": "Favor confirmar que você deseja remover este host.",
|
"message.action.remove.host": "Favor confirmar que você deseja remover este host.",
|
||||||
"message.action.reset.password.off": "Sua Instância não suporta esta funcionalidade.",
|
"message.action.reset.password.off": "Sua Instância não suporta esta funcionalidade.",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "Уничтожение машины...",
|
"label.action.destroy.instance.processing": "Уничтожение машины...",
|
||||||
"label.action.destroy.systemvm": "Уничтожить системную ВМ",
|
"label.action.destroy.systemvm": "Уничтожить системную ВМ",
|
||||||
"label.action.destroy.systemvm.processing": "Уничтожение системной ВМ....",
|
"label.action.destroy.systemvm.processing": "Уничтожение системной ВМ....",
|
||||||
|
"label.action.destroy.volume":"Destroy Volume",
|
||||||
"label.action.detach.disk": "Отсоединить диск",
|
"label.action.detach.disk": "Отсоединить диск",
|
||||||
"label.action.detach.disk.processing": "Отсоединение диска....",
|
"label.action.detach.disk.processing": "Отсоединение диска....",
|
||||||
"label.action.detach.iso": "Отсоединить ISO",
|
"label.action.detach.iso": "Отсоединить ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "Перезагрузка роутера...",
|
"label.action.reboot.router.processing": "Перезагрузка роутера...",
|
||||||
"label.action.reboot.systemvm": "Перезапустить системную ВМ",
|
"label.action.reboot.systemvm": "Перезапустить системную ВМ",
|
||||||
"label.action.reboot.systemvm.processing": "Перезагрузка системной ВМ",
|
"label.action.reboot.systemvm.processing": "Перезагрузка системной ВМ",
|
||||||
|
"label.action.recover.volume":"Recover Volume",
|
||||||
"label.action.recurring.snapshot": "Повторяемые снимки",
|
"label.action.recurring.snapshot": "Повторяемые снимки",
|
||||||
"label.action.register.iso": "Регистрация ISO",
|
"label.action.register.iso": "Регистрация ISO",
|
||||||
"label.action.register.template": "Регистрация шаблона по URL",
|
"label.action.register.template": "Регистрация шаблона по URL",
|
||||||
@ -1847,6 +1849,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "Пожалуйста подтвердите, что Вы хотите удалть эту зону.",
|
"message.action.delete.zone": "Пожалуйста подтвердите, что Вы хотите удалть эту зону.",
|
||||||
"message.action.destroy.instance": "Пожалуйста подтвердите, что Вы хотите уничтожить эту машину.",
|
"message.action.destroy.instance": "Пожалуйста подтвердите, что Вы хотите уничтожить эту машину.",
|
||||||
"message.action.destroy.systemvm": "Подтвердите, что вы действительно хотите удалить эту системную ВМ.",
|
"message.action.destroy.systemvm": "Подтвердите, что вы действительно хотите удалить эту системную ВМ.",
|
||||||
|
"message.action.destroy.volume":"Please confirm that you want to destroy this volume.",
|
||||||
"message.action.disable.cluster": "Пожалуйста подтвердите, что Вы хотите отключить данный кластер.",
|
"message.action.disable.cluster": "Пожалуйста подтвердите, что Вы хотите отключить данный кластер.",
|
||||||
"message.action.disable.nexusVswitch": "Пожалуйста, подтвердите, что вы хотите включить это nexusVswitch.",
|
"message.action.disable.nexusVswitch": "Пожалуйста, подтвердите, что вы хотите включить это nexusVswitch.",
|
||||||
"message.action.disable.physical.network": "Подтвердите, что вы действительно хотите выключить эту физическую сеть.",
|
"message.action.disable.physical.network": "Подтвердите, что вы действительно хотите выключить эту физическую сеть.",
|
||||||
@ -1871,6 +1874,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "Подтвердите, что вы действительно хотите перезагрузить эту машину.",
|
"message.action.reboot.instance": "Подтвердите, что вы действительно хотите перезагрузить эту машину.",
|
||||||
"message.action.reboot.router": "Подтвердите, что вы действительно хотите перезагрузить этот роутер.",
|
"message.action.reboot.router": "Подтвердите, что вы действительно хотите перезагрузить этот роутер.",
|
||||||
"message.action.reboot.systemvm": "Подтвердите, что вы действительно хотите запустить эту системную ВМ.",
|
"message.action.reboot.systemvm": "Подтвердите, что вы действительно хотите запустить эту системную ВМ.",
|
||||||
|
"message.action.recover.volume":"Please confirm that you would like to recover this volume.",
|
||||||
"message.action.release.ip": "Пожалуйста подтвержите желание освободить этот IP адрес.",
|
"message.action.release.ip": "Пожалуйста подтвержите желание освободить этот IP адрес.",
|
||||||
"message.action.remove.host": "Удаление последнего/единственного сервера в кластере и повторная его установка приведет уничтожению рабочего окружения/базы данных на сервере и сделае гостевые машины непригодными к использованию.",
|
"message.action.remove.host": "Удаление последнего/единственного сервера в кластере и повторная его установка приведет уничтожению рабочего окружения/базы данных на сервере и сделае гостевые машины непригодными к использованию.",
|
||||||
"message.action.reset.password.off": "На данный момент машина не поддерживает данную функцию",
|
"message.action.reset.password.off": "На данный момент машина не поддерживает данную функцию",
|
||||||
|
|||||||
@ -180,6 +180,7 @@ var dictionary = {
|
|||||||
"label.action.destroy.instance.processing": "正在销毁实例...",
|
"label.action.destroy.instance.processing": "正在销毁实例...",
|
||||||
"label.action.destroy.systemvm": "销毁系统 VM",
|
"label.action.destroy.systemvm": "销毁系统 VM",
|
||||||
"label.action.destroy.systemvm.processing": "正在销毁系统 VM...",
|
"label.action.destroy.systemvm.processing": "正在销毁系统 VM...",
|
||||||
|
"label.action.destroy.volume":"销毁卷",
|
||||||
"label.action.detach.disk": "取消附加磁盘",
|
"label.action.detach.disk": "取消附加磁盘",
|
||||||
"label.action.detach.disk.processing": "正在取消附加磁盘...",
|
"label.action.detach.disk.processing": "正在取消附加磁盘...",
|
||||||
"label.action.detach.iso": "取消附加 ISO",
|
"label.action.detach.iso": "取消附加 ISO",
|
||||||
@ -258,6 +259,7 @@ var dictionary = {
|
|||||||
"label.action.reboot.router.processing": "正在重新启动路由器...",
|
"label.action.reboot.router.processing": "正在重新启动路由器...",
|
||||||
"label.action.reboot.systemvm": "重新启动系统 VM",
|
"label.action.reboot.systemvm": "重新启动系统 VM",
|
||||||
"label.action.reboot.systemvm.processing": "正在重新启动系统 VM...",
|
"label.action.reboot.systemvm.processing": "正在重新启动系统 VM...",
|
||||||
|
"label.action.recover.volume":"恢复卷",
|
||||||
"label.action.recurring.snapshot": "重现快照",
|
"label.action.recurring.snapshot": "重现快照",
|
||||||
"label.action.register.iso": "注册 ISO",
|
"label.action.register.iso": "注册 ISO",
|
||||||
"label.action.register.template": "使用URL注册模板",
|
"label.action.register.template": "使用URL注册模板",
|
||||||
@ -1849,6 +1851,7 @@ var dictionary = {
|
|||||||
"message.action.delete.zone": "请确认您确实要删除此资源域。",
|
"message.action.delete.zone": "请确认您确实要删除此资源域。",
|
||||||
"message.action.destroy.instance": "请确认您确实要销毁此实例。",
|
"message.action.destroy.instance": "请确认您确实要销毁此实例。",
|
||||||
"message.action.destroy.systemvm": "请确认您确实要销毁此系统 VM。",
|
"message.action.destroy.systemvm": "请确认您确实要销毁此系统 VM。",
|
||||||
|
"message.action.destroy.volume":"你确定要销毁这个卷吗?",
|
||||||
"message.action.disable.cluster": "请确认您确实要禁用此群集。",
|
"message.action.disable.cluster": "请确认您确实要禁用此群集。",
|
||||||
"message.action.disable.nexusVswitch": "请确认您确实要禁用此 Nexus 1000v",
|
"message.action.disable.nexusVswitch": "请确认您确实要禁用此 Nexus 1000v",
|
||||||
"message.action.disable.physical.network": "请确认您确实要禁用此物理网络。",
|
"message.action.disable.physical.network": "请确认您确实要禁用此物理网络。",
|
||||||
@ -1873,6 +1876,7 @@ var dictionary = {
|
|||||||
"message.action.reboot.instance": "请确认您确实要重新启动此实例。",
|
"message.action.reboot.instance": "请确认您确实要重新启动此实例。",
|
||||||
"message.action.reboot.router": "此虚拟路由器提供的所有服务都将中断。请确认您确实要重新启动此路由器。",
|
"message.action.reboot.router": "此虚拟路由器提供的所有服务都将中断。请确认您确实要重新启动此路由器。",
|
||||||
"message.action.reboot.systemvm": "请确认您确实要重新启动此系统 VM。",
|
"message.action.reboot.systemvm": "请确认您确实要重新启动此系统 VM。",
|
||||||
|
"message.action.recover.volume":"你确定要恢复这个卷吗?",
|
||||||
"message.action.release.ip": "请确认您确实要释放此 IP。",
|
"message.action.release.ip": "请确认您确实要释放此 IP。",
|
||||||
"message.action.remove.host": "请确认您确实要删除此主机。",
|
"message.action.remove.host": "请确认您确实要删除此主机。",
|
||||||
"message.action.reset.password.off": "您的实例当前不支持此功能。",
|
"message.action.reset.password.off": "您的实例当前不支持此功能。",
|
||||||
|
|||||||
@ -206,6 +206,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_allowUserExpungeRecoverVm = json.listcapabilitiesresponse.capability.allowuserexpungerecovervm;
|
g_allowUserExpungeRecoverVm = json.listcapabilitiesresponse.capability.allowuserexpungerecovervm;
|
||||||
|
g_allowUserExpungeRecoverVolume = json.listcapabilitiesresponse.capability.allowuserexpungerecovervolume;
|
||||||
g_userProjectsEnabled = json.listcapabilitiesresponse.capability.allowusercreateprojects;
|
g_userProjectsEnabled = json.listcapabilitiesresponse.capability.allowusercreateprojects;
|
||||||
|
|
||||||
g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion;
|
g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion;
|
||||||
@ -337,6 +338,7 @@
|
|||||||
g_userPublicTemplateEnabled = json.listcapabilitiesresponse.capability.userpublictemplateenabled.toString(); //convert boolean to string if it's boolean
|
g_userPublicTemplateEnabled = json.listcapabilitiesresponse.capability.userpublictemplateenabled.toString(); //convert boolean to string if it's boolean
|
||||||
}
|
}
|
||||||
g_allowUserExpungeRecoverVm = json.listcapabilitiesresponse.capability.allowuserexpungerecovervm;
|
g_allowUserExpungeRecoverVm = json.listcapabilitiesresponse.capability.allowuserexpungerecovervm;
|
||||||
|
g_allowUserExpungeRecoverVolume = json.listcapabilitiesresponse.capability.allowuserexpungerecovervolume;
|
||||||
g_userProjectsEnabled = json.listcapabilitiesresponse.capability.allowusercreateprojects;
|
g_userProjectsEnabled = json.listcapabilitiesresponse.capability.allowusercreateprojects;
|
||||||
|
|
||||||
g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion;
|
g_cloudstackversion = json.listcapabilitiesresponse.capability.cloudstackversion;
|
||||||
|
|||||||
@ -31,6 +31,7 @@ var g_kvmsnapshotenabled = null;
|
|||||||
var g_regionsecondaryenabled = null;
|
var g_regionsecondaryenabled = null;
|
||||||
var g_userPublicTemplateEnabled = "true";
|
var g_userPublicTemplateEnabled = "true";
|
||||||
var g_allowUserExpungeRecoverVm = "false";
|
var g_allowUserExpungeRecoverVm = "false";
|
||||||
|
var g_allowUserExpungeRecoverVolume = "false";
|
||||||
var g_cloudstackversion = null;
|
var g_cloudstackversion = null;
|
||||||
var g_queryAsyncJobResultInterval = 3000;
|
var g_queryAsyncJobResultInterval = 3000;
|
||||||
var g_idpList = null;
|
var g_idpList = null;
|
||||||
|
|||||||
@ -213,6 +213,9 @@
|
|||||||
zonename: {
|
zonename: {
|
||||||
label: 'label.zone'
|
label: 'label.zone'
|
||||||
},
|
},
|
||||||
|
vmdisplayname: {
|
||||||
|
label: 'label.vm.display.name'
|
||||||
|
},
|
||||||
state: {
|
state: {
|
||||||
label: 'label.metrics.state',
|
label: 'label.metrics.state',
|
||||||
converter: function (str) {
|
converter: function (str) {
|
||||||
@ -224,6 +227,7 @@
|
|||||||
'Ready': 'on',
|
'Ready': 'on',
|
||||||
'Destroy': 'off',
|
'Destroy': 'off',
|
||||||
'Expunging': 'off',
|
'Expunging': 'off',
|
||||||
|
'Expunged': 'off',
|
||||||
'Migrating': 'warning',
|
'Migrating': 'warning',
|
||||||
'UploadOp': 'warning',
|
'UploadOp': 'warning',
|
||||||
'Snapshotting': 'warning',
|
'Snapshotting': 'warning',
|
||||||
@ -817,6 +821,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
state: {
|
||||||
|
label: 'label.state',
|
||||||
|
select: function(args) {
|
||||||
|
args.response.success({
|
||||||
|
data: [{
|
||||||
|
name: '',
|
||||||
|
description: ''
|
||||||
|
}, {
|
||||||
|
name: 'Allocated',
|
||||||
|
description: 'state.Allocated'
|
||||||
|
}, {
|
||||||
|
name: 'Ready',
|
||||||
|
description: 'state.Ready'
|
||||||
|
}, {
|
||||||
|
name: 'Destroy',
|
||||||
|
description: 'state.Destroy'
|
||||||
|
}, {
|
||||||
|
name: 'Expunging',
|
||||||
|
description: 'state.Expunging'
|
||||||
|
}, {
|
||||||
|
name: 'Expunged',
|
||||||
|
description: 'state.Expunged'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
tagKey: {
|
tagKey: {
|
||||||
label: 'label.tag.key'
|
label: 'label.tag.key'
|
||||||
},
|
},
|
||||||
@ -1446,6 +1477,102 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
destroy: {
|
||||||
|
label: 'label.action.destroy.volume',
|
||||||
|
createForm: {
|
||||||
|
title: 'label.action.destroy.volume',
|
||||||
|
desc: 'message.action.destroy.volume',
|
||||||
|
isWarning: true,
|
||||||
|
preFilter: function(args) {
|
||||||
|
if (!isAdmin() && ! g_allowUserExpungeRecoverVolume) {
|
||||||
|
args.$form.find('.form-item[rel=expunge]').hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
expunge: {
|
||||||
|
label: 'label.expunge',
|
||||||
|
isBoolean: true,
|
||||||
|
isChecked: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
confirm: function(args) {
|
||||||
|
return 'message.action.destroy.volume';
|
||||||
|
},
|
||||||
|
notification: function(args) {
|
||||||
|
return 'label.action.destroy.volume';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
action: function(args) {
|
||||||
|
var data = {
|
||||||
|
id: args.context.volumes[0].id
|
||||||
|
};
|
||||||
|
if (args.data.expunge == 'on') {
|
||||||
|
$.extend(data, {
|
||||||
|
expunge: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: createURL("destroyVolume"),
|
||||||
|
data: data,
|
||||||
|
dataType: "json",
|
||||||
|
async: true,
|
||||||
|
success: function(json) {
|
||||||
|
var jid = json.destroyvolumeresponse.jobid;
|
||||||
|
args.response.success({
|
||||||
|
_custom: {
|
||||||
|
jobId: jid,
|
||||||
|
getUpdatedItem: function(json) {
|
||||||
|
if ('volume' in json.queryasyncjobresultresponse.jobresult) { //destroy without expunge
|
||||||
|
var volume = json.queryasyncjobresultresponse.jobresult.volume;
|
||||||
|
if (volume.state == 'Expunged') {
|
||||||
|
return { 'toRemove': true };
|
||||||
|
} else {
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
} else //destroy with expunge
|
||||||
|
return { 'toRemove': true };
|
||||||
|
},
|
||||||
|
getActionFilter: function() {
|
||||||
|
return volumeActionfilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
notification: {
|
||||||
|
poll: pollAsyncJobResult
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
recover: {
|
||||||
|
label: 'label.action.recover.volume',
|
||||||
|
messages: {
|
||||||
|
confirm: function(args) {
|
||||||
|
return 'message.action.recover.volume';
|
||||||
|
},
|
||||||
|
notification: function(args) {
|
||||||
|
return 'label.action.recover.volume';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
action: function(args) {
|
||||||
|
$.ajax({
|
||||||
|
url: createURL("recoverVolume&id=" + args.context.volumes[0].id),
|
||||||
|
dataType: "json",
|
||||||
|
success: function(json) {
|
||||||
|
args.response.success();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
notification: {
|
||||||
|
poll: function(args) {
|
||||||
|
args.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
resize: {
|
resize: {
|
||||||
label: 'label.action.resize.volume',
|
label: 'label.action.resize.volume',
|
||||||
messages: {
|
messages: {
|
||||||
@ -2656,6 +2783,15 @@
|
|||||||
var jsonObj = args.context.item;
|
var jsonObj = args.context.item;
|
||||||
var allowedActions = [];
|
var allowedActions = [];
|
||||||
|
|
||||||
|
if ((isAdmin() || g_allowUserExpungeRecoverVolume) && jsonObj.state == 'Destroy') {
|
||||||
|
return ["recover", "remove"];
|
||||||
|
} else if (jsonObj.state == 'Destroy') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonObj.state == 'Expunging' || jsonObj.state == 'Expunged') {
|
||||||
|
return ["remove"];
|
||||||
|
}
|
||||||
|
|
||||||
if (jsonObj.state == 'Destroyed' || jsonObj.state == 'Migrating' || jsonObj.state == 'Uploading') {
|
if (jsonObj.state == 'Destroyed' || jsonObj.state == 'Migrating' || jsonObj.state == 'Uploading') {
|
||||||
return [];
|
return [];
|
||||||
@ -2710,7 +2846,12 @@
|
|||||||
allowedActions.push("detachDisk");
|
allowedActions.push("detachDisk");
|
||||||
}
|
}
|
||||||
} else { // Disk not attached
|
} else { // Disk not attached
|
||||||
allowedActions.push("remove");
|
if (jsonObj.state == "Allocated" || jsonObj.state == "Uploaded") {
|
||||||
|
allowedActions.push("remove");
|
||||||
|
} else {
|
||||||
|
allowedActions.push("createTemplate");
|
||||||
|
allowedActions.push("destroy");
|
||||||
|
}
|
||||||
if (jsonObj.state == "Ready" && isAdmin()) {
|
if (jsonObj.state == "Ready" && isAdmin()) {
|
||||||
allowedActions.push("migrateToAnotherStorage");
|
allowedActions.push("migrateToAnotherStorage");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user