mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
feature: Shared Storage Filesystem as a First Class Feature (#9208)
This PR implements Storage filesystem as a first class feature. https://cwiki.apache.org/confluence/display/CLOUDSTACK/Storage+Filesystem+as+a+First+Class+Feature Documentation PR: apache/cloudstack-documentation#420 Co-authored-by: Wei Zhou <weizhou@apache.org>
This commit is contained in:
parent
72d0546d8b
commit
605534b417
@ -30,6 +30,7 @@ import org.apache.cloudstack.api.response.ZoneResponse;
|
|||||||
import org.apache.cloudstack.config.Configuration;
|
import org.apache.cloudstack.config.Configuration;
|
||||||
import org.apache.cloudstack.ha.HAConfig;
|
import org.apache.cloudstack.ha.HAConfig;
|
||||||
import org.apache.cloudstack.quota.QuotaTariff;
|
import org.apache.cloudstack.quota.QuotaTariff;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
import org.apache.cloudstack.storage.object.Bucket;
|
import org.apache.cloudstack.storage.object.Bucket;
|
||||||
import org.apache.cloudstack.storage.object.ObjectStore;
|
import org.apache.cloudstack.storage.object.ObjectStore;
|
||||||
import org.apache.cloudstack.usage.Usage;
|
import org.apache.cloudstack.usage.Usage;
|
||||||
@ -744,6 +745,18 @@ public class EventTypes {
|
|||||||
public static final String EVENT_QUOTA_TARIFF_DELETE = "QUOTA.TARIFF.DELETE";
|
public static final String EVENT_QUOTA_TARIFF_DELETE = "QUOTA.TARIFF.DELETE";
|
||||||
public static final String EVENT_QUOTA_TARIFF_UPDATE = "QUOTA.TARIFF.UPDATE";
|
public static final String EVENT_QUOTA_TARIFF_UPDATE = "QUOTA.TARIFF.UPDATE";
|
||||||
|
|
||||||
|
// SharedFS
|
||||||
|
public static final String EVENT_SHAREDFS_CREATE = "SHAREDFS.CREATE";
|
||||||
|
public static final String EVENT_SHAREDFS_START = "SHAREDFS.START";
|
||||||
|
public static final String EVENT_SHAREDFS_UPDATE = "SHAREDFS.UPDATE";
|
||||||
|
public static final String EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING = "SHAREDFS.CHANGE.SERVICE.OFFERING";
|
||||||
|
public static final String EVENT_SHAREDFS_CHANGE_DISK_OFFERING = "SHAREDFS.CHANGE.DISK.OFFERING";
|
||||||
|
public static final String EVENT_SHAREDFS_STOP = "SHAREDFS.STOP";
|
||||||
|
public static final String EVENT_SHAREDFS_RESTART = "SHAREDFS.RESTART";
|
||||||
|
public static final String EVENT_SHAREDFS_DESTROY = "SHAREDFS.DESTROY";
|
||||||
|
public static final String EVENT_SHAREDFS_EXPUNGE = "SHAREDFS.EXPUNGE";
|
||||||
|
public static final String EVENT_SHAREDFS_RECOVER = "SHAREDFS.RECOVER";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
||||||
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
|
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
|
||||||
@ -1203,6 +1216,18 @@ public class EventTypes {
|
|||||||
entityEventDetails.put(EVENT_QUOTA_TARIFF_CREATE, QuotaTariff.class);
|
entityEventDetails.put(EVENT_QUOTA_TARIFF_CREATE, QuotaTariff.class);
|
||||||
entityEventDetails.put(EVENT_QUOTA_TARIFF_DELETE, QuotaTariff.class);
|
entityEventDetails.put(EVENT_QUOTA_TARIFF_DELETE, QuotaTariff.class);
|
||||||
entityEventDetails.put(EVENT_QUOTA_TARIFF_UPDATE, QuotaTariff.class);
|
entityEventDetails.put(EVENT_QUOTA_TARIFF_UPDATE, QuotaTariff.class);
|
||||||
|
|
||||||
|
// SharedFS
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_CREATE, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_START, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_STOP, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_UPDATE, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_CHANGE_DISK_OFFERING, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_RESTART, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_DESTROY, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_EXPUNGE, SharedFS.class);
|
||||||
|
entityEventDetails.put(EVENT_SHAREDFS_RECOVER, SharedFS.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNetworkEvent(String eventType) {
|
public static boolean isNetworkEvent(String eventType) {
|
||||||
|
|||||||
@ -102,8 +102,12 @@ public interface VolumeApiService {
|
|||||||
|
|
||||||
boolean deleteVolume(long volumeId, Account caller);
|
boolean deleteVolume(long volumeId, Account caller);
|
||||||
|
|
||||||
|
Volume changeDiskOfferingForVolumeInternal(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean autoMigrateVolume, boolean shrinkOk) throws ResourceAllocationException;
|
||||||
|
|
||||||
Volume attachVolumeToVM(AttachVolumeCmd command);
|
Volume attachVolumeToVM(AttachVolumeCmd command);
|
||||||
|
|
||||||
|
Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS);
|
||||||
|
|
||||||
Volume detachVolumeViaDestroyVM(long vmId, long volumeId);
|
Volume detachVolumeViaDestroyVM(long vmId, long volumeId);
|
||||||
|
|
||||||
Volume detachVolumeFromVM(DetachVolumeCmd cmd);
|
Volume detachVolumeFromVM(DetachVolumeCmd cmd);
|
||||||
|
|||||||
@ -85,7 +85,8 @@ public enum ApiCommandResourceType {
|
|||||||
Bucket(org.apache.cloudstack.storage.object.Bucket.class),
|
Bucket(org.apache.cloudstack.storage.object.Bucket.class),
|
||||||
QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.class),
|
QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.class),
|
||||||
KubernetesCluster(null),
|
KubernetesCluster(null),
|
||||||
KubernetesSupportedVersion(null);
|
KubernetesSupportedVersion(null),
|
||||||
|
SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class);
|
||||||
|
|
||||||
private final Class<?> clazz;
|
private final Class<?> clazz;
|
||||||
|
|
||||||
|
|||||||
@ -188,6 +188,7 @@ public class ApiConstants {
|
|||||||
public static final String EXTERNAL_UUID = "externaluuid";
|
public static final String EXTERNAL_UUID = "externaluuid";
|
||||||
public static final String FENCE = "fence";
|
public static final String FENCE = "fence";
|
||||||
public static final String FETCH_LATEST = "fetchlatest";
|
public static final String FETCH_LATEST = "fetchlatest";
|
||||||
|
public static final String FILESYSTEM = "filesystem";
|
||||||
public static final String FIRSTNAME = "firstname";
|
public static final String FIRSTNAME = "firstname";
|
||||||
public static final String FORCED = "forced";
|
public static final String FORCED = "forced";
|
||||||
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
|
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
|
||||||
@ -432,6 +433,7 @@ public class ApiConstants {
|
|||||||
public static final String SIGNATURE_VERSION = "signatureversion";
|
public static final String SIGNATURE_VERSION = "signatureversion";
|
||||||
public static final String SINCE = "since";
|
public static final String SINCE = "since";
|
||||||
public static final String SIZE = "size";
|
public static final String SIZE = "size";
|
||||||
|
public static final String SIZEGB = "sizegb";
|
||||||
public static final String SNAPSHOT = "snapshot";
|
public static final String SNAPSHOT = "snapshot";
|
||||||
public static final String SNAPSHOT_ID = "snapshotid";
|
public static final String SNAPSHOT_ID = "snapshotid";
|
||||||
public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid";
|
public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid";
|
||||||
@ -504,6 +506,7 @@ public class ApiConstants {
|
|||||||
public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
|
public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
|
||||||
public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
|
public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
|
||||||
public static final String VIRTUAL_MACHINE_TYPE = "virtualmachinetype";
|
public static final String VIRTUAL_MACHINE_TYPE = "virtualmachinetype";
|
||||||
|
public static final String VIRTUAL_MACHINE_STATE = "vmstate";
|
||||||
public static final String VIRTUAL_MACHINES = "virtualmachines";
|
public static final String VIRTUAL_MACHINES = "virtualmachines";
|
||||||
public static final String USAGE_ID = "usageid";
|
public static final String USAGE_ID = "usageid";
|
||||||
public static final String USAGE_TYPE = "usagetype";
|
public static final String USAGE_TYPE = "usagetype";
|
||||||
@ -1143,6 +1146,9 @@ public class ApiConstants {
|
|||||||
|
|
||||||
public static final String NFS_MOUNT_OPTIONS = "nfsmountopts";
|
public static final String NFS_MOUNT_OPTIONS = "nfsmountopts";
|
||||||
|
|
||||||
|
public static final String SHAREDFSVM_MIN_CPU_COUNT = "sharedfsvmmincpucount";
|
||||||
|
public static final String SHAREDFSVM_MIN_RAM_SIZE = "sharedfsvmminramsize";
|
||||||
|
|
||||||
public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " +
|
public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " +
|
||||||
"a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " +
|
"a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " +
|
||||||
"numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " +
|
"numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " +
|
||||||
|
|||||||
@ -54,6 +54,7 @@ import org.apache.cloudstack.api.response.DomainResponse;
|
|||||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||||
import org.apache.cloudstack.api.response.EventResponse;
|
import org.apache.cloudstack.api.response.EventResponse;
|
||||||
import org.apache.cloudstack.api.response.ExtractResponse;
|
import org.apache.cloudstack.api.response.ExtractResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
import org.apache.cloudstack.api.response.FirewallResponse;
|
import org.apache.cloudstack.api.response.FirewallResponse;
|
||||||
import org.apache.cloudstack.api.response.FirewallRuleResponse;
|
import org.apache.cloudstack.api.response.FirewallRuleResponse;
|
||||||
import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
|
import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
|
||||||
@ -151,6 +152,7 @@ import org.apache.cloudstack.region.PortableIp;
|
|||||||
import org.apache.cloudstack.region.PortableIpRange;
|
import org.apache.cloudstack.region.PortableIpRange;
|
||||||
import org.apache.cloudstack.region.Region;
|
import org.apache.cloudstack.region.Region;
|
||||||
import org.apache.cloudstack.secstorage.heuristics.Heuristic;
|
import org.apache.cloudstack.secstorage.heuristics.Heuristic;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
import org.apache.cloudstack.storage.object.ObjectStore;
|
import org.apache.cloudstack.storage.object.ObjectStore;
|
||||||
import org.apache.cloudstack.usage.Usage;
|
import org.apache.cloudstack.usage.Usage;
|
||||||
|
|
||||||
@ -551,4 +553,6 @@ public interface ResponseGenerator {
|
|||||||
ObjectStoreResponse createObjectStoreResponse(ObjectStore os);
|
ObjectStoreResponse createObjectStoreResponse(ObjectStore os);
|
||||||
|
|
||||||
BucketResponse createBucketResponse(Bucket bucket);
|
BucketResponse createBucketResponse(Bucket bucket);
|
||||||
|
|
||||||
|
SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,6 +70,8 @@ public class ListCapabilitiesCmd extends BaseCmd {
|
|||||||
response.setInstancesStatsUserOnly((Boolean) capabilities.get(ApiConstants.INSTANCES_STATS_USER_ONLY));
|
response.setInstancesStatsUserOnly((Boolean) capabilities.get(ApiConstants.INSTANCES_STATS_USER_ONLY));
|
||||||
response.setInstancesDisksStatsRetentionEnabled((Boolean) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED));
|
response.setInstancesDisksStatsRetentionEnabled((Boolean) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED));
|
||||||
response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
|
response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
|
||||||
|
response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT));
|
||||||
|
response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE));
|
||||||
response.setObjectName("capability");
|
response.setObjectName("capability");
|
||||||
response.setResponseName(getCommandName());
|
response.setResponseName(getCommandName());
|
||||||
this.setResponseObject(response);
|
this.setResponseObject(response);
|
||||||
|
|||||||
@ -0,0 +1,145 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
|
@APICommand(name = "changeSharedFileSystemDiskOffering",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "Change Disk offering of a Shared FileSystem",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class ChangeSharedFSDiskOfferingCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DISK_OFFERING_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
entityType = DiskOfferingResponse.class,
|
||||||
|
description = "the disk offering to use for the underlying storage")
|
||||||
|
private Long diskOfferingId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SIZE,
|
||||||
|
type = CommandType.LONG,
|
||||||
|
description = "the size of the shared filesystem in GiB")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.MIN_IOPS,
|
||||||
|
type = CommandType.LONG,
|
||||||
|
description = "min iops")
|
||||||
|
private Long minIops;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.MAX_IOPS,
|
||||||
|
type = CommandType.LONG,
|
||||||
|
description = "max iops")
|
||||||
|
private Long maxIops;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getDiskOfferingId() {
|
||||||
|
return diskOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMinIops() {
|
||||||
|
return minIops;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMaxIops() {
|
||||||
|
return maxIops;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_CHANGE_DISK_OFFERING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Changing disk offering for the Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws ResourceAllocationException {
|
||||||
|
SharedFS sharedFS = sharedFSService.changeSharedFSDiskOffering(this);
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change disk offering for the Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,147 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.ManagementServerException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.exception.VirtualMachineMigrationException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "changeSharedFileSystemServiceOffering",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "Change Service offering of a Shared FileSystem",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class ChangeSharedFSServiceOfferingCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SERVICE_OFFERING_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
entityType = ServiceOfferingResponse.class,
|
||||||
|
required = true,
|
||||||
|
description = "the offering to use for the shared filesystem vm")
|
||||||
|
private Long serviceOfferingId;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getServiceOfferingId() {
|
||||||
|
return serviceOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Changing service offering for the Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getExceptionMsg(Exception ex) {
|
||||||
|
return "Shared FileSystem restart failed with exception" + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS;
|
||||||
|
try {
|
||||||
|
sharedFS = sharedFSService.changeSharedFSServiceOffering(this);
|
||||||
|
} catch (ResourceUnavailableException ex) {
|
||||||
|
logger.warn("Shared FileSystem change service offering exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getExceptionMsg(ex));
|
||||||
|
} catch (InsufficientCapacityException ex) {
|
||||||
|
logger.warn("Shared FileSystem change service offering exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getExceptionMsg(ex));
|
||||||
|
} catch (OperationTimedoutException ex) {
|
||||||
|
logger.warn("Shared FileSystem change service offering exception: ", ex);
|
||||||
|
throw new CloudRuntimeException("Shared FileSystem change service offering timed out due to " + ex.getMessage());
|
||||||
|
} catch (ManagementServerException ex) {
|
||||||
|
logger.warn("Shared FileSystem change service offering exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
} catch (VirtualMachineMigrationException ex) {
|
||||||
|
logger.warn("Shared FileSystem change service offering exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change the service offering for the Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,304 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||||
|
import org.apache.cloudstack.api.response.DomainResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.NetworkResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSProvider;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
@APICommand(name = "createSharedFileSystem",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "Create a new Shared File System of specified size and disk offering, attached to the given network",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class CreateSharedFSCmd extends BaseAsyncCreateCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NAME,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
required = true,
|
||||||
|
description = "the name of the shared filesystem.")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ACCOUNT,
|
||||||
|
type = BaseCmd.CommandType.STRING,
|
||||||
|
description = "the account associated with the shared filesystem. Must be used with the domainId parameter.")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
entityType = DomainResponse.class,
|
||||||
|
description = "the domain ID associated with the shared filesystem. If used with the account parameter"
|
||||||
|
+ " returns the shared filesystem associated with the account for the specified domain." +
|
||||||
|
"If account is NOT provided then the shared filesystem will be assigned to the caller account and domain.")
|
||||||
|
private Long domainId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.PROJECT_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
entityType = ProjectResponse.class,
|
||||||
|
description = "the project associated with the shared filesystem. Mutually exclusive with account parameter")
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DESCRIPTION,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
description = "the description for the shared filesystem.")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SIZE,
|
||||||
|
type = CommandType.LONG,
|
||||||
|
description = "the size of the shared filesystem in GiB")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ZONE_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = ZoneResponse.class,
|
||||||
|
description = "the zone id.")
|
||||||
|
private Long zoneId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DISK_OFFERING_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = DiskOfferingResponse.class,
|
||||||
|
description = "the disk offering to use for the underlying storage. This will define the size and other specifications like encryption and qos for the shared filesystem.")
|
||||||
|
private Long diskOfferingId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.MIN_IOPS,
|
||||||
|
type = CommandType.LONG,
|
||||||
|
description = "min iops")
|
||||||
|
private Long minIops;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.MAX_IOPS,
|
||||||
|
type = CommandType.LONG,
|
||||||
|
description = "max iops")
|
||||||
|
private Long maxIops;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SERVICE_OFFERING_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = ServiceOfferingResponse.class,
|
||||||
|
description = "the service offering to use for the shared filesystem VM hosting the data. The offering should be HA enabled and the cpu count and memory size should be greater than equal to sharedfsvm.min.cpu.count and sharedfsvm.min.ram.size respectively")
|
||||||
|
private Long serviceOfferingId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.FILESYSTEM,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
required = true,
|
||||||
|
description = "the filesystem format (XFS / EXT4) which will be installed on the shared filesystem.")
|
||||||
|
private String fsFormat;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.PROVIDER,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
description = "the provider to be used for the shared filesystem. The list of providers can be fetched by using the listSharedFileSystemProviders API.")
|
||||||
|
private String sharedFSProviderName;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NETWORK_ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = NetworkResponse.class,
|
||||||
|
description = "network to attach the shared filesystem to")
|
||||||
|
private Long networkId;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Long getProjectId() {
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getDomainId() {
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountName() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getZoneId() {
|
||||||
|
return zoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getDiskOfferingId() {
|
||||||
|
return diskOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getServiceOfferingId() {
|
||||||
|
return serviceOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMaxIops() {
|
||||||
|
return maxIops;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMinIops() {
|
||||||
|
return minIops;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFsFormat() {
|
||||||
|
return fsFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getNetworkId() {
|
||||||
|
return networkId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSharedFSProviderName() {
|
||||||
|
if (sharedFSProviderName != null) {
|
||||||
|
return sharedFSProviderName;
|
||||||
|
} else {
|
||||||
|
return SharedFSProvider.SharedFSProviderType.SHAREDFSVM.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApiCommandResourceType getApiResourceType() {
|
||||||
|
return ApiCommandResourceType.SharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getApiResourceId() {
|
||||||
|
return this.getEntityId();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true);
|
||||||
|
if (accountId == null) {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Creating shared filesystem " + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCreateExceptionMsg(Exception ex) {
|
||||||
|
return "Shared FileSystem create failed with exception" + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStartExceptionMsg(Exception ex) {
|
||||||
|
return "Shared FileSystem start failed with exception: " + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void create() {
|
||||||
|
SharedFS sharedFS;
|
||||||
|
sharedFS = sharedFSService.allocSharedFS(this);
|
||||||
|
if (sharedFS != null) {
|
||||||
|
setEntityId(sharedFS.getId());
|
||||||
|
setEntityUuid(sharedFS.getUuid());
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS;
|
||||||
|
try {
|
||||||
|
sharedFS = sharedFSService.deploySharedFS(this);
|
||||||
|
} catch (ResourceUnavailableException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getStartExceptionMsg(ex));
|
||||||
|
} catch (ConcurrentOperationException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, getStartExceptionMsg(ex));
|
||||||
|
} catch (InsufficientCapacityException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getStartExceptionMsg(ex));
|
||||||
|
} catch (ResourceAllocationException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
|
||||||
|
} catch (OperationTimedoutException ex) {
|
||||||
|
throw new CloudRuntimeException("Shared FileSystem start timed out due to " + ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
|
||||||
|
@APICommand(name = "destroySharedFileSystem",
|
||||||
|
responseObject= SuccessResponse.class,
|
||||||
|
description = "Destroy a Shared FileSystem by id",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class DestroySharedFSCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem to delete")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.EXPUNGE,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
description = "If true is passed, the shared filesystem is expunged immediately. False by default.")
|
||||||
|
private Boolean expunge;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.FORCED,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
description = "If true is passed, the shared filesystem can be destroyed without stopping it first.")
|
||||||
|
private Boolean forced;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExpunge() {
|
||||||
|
return (expunge != null) ? expunge : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForced() {
|
||||||
|
return (forced != null) ? forced : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_DESTROY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Destroying Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
Boolean result = sharedFSService.destroySharedFS(this);
|
||||||
|
if (result) {
|
||||||
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
|
||||||
|
@APICommand(name = "expungeSharedFileSystem",
|
||||||
|
responseObject= SuccessResponse.class,
|
||||||
|
description = "Expunge a Shared FileSystem by id",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class ExpungeSharedFSCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SharedFSResponse.class, description = "the ID of the shared filesystem to expunge")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_EXPUNGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Expunging Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
try {
|
||||||
|
sharedFSService.deleteSharedFS(id);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to expunge Shared FileSystem");
|
||||||
|
} finally {
|
||||||
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseListRetrieveOnlyResourceCountCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.api.response.NetworkResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@APICommand(name = "listSharedFileSystems",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "List Shared FileSystems",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class ListSharedFSCmd extends BaseListRetrieveOnlyResourceCountCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SharedFSResponse.class, description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the shared filesystem")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the availability zone")
|
||||||
|
private Long zoneId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "the ID of the network")
|
||||||
|
private Long networkId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the disk offering of the shared filesystem")
|
||||||
|
private Long diskOfferingId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "the service offering of the shared filesystem")
|
||||||
|
private Long serviceOfferingId;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getZoneId() {
|
||||||
|
return zoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getNetworkId() {
|
||||||
|
return networkId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getDiskOfferingId() {
|
||||||
|
return diskOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getServiceOfferingId() {
|
||||||
|
return serviceOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
ListResponse<SharedFSResponse> response = sharedFSService.searchForSharedFS(getResponseView(), this);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.BaseListCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSProviderResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSProvider;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
@APICommand(name = "listSharedFileSystemProviders",
|
||||||
|
responseObject = SharedFSProviderResponse.class,
|
||||||
|
description = "Lists all available shared filesystem providers.",
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class ListSharedFSProvidersCmd extends BaseListCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
List<SharedFSProvider> sharedFSProviders = sharedFSService.getSharedFSProviders();
|
||||||
|
final ListResponse<SharedFSProviderResponse> response = new ListResponse<>();
|
||||||
|
final List<SharedFSProviderResponse> responses = new ArrayList<>();
|
||||||
|
|
||||||
|
for (SharedFSProvider sharedFSProvider : sharedFSProviders) {
|
||||||
|
SharedFSProviderResponse sharedFSProviderResponse = new SharedFSProviderResponse();
|
||||||
|
sharedFSProviderResponse.setName(sharedFSProvider.getName());
|
||||||
|
sharedFSProviderResponse.setObjectName("sharedfilesystemprovider");
|
||||||
|
responses.add(sharedFSProviderResponse);
|
||||||
|
}
|
||||||
|
response.setResponses(responses, responses.size());
|
||||||
|
response.setResponseName(this.getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
@APICommand(name = "recoverSharedFileSystem",
|
||||||
|
responseObject= SuccessResponse.class,
|
||||||
|
description = "Recover a Shared FileSystem by id",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class RecoverSharedFSCmd extends BaseCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SharedFSResponse.class, description = "the ID of the shared filesystem to recover")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS = sharedFSService.recoverSharedFS(id);
|
||||||
|
if (sharedFS != null) {
|
||||||
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to recover Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "restartSharedFileSystem",
|
||||||
|
responseObject= SuccessResponse.class,
|
||||||
|
description = "Restart a Shared FileSystem",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class RestartSharedFSCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.CLEANUP,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
description = "is cleanup required")
|
||||||
|
private boolean cleanup;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getCleanup() {
|
||||||
|
return cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_RESTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Restarting Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRestartExceptionMsg(Exception ex) {
|
||||||
|
return "Shared FileSystem restart failed with exception" + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS;
|
||||||
|
try {
|
||||||
|
sharedFS = sharedFSService.restartSharedFS(this.getId(), this.getCleanup());
|
||||||
|
} catch (ResourceUnavailableException ex) {
|
||||||
|
logger.warn("Shared FileSystem restart exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getRestartExceptionMsg(ex));
|
||||||
|
} catch (ConcurrentOperationException ex) {
|
||||||
|
logger.warn("Shared FileSystem restart exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, getRestartExceptionMsg(ex));
|
||||||
|
} catch (InsufficientCapacityException ex) {
|
||||||
|
logger.warn("Shared FileSystem restart exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getRestartExceptionMsg(ex));
|
||||||
|
} catch (ResourceAllocationException ex) {
|
||||||
|
logger.warn("Shared FileSystem restart exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
|
||||||
|
} catch (OperationTimedoutException ex) {
|
||||||
|
logger.warn("Shared FileSystem restart exception: ", ex);
|
||||||
|
throw new CloudRuntimeException("Shared FileSystem start timed out due to " + ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to restart Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "startSharedFileSystem",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "Start a Shared FileSystem",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class StartSharedFSCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Starting Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStartExceptionMsg(Exception ex) {
|
||||||
|
return "Shared FileSystem start failed with exception: " + ex.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS;
|
||||||
|
try {
|
||||||
|
sharedFS = sharedFSService.startSharedFS(this.getId());
|
||||||
|
} catch (ResourceUnavailableException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, getStartExceptionMsg(ex));
|
||||||
|
} catch (ConcurrentOperationException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, getStartExceptionMsg(ex));
|
||||||
|
} catch (InsufficientCapacityException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, getStartExceptionMsg(ex));
|
||||||
|
} catch (ResourceAllocationException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
|
||||||
|
} catch (OperationTimedoutException ex) {
|
||||||
|
logger.warn("Shared FileSystem start exception: ", ex);
|
||||||
|
throw new CloudRuntimeException("Shared FileSystem start timed out due to " + ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to start Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
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.BaseAsyncCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
|
@APICommand(name = "stopSharedFileSystem",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "Stop a Shared FileSystem",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class StopSharedFSCmd extends BaseAsyncCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.FORCED,
|
||||||
|
type = CommandType.BOOLEAN,
|
||||||
|
description = "Force stop the shared filesystem.")
|
||||||
|
private Boolean forced;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForced() {
|
||||||
|
return (forced != null) ? forced : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventType() {
|
||||||
|
return EventTypes.EVENT_SHAREDFS_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEventDescription() {
|
||||||
|
return "Stopping Shared FileSystem " + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS = sharedFSService.stopSharedFS(this.getId(), this.isForced());
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to stop Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSService;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
|
@APICommand(name = "updateSharedFileSystem",
|
||||||
|
responseObject= SharedFSResponse.class,
|
||||||
|
description = "Update a Shared FileSystem",
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = SharedFS.class,
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
since = "4.20.0",
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||||
|
public class UpdateSharedFSCmd extends BaseCmd implements UserCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SharedFSService sharedFSService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ID,
|
||||||
|
type = CommandType.UUID,
|
||||||
|
required = true,
|
||||||
|
entityType = SharedFSResponse.class,
|
||||||
|
description = "the ID of the shared filesystem")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NAME,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
description = "the name of the shared filesystem.")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DESCRIPTION,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
description = "the description for the shared filesystem.")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccount().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
SharedFS sharedFS = sharedFSService.updateSharedFS(this);
|
||||||
|
if (sharedFS != null) {
|
||||||
|
ResponseObject.ResponseView respView = getResponseView();
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (_accountService.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
SharedFSResponse response = _responseGenerator.createSharedFSResponse(respView, sharedFS);
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} else {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -128,6 +128,14 @@ public class CapabilitiesResponse extends BaseResponse {
|
|||||||
@Param(description = "the retention time for Instances disks stats", since = "4.18.0")
|
@Param(description = "the retention time for Instances disks stats", since = "4.18.0")
|
||||||
private Integer instancesDisksStatsRetentionTime;
|
private Integer instancesDisksStatsRetentionTime;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT)
|
||||||
|
@Param(description = "the min CPU count for the service offering used by the shared filesystem VM", since = "4.20.0")
|
||||||
|
private Integer sharedFsVmMinCpuCount;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE)
|
||||||
|
@Param(description = "the min Ram size for the service offering used by the shared filesystem VM", since = "4.20.0")
|
||||||
|
private Integer sharedFsVmMinRamSize;
|
||||||
|
|
||||||
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
|
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
|
||||||
this.securityGroupsEnabled = securityGroupsEnabled;
|
this.securityGroupsEnabled = securityGroupsEnabled;
|
||||||
}
|
}
|
||||||
@ -231,4 +239,12 @@ public class CapabilitiesResponse extends BaseResponse {
|
|||||||
public void setCustomHypervisorDisplayName(String customHypervisorDisplayName) {
|
public void setCustomHypervisorDisplayName(String customHypervisorDisplayName) {
|
||||||
this.customHypervisorDisplayName = customHypervisorDisplayName;
|
this.customHypervisorDisplayName = customHypervisorDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSharedFsVmMinCpuCount(Integer sharedFsVmMinCpuCount) {
|
||||||
|
this.sharedFsVmMinCpuCount = sharedFsVmMinCpuCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) {
|
||||||
|
this.sharedFsVmMinRamSize = sharedFsVmMinRamSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
// 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.response;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class SharedFSProviderResponse extends BaseResponse {
|
||||||
|
@SerializedName(ApiConstants.NAME)
|
||||||
|
@Param(description = "the name of the shared filesystem provider")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,369 @@
|
|||||||
|
// 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.response;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseResponseWithTagInformation;
|
||||||
|
import org.apache.cloudstack.api.EntityReference;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
|
||||||
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@EntityReference(value = SharedFS.class)
|
||||||
|
public class SharedFSResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse {
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ID)
|
||||||
|
@Param(description = "ID of the shared filesystem")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.NAME)
|
||||||
|
@Param(description = "name of the shared filesystem")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DESCRIPTION)
|
||||||
|
@Param(description = "description of the shared filesystem")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ZONE_ID)
|
||||||
|
@Param(description = "ID of the availability zone")
|
||||||
|
private String zoneId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ZONE_NAME)
|
||||||
|
@Param(description = "Name of the availability zone")
|
||||||
|
private String zoneName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
|
||||||
|
@Param(description = "ID of the storage fs vm")
|
||||||
|
private String virtualMachineId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE)
|
||||||
|
@Param(description = "ID of the storage fs vm")
|
||||||
|
private String virtualMachineState;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.VOLUME_NAME)
|
||||||
|
@Param(description = "name of the storage fs data volume")
|
||||||
|
private String volumeName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.VOLUME_ID)
|
||||||
|
@Param(description = "ID of the storage fs data volume")
|
||||||
|
private String volumeId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.STORAGE)
|
||||||
|
@Param(description = "name of the storage pool hosting the data volume")
|
||||||
|
private String storagePoolName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.STORAGE_ID)
|
||||||
|
@Param(description = "ID of the storage pool hosting the data volume")
|
||||||
|
private String storagePoolId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SIZE)
|
||||||
|
@Param(description = "size of the shared filesystem")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SIZEGB)
|
||||||
|
@Param(description = "size of the shared filesystem in GiB")
|
||||||
|
private String sizeGB;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DISK_OFFERING_ID)
|
||||||
|
@Param(description = "disk offering ID for the shared filesystem")
|
||||||
|
private String diskOfferingId;
|
||||||
|
|
||||||
|
@SerializedName("diskofferingname")
|
||||||
|
@Param(description = "disk offering for the shared filesystem")
|
||||||
|
private String diskOfferingName;
|
||||||
|
|
||||||
|
@SerializedName("iscustomdiskoffering")
|
||||||
|
@Param(description = "disk offering for the shared filesystem has custom size")
|
||||||
|
private Boolean isCustomDiskOffering;
|
||||||
|
|
||||||
|
@SerializedName("diskofferingdisplaytext")
|
||||||
|
@Param(description = "disk offering display text for the shared filesystem")
|
||||||
|
private String diskOfferingDisplayText;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SERVICE_OFFERING_ID)
|
||||||
|
@Param(description = "service offering ID for the shared filesystem")
|
||||||
|
private String serviceOfferingId;
|
||||||
|
|
||||||
|
@SerializedName("serviceofferingname")
|
||||||
|
@Param(description = "service offering for the shared filesystem")
|
||||||
|
private String serviceOfferingName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.NETWORK_ID)
|
||||||
|
@Param(description = "Network ID of the shared filesystem")
|
||||||
|
private String networkId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.NETWORK_NAME)
|
||||||
|
@Param(description = "Network name of the shared filesystem")
|
||||||
|
private String networkName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.NIC)
|
||||||
|
@Param(description = "the list of nics associated with the shared filesystem", responseObject = NicResponse.class)
|
||||||
|
private List<NicResponse> nics;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PATH)
|
||||||
|
@Param(description = "path to mount the shared filesystem")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.STATE)
|
||||||
|
@Param(description = "the state of the shared filesystem")
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PROVIDER)
|
||||||
|
@Param(description = "the shared filesystem provider")
|
||||||
|
private String provider;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.FILESYSTEM)
|
||||||
|
@Param(description = "the filesystem format")
|
||||||
|
private String filesystem;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ACCOUNT)
|
||||||
|
@Param(description = "the account associated with the shared filesystem")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PROJECT_ID)
|
||||||
|
@Param(description = "the project ID of the shared filesystem")
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PROJECT)
|
||||||
|
@Param(description = "the project name of the shared filesystem")
|
||||||
|
private String projectName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||||
|
@Param(description = "the ID of the domain associated with the shared filesystem")
|
||||||
|
private String domainId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DOMAIN)
|
||||||
|
@Param(description = "the domain associated with the shared filesystem")
|
||||||
|
private String domainName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DOMAIN_PATH)
|
||||||
|
@Param(description = "path of the domain to which the shared filesystem")
|
||||||
|
private String domainPath;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PROVISIONINGTYPE)
|
||||||
|
@Param(description = "provisioning type used in the shared filesystem")
|
||||||
|
private String provisioningType;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DISK_IO_READ)
|
||||||
|
@Param(description = "the read (IO) of disk on the shared filesystem")
|
||||||
|
private Long diskIORead;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DISK_IO_WRITE)
|
||||||
|
@Param(description = "the write (IO) of disk on the shared filesystem")
|
||||||
|
private Long diskIOWrite;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DISK_KBS_READ)
|
||||||
|
@Param(description = "the shared filesystem's disk read in KiB")
|
||||||
|
private Long diskKbsRead;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DISK_KBS_WRITE)
|
||||||
|
@Param(description = "the shared filesystem's disk write in KiB")
|
||||||
|
private Long diskKbsWrite;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.VIRTUAL_SIZE)
|
||||||
|
@Param(description = "the bytes allocated")
|
||||||
|
private Long virtualSize;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PHYSICAL_SIZE)
|
||||||
|
@Param(description = "the bytes actually consumed on disk")
|
||||||
|
private Long physicalSize;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.UTILIZATION)
|
||||||
|
@Param(description = "the disk utilization")
|
||||||
|
private String utilization;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccountName(String accountName) {
|
||||||
|
this.accountName = accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProjectId(String projectId) {
|
||||||
|
this.projectId = projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProjectName(String projectName) {
|
||||||
|
this.projectName = projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDomainId(String domainId) {
|
||||||
|
this.domainId = domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDomainName(String domainName) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDomainPath(String domainPath) {
|
||||||
|
this.domainPath = domainPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZoneId(String zoneId) {
|
||||||
|
this.zoneId = zoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZoneName(String zoneName) {
|
||||||
|
this.zoneName = zoneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVirtualMachineId(String virtualMachineId) {
|
||||||
|
this.virtualMachineId = virtualMachineId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolumeId(String volumeId) {
|
||||||
|
this.volumeId = volumeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNetworkId(String networkId) {
|
||||||
|
this.networkId = networkId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNetworkName(String networkName) {
|
||||||
|
this.networkName = networkName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<NicResponse> getNics() {
|
||||||
|
return nics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNic(NicResponse nic) {
|
||||||
|
if (this.nics == null) {
|
||||||
|
this.nics = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.nics.add(nic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(Long size) {
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolumeName(String volumeName) {
|
||||||
|
this.volumeName = volumeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoragePoolName(String storagePoolName) {
|
||||||
|
this.storagePoolName = storagePoolName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoragePoolId(String storagePoolId) {
|
||||||
|
this.storagePoolId = storagePoolId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvider(String provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilesystem(String filesystem) {
|
||||||
|
this.filesystem = filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSizeGB(Long size) {
|
||||||
|
if (size != null) {
|
||||||
|
this.sizeGB = String.format("%.2f GiB", size / (1024.0 * 1024.0 * 1024.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskOfferingId(String diskOfferingId) {
|
||||||
|
this.diskOfferingId = diskOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskOfferingName(String diskOfferingName) {
|
||||||
|
this.diskOfferingName = diskOfferingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskOfferingDisplayText(String diskOfferingDisplayText) {
|
||||||
|
this.diskOfferingDisplayText = diskOfferingDisplayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceOfferingId(String serviceOfferingId) {
|
||||||
|
this.serviceOfferingId = serviceOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceOfferingName(String serviceOfferingName) {
|
||||||
|
this.serviceOfferingName = serviceOfferingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProvisioningType(String provisioningType) {
|
||||||
|
this.provisioningType = provisioningType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskIORead(Long diskIORead) {
|
||||||
|
this.diskIORead = diskIORead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskIOWrite(Long diskIOWrite) {
|
||||||
|
this.diskIOWrite = diskIOWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskKbsRead(Long diskKbsRead) {
|
||||||
|
this.diskKbsRead = diskKbsRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDiskKbsWrite(Long diskKbsWrite) {
|
||||||
|
this.diskKbsWrite = diskKbsWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVirtualSize(Long virtualSize) {
|
||||||
|
this.virtualSize = virtualSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhysicalSize(Long physicalSize) {
|
||||||
|
this.physicalSize = physicalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUtilization(String utilization) {
|
||||||
|
this.utilization = utilization;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsCustomDiskOffering(Boolean isCustomDiskOffering) {
|
||||||
|
this.isCustomDiskOffering = isCustomDiskOffering;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVirtualMachineState(String virtualMachineState) {
|
||||||
|
this.virtualMachineState = virtualMachineState;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -388,6 +388,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
|
|||||||
@Param(description = "VNF details", since = "4.19.0")
|
@Param(description = "VNF details", since = "4.19.0")
|
||||||
private Map<String, String> vnfDetails;
|
private Map<String, String> vnfDetails;
|
||||||
|
|
||||||
|
@SerializedName((ApiConstants.VM_TYPE))
|
||||||
|
@Param(description = "User VM type", since = "4.20.0")
|
||||||
|
private String vmType;
|
||||||
|
|
||||||
public UserVmResponse() {
|
public UserVmResponse() {
|
||||||
securityGroupList = new LinkedHashSet<>();
|
securityGroupList = new LinkedHashSet<>();
|
||||||
nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId())));
|
nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId())));
|
||||||
@ -1142,6 +1146,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
|
|||||||
this.vnfDetails.put(key,value);
|
this.vnfDetails.put(key,value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setVmType(String vmType) {
|
||||||
|
this.vmType = vmType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVmType() {
|
||||||
|
return vmType;
|
||||||
|
}
|
||||||
|
|
||||||
public void setIpAddress(String ipAddress) {
|
public void setIpAddress(String ipAddress) {
|
||||||
this.ipAddress = ipAddress;
|
this.ipAddress = ipAddress;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,7 +93,7 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co
|
|||||||
@Param(description = "display name of the virtual machine")
|
@Param(description = "display name of the virtual machine")
|
||||||
private String virtualMachineDisplayName;
|
private String virtualMachineDisplayName;
|
||||||
|
|
||||||
@SerializedName("vmstate")
|
@SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE)
|
||||||
@Param(description = "state of the virtual machine")
|
@Param(description = "state of the virtual machine")
|
||||||
private String virtualMachineState;
|
private String virtualMachineState;
|
||||||
|
|
||||||
@ -262,11 +262,11 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co
|
|||||||
private boolean supportsStorageSnapshot;
|
private boolean supportsStorageSnapshot;
|
||||||
|
|
||||||
@SerializedName(ApiConstants.PHYSICAL_SIZE)
|
@SerializedName(ApiConstants.PHYSICAL_SIZE)
|
||||||
@Param(description = "the bytes allocated")
|
@Param(description = "the bytes actually consumed on disk")
|
||||||
private Long physicalsize;
|
private Long physicalsize;
|
||||||
|
|
||||||
@SerializedName(ApiConstants.VIRTUAL_SIZE)
|
@SerializedName(ApiConstants.VIRTUAL_SIZE)
|
||||||
@Param(description = "the bytes actually consumed on disk")
|
@Param(description = "the bytes allocated")
|
||||||
private Long virtualsize;
|
private Long virtualsize;
|
||||||
|
|
||||||
@SerializedName(ApiConstants.UTILIZATION)
|
@SerializedName(ApiConstants.UTILIZATION)
|
||||||
|
|||||||
@ -0,0 +1,189 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
|
import com.cloud.utils.fsm.StateObject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public interface SharedFS extends ControlledEntity, Identity, InternalIdentity, StateObject<SharedFS.State> {
|
||||||
|
|
||||||
|
static final ConfigKey<Boolean> SharedFSFeatureEnabled = new ConfigKey<Boolean>("Advanced", Boolean.class,
|
||||||
|
"sharedfs.feature.enabled",
|
||||||
|
"true",
|
||||||
|
" Indicates whether the Shared FileSystem feature is enabled or not. Management server restart needed on change",
|
||||||
|
false);
|
||||||
|
|
||||||
|
ConfigKey<Integer> SharedFSCleanupInterval = new ConfigKey<>(Integer.class,
|
||||||
|
"sharedfs.cleanup.interval",
|
||||||
|
"Advanced",
|
||||||
|
"14400",
|
||||||
|
"The interval (in seconds) to wait before running the shared filesystem cleanup thread.",
|
||||||
|
false,
|
||||||
|
ConfigKey.Scope.Global,
|
||||||
|
null,
|
||||||
|
SharedFSFeatureEnabled.key());
|
||||||
|
|
||||||
|
ConfigKey<Integer> SharedFSCleanupDelay = new ConfigKey<>(Integer.class,
|
||||||
|
"sharedfs.cleanup.delay",
|
||||||
|
"Advanced",
|
||||||
|
"86400",
|
||||||
|
"Determines how long (in seconds) to wait before actually expunging destroyed shared filesystem.",
|
||||||
|
false,
|
||||||
|
ConfigKey.Scope.Global,
|
||||||
|
null,
|
||||||
|
SharedFSFeatureEnabled.key());
|
||||||
|
|
||||||
|
ConfigKey<Integer> SharedFSExpungeWorkers = new ConfigKey<>(Integer.class,
|
||||||
|
"sharedfs.expunge.workers",
|
||||||
|
"Advanced",
|
||||||
|
"2",
|
||||||
|
"Determines how many threads are created to do the work of expunging destroyed shared filesystem.",
|
||||||
|
false,
|
||||||
|
ConfigKey.Scope.Global,
|
||||||
|
null,
|
||||||
|
SharedFSFeatureEnabled.key());
|
||||||
|
|
||||||
|
String SharedFSVmNamePrefix = "fsvm";
|
||||||
|
String SharedFSPath = "/export";
|
||||||
|
|
||||||
|
enum FileSystemType {
|
||||||
|
EXT4, XFS
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Protocol {
|
||||||
|
NFS
|
||||||
|
}
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Allocated(false, "The shared filesystem is allocated in db but hasn't been created or started yet."),
|
||||||
|
Ready(false, "The shared filesystem is ready to use."),
|
||||||
|
Stopping(true, "The shared filesystem is being stopped"),
|
||||||
|
Stopped(false, "The shared filesystem is in stopped state. It can not be used but the data is still there."),
|
||||||
|
Starting(true, "The shared filesystem is being started."),
|
||||||
|
Destroyed(false, "The shared filesystem is destroyed."),
|
||||||
|
Expunging(true, "The shared filesystem is being expunged."),
|
||||||
|
Expunged(false, "The shared filesystem has been expunged."),
|
||||||
|
Error(false, "The shared filesystem is in error state.");
|
||||||
|
|
||||||
|
boolean _transitional;
|
||||||
|
String _description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SharedFS State
|
||||||
|
*
|
||||||
|
* @param transitional true for transition/non-final state, otherwise false
|
||||||
|
* @param description description of the state
|
||||||
|
*/
|
||||||
|
State(boolean transitional, String description) {
|
||||||
|
_transitional = transitional;
|
||||||
|
_description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTransitional() {
|
||||||
|
return _transitional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return _description;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static StateMachine2<State, Event, SharedFS> s_fsm = new StateMachine2<State, Event, SharedFS>();
|
||||||
|
|
||||||
|
public static StateMachine2<SharedFS.State, SharedFS.Event, SharedFS> getStateMachine() {
|
||||||
|
return s_fsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.OperationFailed, Error, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.OperationSucceeded, Ready, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Error, Event.DestroyRequested, Destroyed, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Stopped, Event.StartRequested, Starting, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Starting, Event.OperationSucceeded, Ready, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Starting, Event.OperationFailed, Stopped, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Ready, Event.StopRequested, Stopping, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Stopping, Event.OperationSucceeded, Stopped, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Stopping, Event.OperationFailed, Ready, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Stopped, Event.DestroyRequested, Destroyed, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Destroyed, Event.RecoveryRequested, Stopped, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Destroyed, Event.ExpungeOperation, Expunging, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Error, Event.ExpungeOperation, Expunging, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Expunging, Event.ExpungeOperation, Expunging, null));
|
||||||
|
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Expunging, Event.OperationSucceeded, Expunged, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Event {
|
||||||
|
StopRequested,
|
||||||
|
StartRequested,
|
||||||
|
DestroyRequested,
|
||||||
|
OperationSucceeded,
|
||||||
|
OperationFailed,
|
||||||
|
ExpungeOperation,
|
||||||
|
RecoveryRequested,
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getSharedFSPath() {
|
||||||
|
return SharedFSPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getId();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
void setName(String name);
|
||||||
|
|
||||||
|
String getUuid();
|
||||||
|
|
||||||
|
String getDescription();
|
||||||
|
|
||||||
|
void setDescription(String description);
|
||||||
|
|
||||||
|
Long getDataCenterId();
|
||||||
|
|
||||||
|
State getState();
|
||||||
|
|
||||||
|
String getFsProviderName();
|
||||||
|
|
||||||
|
Protocol getProtocol();
|
||||||
|
|
||||||
|
Long getVolumeId();
|
||||||
|
|
||||||
|
void setVolumeId(Long volumeId);
|
||||||
|
|
||||||
|
Long getVmId();
|
||||||
|
|
||||||
|
void setVmId(Long vmId);
|
||||||
|
|
||||||
|
FileSystemType getFsType();
|
||||||
|
|
||||||
|
Long getServiceOfferingId();
|
||||||
|
|
||||||
|
void setServiceOfferingId(Long serviceOfferingId);
|
||||||
|
|
||||||
|
Date getUpdated();
|
||||||
|
|
||||||
|
public long getUpdatedCount();
|
||||||
|
|
||||||
|
public void incrUpdatedCount();
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import com.cloud.dc.DataCenter;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.ManagementServerException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.exception.VirtualMachineMigrationException;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
|
||||||
|
public interface SharedFSLifeCycle {
|
||||||
|
void checkPrerequisites(DataCenter zone, Long serviceOfferingId);
|
||||||
|
|
||||||
|
Pair<Long, Long> deploySharedFS(SharedFS sharedFS, Long networkId, Long diskOfferingId, Long size, Long minIops, Long maxIops) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException;
|
||||||
|
|
||||||
|
void startSharedFS(SharedFS sharedFS) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException;
|
||||||
|
|
||||||
|
boolean stopSharedFS(SharedFS sharedFS, Boolean forced);
|
||||||
|
|
||||||
|
boolean deleteSharedFS(SharedFS sharedFS);
|
||||||
|
|
||||||
|
boolean reDeploySharedFS(SharedFS sharedFS) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException;
|
||||||
|
|
||||||
|
boolean changeSharedFSServiceOffering(SharedFS sharedFS, Long serviceOfferingId) throws ManagementServerException, ResourceUnavailableException, VirtualMachineMigrationException;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.Adapter;
|
||||||
|
|
||||||
|
public interface SharedFSProvider extends Adapter {
|
||||||
|
|
||||||
|
enum SharedFSProviderType {
|
||||||
|
SHAREDFSVM
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure();
|
||||||
|
|
||||||
|
SharedFSLifeCycle getSharedFSLifeCycle();
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSDiskOfferingCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSServiceOfferingCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.CreateSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.DestroySharedFSCmd;
|
||||||
|
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.ManagementServerException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.exception.VirtualMachineMigrationException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ListSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.UpdateSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
|
||||||
|
public interface SharedFSService {
|
||||||
|
|
||||||
|
List<SharedFSProvider> getSharedFSProviders();
|
||||||
|
|
||||||
|
boolean stateTransitTo(SharedFS sharedFS, SharedFS.Event event);
|
||||||
|
|
||||||
|
void setSharedFSProviders(List<SharedFSProvider> sharedFSProviders);
|
||||||
|
|
||||||
|
SharedFSProvider getSharedFSProvider(String sharedFSProviderName);
|
||||||
|
|
||||||
|
SharedFS allocSharedFS(CreateSharedFSCmd cmd);
|
||||||
|
|
||||||
|
SharedFS deploySharedFS(CreateSharedFSCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException;
|
||||||
|
|
||||||
|
SharedFS startSharedFS(Long sharedFSId) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException;
|
||||||
|
|
||||||
|
SharedFS stopSharedFS(Long sharedFSId, Boolean forced);
|
||||||
|
|
||||||
|
SharedFS restartSharedFS(Long sharedFSId, boolean cleanup) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException;
|
||||||
|
|
||||||
|
ListResponse<SharedFSResponse> searchForSharedFS(ResponseObject.ResponseView respView, ListSharedFSCmd cmd);
|
||||||
|
|
||||||
|
SharedFS updateSharedFS(UpdateSharedFSCmd cmd);
|
||||||
|
|
||||||
|
SharedFS changeSharedFSDiskOffering(ChangeSharedFSDiskOfferingCmd cmd) throws ResourceAllocationException;
|
||||||
|
|
||||||
|
SharedFS changeSharedFSServiceOffering(ChangeSharedFSServiceOfferingCmd cmd) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ManagementServerException, VirtualMachineMigrationException;
|
||||||
|
|
||||||
|
Boolean destroySharedFS(DestroySharedFSCmd cmd);
|
||||||
|
|
||||||
|
SharedFS recoverSharedFS(Long sharedFSId);
|
||||||
|
|
||||||
|
void deleteSharedFS(Long sharedFSId);
|
||||||
|
}
|
||||||
@ -653,6 +653,11 @@
|
|||||||
<artifactId>cloud-plugin-storage-object-simulator</artifactId>
|
<artifactId>cloud-plugin-storage-object-simulator</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloud-plugin-sharedfs-provider-storagevm</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.cloudstack</groupId>
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
<artifactId>cloud-usage</artifactId>
|
<artifactId>cloud-usage</artifactId>
|
||||||
|
|||||||
@ -363,4 +363,7 @@
|
|||||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||||
<property name="excludeKey" value="event.buses.exclude" />
|
<property name="excludeKey" value="event.buses.exclude" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="sharedFSProvidersRegistry" class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||||
|
</bean>
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -77,6 +77,10 @@
|
|||||||
value="org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy" />
|
value="org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
|
||||||
|
<property name="registry" ref="sharedFSProvidersRegistry" />
|
||||||
|
<property name="typeClass"
|
||||||
|
value="org.apache.cloudstack.storage.sharedfs.SharedFSProvider" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage.sharedfs;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "shared_filesystem")
|
||||||
|
public class SharedFSVO implements SharedFS {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "domain_id")
|
||||||
|
private long domainId;
|
||||||
|
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private long accountId;
|
||||||
|
|
||||||
|
@Column(name = "data_center_id")
|
||||||
|
private long dataCenterId;
|
||||||
|
|
||||||
|
@Column(name = "state")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
@Column(name = "fs_provider_name")
|
||||||
|
private String fsProviderName;
|
||||||
|
|
||||||
|
@Column(name = "protocol")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private Protocol protocol;
|
||||||
|
|
||||||
|
@Column(name = "volume_id")
|
||||||
|
private Long volumeId;
|
||||||
|
|
||||||
|
@Column(name = "vm_id")
|
||||||
|
private Long vmId;
|
||||||
|
|
||||||
|
@Column(name = "fs_type")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private FileSystemType fsType;
|
||||||
|
|
||||||
|
@Column(name = "service_offering_id")
|
||||||
|
private Long serviceOfferingId;
|
||||||
|
|
||||||
|
@Column(name = "updated")
|
||||||
|
@Temporal(value = TemporalType.TIMESTAMP)
|
||||||
|
Date updated;
|
||||||
|
|
||||||
|
@Column(name = "update_count", updatable = true, nullable = false)
|
||||||
|
protected long updatedCount; // This field should be updated everytime the
|
||||||
|
// state is updated. There's no set method in
|
||||||
|
// the vo object because it is done with in the
|
||||||
|
// dao code.
|
||||||
|
|
||||||
|
@Column(name = GenericDao.CREATED_COLUMN)
|
||||||
|
protected Date created;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||||
|
protected Date removed;
|
||||||
|
|
||||||
|
public SharedFSVO() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedFSVO(String name, String description, long domainId, long accountId, long dataCenterId,
|
||||||
|
String fsProviderName, Protocol protocol, FileSystemType fsType, Long serviceOfferingId) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.domainId = domainId;
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.dataCenterId = dataCenterId;
|
||||||
|
this.fsProviderName = fsProviderName;
|
||||||
|
this.protocol = protocol;
|
||||||
|
this.state = State.Allocated;
|
||||||
|
this.fsType = fsType;
|
||||||
|
this.serviceOfferingId = serviceOfferingId;
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getEntityType() {
|
||||||
|
return SharedFS.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDomainId() {
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getAccountId() {
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getDataCenterId() {
|
||||||
|
return dataCenterId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFsProviderName() {
|
||||||
|
return fsProviderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Protocol getProtocol() {
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getVolumeId() {
|
||||||
|
return volumeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVolumeId(Long volumeId) {
|
||||||
|
this.volumeId = volumeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getVmId() {
|
||||||
|
return vmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVmId(Long vmId) {
|
||||||
|
this.vmId = vmId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileSystemType getFsType() {
|
||||||
|
return fsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getServiceOfferingId() {
|
||||||
|
return serviceOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setServiceOfferingId(Long serviceOfferingId) {
|
||||||
|
this.serviceOfferingId = serviceOfferingId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getUpdated() {
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUpdatedCount() {
|
||||||
|
return updatedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrUpdatedCount() {
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
// 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.storage.sharedfs.dao;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
import com.cloud.utils.fsm.StateDao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface SharedFSDao extends GenericDao<SharedFSVO, Long>, StateDao<SharedFS.State, SharedFS.Event, SharedFS> {
|
||||||
|
List<SharedFSVO> listSharedFSToBeDestroyed(Date date);
|
||||||
|
|
||||||
|
SharedFSVO findSharedFSByNameAccountDomain(String name, Long accountId, Long domainId);
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
// 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.storage.sharedfs.dao;
|
||||||
|
|
||||||
|
import com.cloud.network.dao.NetworkDao;
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.utils.db.UpdateBuilder;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSVO;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SharedFSDaoImpl extends GenericDaoBase<SharedFSVO, Long> implements SharedFSDao {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
VMNetworkMapDao vmNetworkMapDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
NetworkDao networkDao;
|
||||||
|
|
||||||
|
protected final SearchBuilder<SharedFSVO> StateUpdateCountSearch;
|
||||||
|
|
||||||
|
protected final SearchBuilder<SharedFSVO> DestroyedByTimeSearch;
|
||||||
|
|
||||||
|
protected final SearchBuilder<SharedFSVO> NameAccountDomainSearch;
|
||||||
|
|
||||||
|
public SharedFSDaoImpl() {
|
||||||
|
StateUpdateCountSearch = createSearchBuilder();
|
||||||
|
StateUpdateCountSearch.and("id", StateUpdateCountSearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
StateUpdateCountSearch.and("state", StateUpdateCountSearch.entity().getState(), SearchCriteria.Op.EQ);
|
||||||
|
StateUpdateCountSearch.and("updatedCount", StateUpdateCountSearch.entity().getUpdatedCount(), SearchCriteria.Op.EQ);
|
||||||
|
StateUpdateCountSearch.done();
|
||||||
|
|
||||||
|
DestroyedByTimeSearch = createSearchBuilder();
|
||||||
|
DestroyedByTimeSearch.and("state", DestroyedByTimeSearch.entity().getState(), SearchCriteria.Op.IN);
|
||||||
|
DestroyedByTimeSearch.and("accountId", DestroyedByTimeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
DestroyedByTimeSearch.done();
|
||||||
|
|
||||||
|
NameAccountDomainSearch = createSearchBuilder();
|
||||||
|
NameAccountDomainSearch.and("name", NameAccountDomainSearch.entity().getName(), SearchCriteria.Op.EQ);
|
||||||
|
NameAccountDomainSearch.and("accountId", NameAccountDomainSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
NameAccountDomainSearch.and("domainId", NameAccountDomainSearch.entity().getDomainId(), SearchCriteria.Op.EQ);
|
||||||
|
NameAccountDomainSearch.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateState(SharedFS.State currentState, SharedFS.Event event, SharedFS.State nextState, SharedFS vo, Object data) {
|
||||||
|
|
||||||
|
Long oldUpdated = vo.getUpdatedCount();
|
||||||
|
Date oldUpdatedTime = vo.getUpdated();
|
||||||
|
|
||||||
|
SearchCriteria<SharedFSVO> sc = StateUpdateCountSearch.create();
|
||||||
|
sc.setParameters("id", vo.getId());
|
||||||
|
sc.setParameters("state", currentState);
|
||||||
|
sc.setParameters("updatedCount", vo.getUpdatedCount());
|
||||||
|
|
||||||
|
vo.incrUpdatedCount();
|
||||||
|
|
||||||
|
UpdateBuilder builder = getUpdateBuilder(vo);
|
||||||
|
builder.set(vo, "state", nextState);
|
||||||
|
builder.set(vo, "updated", new Date());
|
||||||
|
|
||||||
|
int rows = update((SharedFSVO) vo, sc);
|
||||||
|
if (rows == 0 && logger.isDebugEnabled()) {
|
||||||
|
SharedFSVO dbSharedFS = findByIdIncludingRemoved(vo.getId());
|
||||||
|
if (dbSharedFS != null) {
|
||||||
|
StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString());
|
||||||
|
str.append(": DB Data={id=").append(dbSharedFS.getId()).append("; state=").append(dbSharedFS.getState()).append("; updatecount=").append(dbSharedFS.getUpdatedCount()).append(";updatedTime=")
|
||||||
|
.append(dbSharedFS.getUpdated());
|
||||||
|
str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatecount=").append(vo.getUpdatedCount())
|
||||||
|
.append("; updatedTime=").append(vo.getUpdated());
|
||||||
|
str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatecount=").append(oldUpdated)
|
||||||
|
.append("; updatedTime=").append(oldUpdatedTime);
|
||||||
|
} else {
|
||||||
|
logger.debug("Unable to update sharedfs: id=" + vo.getId() + ", as it is not present in the database anymore");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SharedFSVO> listSharedFSToBeDestroyed(Date date) {
|
||||||
|
SearchCriteria<SharedFSVO> sc = DestroyedByTimeSearch.create();
|
||||||
|
sc.setParameters("state", SharedFS.State.Destroyed, SharedFS.State.Expunging, SharedFS.State.Error);
|
||||||
|
sc.setParameters("updateTime", date);
|
||||||
|
return listBy(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedFSVO findSharedFSByNameAccountDomain(String name, Long accountId, Long domainId) {
|
||||||
|
SearchCriteria<SharedFSVO> sc = NameAccountDomainSearch.create();
|
||||||
|
sc.setParameters("name", name);
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
sc.setParameters("domainId", domainId);
|
||||||
|
return findOneBy(sc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -289,4 +289,6 @@
|
|||||||
<bean id="ClusterDrsPlanDetailsDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanMigrationDaoImpl" />
|
<bean id="ClusterDrsPlanDetailsDaoImpl" class="org.apache.cloudstack.cluster.dao.ClusterDrsPlanMigrationDaoImpl" />
|
||||||
<bean id="objectStoreDetailsDaoImpl" class="org.apache.cloudstack.storage.datastore.db.ObjectStoreDetailsDaoImpl" />
|
<bean id="objectStoreDetailsDaoImpl" class="org.apache.cloudstack.storage.datastore.db.ObjectStoreDetailsDaoImpl" />
|
||||||
<bean id="bucketStatisticsDaoImpl" class="com.cloud.usage.dao.BucketStatisticsDaoImpl" />
|
<bean id="bucketStatisticsDaoImpl" class="com.cloud.usage.dao.BucketStatisticsDaoImpl" />
|
||||||
|
<bean id="SharedFSDaoImpl" class="org.apache.cloudstack.storage.sharedfs.dao.SharedFSDaoImpl" />
|
||||||
|
<bean id="SharedFSJoinDaoImpl" class="org.apache.cloudstack.storage.sharedfs.query.dao.SharedFSJoinDaoImpl" />
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -158,6 +158,229 @@ WHERE
|
|||||||
name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth")
|
name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth")
|
||||||
AND value NOT IN ("true", "y", "t", "1", "on", "yes");
|
AND value NOT IN ("true", "y", "t", "1", "on", "yes");
|
||||||
|
|
||||||
|
CREATE TABLE `cloud`.`shared_filesystem`(
|
||||||
|
`id` bigint unsigned NOT NULL auto_increment COMMENT 'ID',
|
||||||
|
`uuid` varchar(40) COMMENT 'UUID',
|
||||||
|
`name` varchar(255) NOT NULL COMMENT 'Name of the shared filesystem',
|
||||||
|
`description` varchar(1024) COMMENT 'Description',
|
||||||
|
`domain_id` bigint unsigned NOT NULL COMMENT 'Domain ID',
|
||||||
|
`account_id` bigint unsigned NOT NULL COMMENT 'Account ID',
|
||||||
|
`data_center_id` bigint unsigned NOT NULL COMMENT 'Data center ID',
|
||||||
|
`state` varchar(12) NOT NULL COMMENT 'State of the shared filesystem in the FSM',
|
||||||
|
`fs_provider_name` varchar(255) COMMENT 'Name of the shared filesystem provider',
|
||||||
|
`protocol` varchar(10) COMMENT 'Protocol supported by the shared filesystem',
|
||||||
|
`volume_id` bigint unsigned COMMENT 'Volume which the shared filesystem is using as storage',
|
||||||
|
`vm_id` bigint unsigned COMMENT 'vm on which the shared filesystem is hosted',
|
||||||
|
`fs_type` varchar(10) NOT NULL COMMENT 'The filesystem format to be used for the shared filesystem',
|
||||||
|
`service_offering_id` bigint unsigned COMMENT 'Service offering for the vm',
|
||||||
|
`update_count` bigint unsigned COMMENT 'Update count for state change',
|
||||||
|
`updated` datetime COMMENT 'date updated',
|
||||||
|
`created` datetime NOT NULL COMMENT 'date created',
|
||||||
|
`removed` datetime COMMENT 'date removed if not null',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
CONSTRAINT `uc_shared_filesystem__uuid` UNIQUE (`uuid`),
|
||||||
|
INDEX `i_shared_filesystem__account_id`(`account_id`),
|
||||||
|
INDEX `i_shared_filesystem__domain_id`(`domain_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS `cloud`.`user_vm_view`;
|
||||||
|
CREATE VIEW `user_vm_view` AS
|
||||||
|
SELECT
|
||||||
|
`vm_instance`.`id` AS `id`,
|
||||||
|
`vm_instance`.`name` AS `name`,
|
||||||
|
`user_vm`.`display_name` AS `display_name`,
|
||||||
|
`user_vm`.`user_data` AS `user_data`,
|
||||||
|
`user_vm`.`user_vm_type` AS `user_vm_type`,
|
||||||
|
`account`.`id` AS `account_id`,
|
||||||
|
`account`.`uuid` AS `account_uuid`,
|
||||||
|
`account`.`account_name` AS `account_name`,
|
||||||
|
`account`.`type` AS `account_type`,
|
||||||
|
`domain`.`id` AS `domain_id`,
|
||||||
|
`domain`.`uuid` AS `domain_uuid`,
|
||||||
|
`domain`.`name` AS `domain_name`,
|
||||||
|
`domain`.`path` AS `domain_path`,
|
||||||
|
`projects`.`id` AS `project_id`,
|
||||||
|
`projects`.`uuid` AS `project_uuid`,
|
||||||
|
`projects`.`name` AS `project_name`,
|
||||||
|
`instance_group`.`id` AS `instance_group_id`,
|
||||||
|
`instance_group`.`uuid` AS `instance_group_uuid`,
|
||||||
|
`instance_group`.`name` AS `instance_group_name`,
|
||||||
|
`vm_instance`.`uuid` AS `uuid`,
|
||||||
|
`vm_instance`.`user_id` AS `user_id`,
|
||||||
|
`vm_instance`.`last_host_id` AS `last_host_id`,
|
||||||
|
`vm_instance`.`vm_type` AS `type`,
|
||||||
|
`vm_instance`.`limit_cpu_use` AS `limit_cpu_use`,
|
||||||
|
`vm_instance`.`created` AS `created`,
|
||||||
|
`vm_instance`.`state` AS `state`,
|
||||||
|
`vm_instance`.`update_time` AS `update_time`,
|
||||||
|
`vm_instance`.`removed` AS `removed`,
|
||||||
|
`vm_instance`.`ha_enabled` AS `ha_enabled`,
|
||||||
|
`vm_instance`.`hypervisor_type` AS `hypervisor_type`,
|
||||||
|
`vm_instance`.`instance_name` AS `instance_name`,
|
||||||
|
`vm_instance`.`guest_os_id` AS `guest_os_id`,
|
||||||
|
`vm_instance`.`display_vm` AS `display_vm`,
|
||||||
|
`guest_os`.`uuid` AS `guest_os_uuid`,
|
||||||
|
`vm_instance`.`pod_id` AS `pod_id`,
|
||||||
|
`host_pod_ref`.`uuid` AS `pod_uuid`,
|
||||||
|
`vm_instance`.`private_ip_address` AS `private_ip_address`,
|
||||||
|
`vm_instance`.`private_mac_address` AS `private_mac_address`,
|
||||||
|
`vm_instance`.`vm_type` AS `vm_type`,
|
||||||
|
`data_center`.`id` AS `data_center_id`,
|
||||||
|
`data_center`.`uuid` AS `data_center_uuid`,
|
||||||
|
`data_center`.`name` AS `data_center_name`,
|
||||||
|
`data_center`.`is_security_group_enabled` AS `security_group_enabled`,
|
||||||
|
`data_center`.`networktype` AS `data_center_network_type`,
|
||||||
|
`host`.`id` AS `host_id`,
|
||||||
|
`host`.`uuid` AS `host_uuid`,
|
||||||
|
`host`.`name` AS `host_name`,
|
||||||
|
`host`.`cluster_id` AS `cluster_id`,
|
||||||
|
`host`.`status` AS `host_status`,
|
||||||
|
`host`.`resource_state` AS `host_resource_state`,
|
||||||
|
`vm_template`.`id` AS `template_id`,
|
||||||
|
`vm_template`.`uuid` AS `template_uuid`,
|
||||||
|
`vm_template`.`name` AS `template_name`,
|
||||||
|
`vm_template`.`type` AS `template_type`,
|
||||||
|
`vm_template`.`format` AS `template_format`,
|
||||||
|
`vm_template`.`display_text` AS `template_display_text`,
|
||||||
|
`vm_template`.`enable_password` AS `password_enabled`,
|
||||||
|
`iso`.`id` AS `iso_id`,
|
||||||
|
`iso`.`uuid` AS `iso_uuid`,
|
||||||
|
`iso`.`name` AS `iso_name`,
|
||||||
|
`iso`.`display_text` AS `iso_display_text`,
|
||||||
|
`service_offering`.`id` AS `service_offering_id`,
|
||||||
|
`service_offering`.`uuid` AS `service_offering_uuid`,
|
||||||
|
`disk_offering`.`uuid` AS `disk_offering_uuid`,
|
||||||
|
`disk_offering`.`id` AS `disk_offering_id`,
|
||||||
|
(CASE
|
||||||
|
WHEN ISNULL(`service_offering`.`cpu`) THEN `custom_cpu`.`value`
|
||||||
|
ELSE `service_offering`.`cpu`
|
||||||
|
END) AS `cpu`,
|
||||||
|
(CASE
|
||||||
|
WHEN ISNULL(`service_offering`.`speed`) THEN `custom_speed`.`value`
|
||||||
|
ELSE `service_offering`.`speed`
|
||||||
|
END) AS `speed`,
|
||||||
|
(CASE
|
||||||
|
WHEN ISNULL(`service_offering`.`ram_size`) THEN `custom_ram_size`.`value`
|
||||||
|
ELSE `service_offering`.`ram_size`
|
||||||
|
END) AS `ram_size`,
|
||||||
|
`backup_offering`.`uuid` AS `backup_offering_uuid`,
|
||||||
|
`backup_offering`.`id` AS `backup_offering_id`,
|
||||||
|
`service_offering`.`name` AS `service_offering_name`,
|
||||||
|
`disk_offering`.`name` AS `disk_offering_name`,
|
||||||
|
`backup_offering`.`name` AS `backup_offering_name`,
|
||||||
|
`storage_pool`.`id` AS `pool_id`,
|
||||||
|
`storage_pool`.`uuid` AS `pool_uuid`,
|
||||||
|
`storage_pool`.`pool_type` AS `pool_type`,
|
||||||
|
`volumes`.`id` AS `volume_id`,
|
||||||
|
`volumes`.`uuid` AS `volume_uuid`,
|
||||||
|
`volumes`.`device_id` AS `volume_device_id`,
|
||||||
|
`volumes`.`volume_type` AS `volume_type`,
|
||||||
|
`security_group`.`id` AS `security_group_id`,
|
||||||
|
`security_group`.`uuid` AS `security_group_uuid`,
|
||||||
|
`security_group`.`name` AS `security_group_name`,
|
||||||
|
`security_group`.`description` AS `security_group_description`,
|
||||||
|
`nics`.`id` AS `nic_id`,
|
||||||
|
`nics`.`uuid` AS `nic_uuid`,
|
||||||
|
`nics`.`device_id` AS `nic_device_id`,
|
||||||
|
`nics`.`network_id` AS `network_id`,
|
||||||
|
`nics`.`ip4_address` AS `ip_address`,
|
||||||
|
`nics`.`ip6_address` AS `ip6_address`,
|
||||||
|
`nics`.`ip6_gateway` AS `ip6_gateway`,
|
||||||
|
`nics`.`ip6_cidr` AS `ip6_cidr`,
|
||||||
|
`nics`.`default_nic` AS `is_default_nic`,
|
||||||
|
`nics`.`gateway` AS `gateway`,
|
||||||
|
`nics`.`netmask` AS `netmask`,
|
||||||
|
`nics`.`mac_address` AS `mac_address`,
|
||||||
|
`nics`.`broadcast_uri` AS `broadcast_uri`,
|
||||||
|
`nics`.`isolation_uri` AS `isolation_uri`,
|
||||||
|
`vpc`.`id` AS `vpc_id`,
|
||||||
|
`vpc`.`uuid` AS `vpc_uuid`,
|
||||||
|
`networks`.`uuid` AS `network_uuid`,
|
||||||
|
`networks`.`name` AS `network_name`,
|
||||||
|
`networks`.`traffic_type` AS `traffic_type`,
|
||||||
|
`networks`.`guest_type` AS `guest_type`,
|
||||||
|
`user_ip_address`.`id` AS `public_ip_id`,
|
||||||
|
`user_ip_address`.`uuid` AS `public_ip_uuid`,
|
||||||
|
`user_ip_address`.`public_ip_address` AS `public_ip_address`,
|
||||||
|
`ssh_details`.`value` AS `keypair_names`,
|
||||||
|
`resource_tags`.`id` AS `tag_id`,
|
||||||
|
`resource_tags`.`uuid` AS `tag_uuid`,
|
||||||
|
`resource_tags`.`key` AS `tag_key`,
|
||||||
|
`resource_tags`.`value` AS `tag_value`,
|
||||||
|
`resource_tags`.`domain_id` AS `tag_domain_id`,
|
||||||
|
`domain`.`uuid` AS `tag_domain_uuid`,
|
||||||
|
`domain`.`name` AS `tag_domain_name`,
|
||||||
|
`resource_tags`.`account_id` AS `tag_account_id`,
|
||||||
|
`account`.`account_name` AS `tag_account_name`,
|
||||||
|
`resource_tags`.`resource_id` AS `tag_resource_id`,
|
||||||
|
`resource_tags`.`resource_uuid` AS `tag_resource_uuid`,
|
||||||
|
`resource_tags`.`resource_type` AS `tag_resource_type`,
|
||||||
|
`resource_tags`.`customer` AS `tag_customer`,
|
||||||
|
`async_job`.`id` AS `job_id`,
|
||||||
|
`async_job`.`uuid` AS `job_uuid`,
|
||||||
|
`async_job`.`job_status` AS `job_status`,
|
||||||
|
`async_job`.`account_id` AS `job_account_id`,
|
||||||
|
`affinity_group`.`id` AS `affinity_group_id`,
|
||||||
|
`affinity_group`.`uuid` AS `affinity_group_uuid`,
|
||||||
|
`affinity_group`.`name` AS `affinity_group_name`,
|
||||||
|
`affinity_group`.`description` AS `affinity_group_description`,
|
||||||
|
`autoscale_vmgroups`.`id` AS `autoscale_vmgroup_id`,
|
||||||
|
`autoscale_vmgroups`.`uuid` AS `autoscale_vmgroup_uuid`,
|
||||||
|
`autoscale_vmgroups`.`name` AS `autoscale_vmgroup_name`,
|
||||||
|
`vm_instance`.`dynamically_scalable` AS `dynamically_scalable`,
|
||||||
|
`user_data`.`id` AS `user_data_id`,
|
||||||
|
`user_data`.`uuid` AS `user_data_uuid`,
|
||||||
|
`user_data`.`name` AS `user_data_name`,
|
||||||
|
`user_vm`.`user_data_details` AS `user_data_details`,
|
||||||
|
`vm_template`.`user_data_link_policy` AS `user_data_policy`
|
||||||
|
FROM
|
||||||
|
(((((((((((((((((((((((((((((((((((`user_vm`
|
||||||
|
JOIN `vm_instance` ON (((`vm_instance`.`id` = `user_vm`.`id`)
|
||||||
|
AND ISNULL(`vm_instance`.`removed`))))
|
||||||
|
JOIN `account` ON ((`vm_instance`.`account_id` = `account`.`id`)))
|
||||||
|
JOIN `domain` ON ((`vm_instance`.`domain_id` = `domain`.`id`)))
|
||||||
|
LEFT JOIN `guest_os` ON ((`vm_instance`.`guest_os_id` = `guest_os`.`id`)))
|
||||||
|
LEFT JOIN `host_pod_ref` ON ((`vm_instance`.`pod_id` = `host_pod_ref`.`id`)))
|
||||||
|
LEFT JOIN `projects` ON ((`projects`.`project_account_id` = `account`.`id`)))
|
||||||
|
LEFT JOIN `instance_group_vm_map` ON ((`vm_instance`.`id` = `instance_group_vm_map`.`instance_id`)))
|
||||||
|
LEFT JOIN `instance_group` ON ((`instance_group_vm_map`.`group_id` = `instance_group`.`id`)))
|
||||||
|
LEFT JOIN `data_center` ON ((`vm_instance`.`data_center_id` = `data_center`.`id`)))
|
||||||
|
LEFT JOIN `host` ON ((`vm_instance`.`host_id` = `host`.`id`)))
|
||||||
|
LEFT JOIN `vm_template` ON ((`vm_instance`.`vm_template_id` = `vm_template`.`id`)))
|
||||||
|
LEFT JOIN `vm_template` `iso` ON ((`iso`.`id` = `user_vm`.`iso_id`)))
|
||||||
|
LEFT JOIN `volumes` ON ((`vm_instance`.`id` = `volumes`.`instance_id`)))
|
||||||
|
LEFT JOIN `service_offering` ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`)))
|
||||||
|
LEFT JOIN `disk_offering` `svc_disk_offering` ON ((`volumes`.`disk_offering_id` = `svc_disk_offering`.`id`)))
|
||||||
|
LEFT JOIN `disk_offering` ON ((`volumes`.`disk_offering_id` = `disk_offering`.`id`)))
|
||||||
|
LEFT JOIN `backup_offering` ON ((`vm_instance`.`backup_offering_id` = `backup_offering`.`id`)))
|
||||||
|
LEFT JOIN `storage_pool` ON ((`volumes`.`pool_id` = `storage_pool`.`id`)))
|
||||||
|
LEFT JOIN `security_group_vm_map` ON ((`vm_instance`.`id` = `security_group_vm_map`.`instance_id`)))
|
||||||
|
LEFT JOIN `security_group` ON ((`security_group_vm_map`.`security_group_id` = `security_group`.`id`)))
|
||||||
|
LEFT JOIN `user_data` ON ((`user_data`.`id` = `user_vm`.`user_data_id`)))
|
||||||
|
LEFT JOIN `nics` ON (((`vm_instance`.`id` = `nics`.`instance_id`)
|
||||||
|
AND ISNULL(`nics`.`removed`))))
|
||||||
|
LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`)))
|
||||||
|
LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`)
|
||||||
|
AND ISNULL(`vpc`.`removed`))))
|
||||||
|
LEFT JOIN `user_ip_address` FORCE INDEX(`fk_user_ip_address__vm_id`) ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`)))
|
||||||
|
LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`)
|
||||||
|
AND (`ssh_details`.`name` = 'SSH.KeyPairNames'))))
|
||||||
|
LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`)
|
||||||
|
AND (`resource_tags`.`resource_type` = 'UserVm'))))
|
||||||
|
LEFT JOIN `async_job` ON (((`async_job`.`instance_id` = `vm_instance`.`id`)
|
||||||
|
AND (`async_job`.`instance_type` = 'VirtualMachine')
|
||||||
|
AND (`async_job`.`job_status` = 0))))
|
||||||
|
LEFT JOIN `affinity_group_vm_map` ON ((`vm_instance`.`id` = `affinity_group_vm_map`.`instance_id`)))
|
||||||
|
LEFT JOIN `affinity_group` ON ((`affinity_group_vm_map`.`affinity_group_id` = `affinity_group`.`id`)))
|
||||||
|
LEFT JOIN `autoscale_vmgroup_vm_map` ON ((`autoscale_vmgroup_vm_map`.`instance_id` = `vm_instance`.`id`)))
|
||||||
|
LEFT JOIN `autoscale_vmgroups` ON ((`autoscale_vmgroup_vm_map`.`vmgroup_id` = `autoscale_vmgroups`.`id`)))
|
||||||
|
LEFT JOIN `user_vm_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`)
|
||||||
|
AND (`custom_cpu`.`name` = 'CpuNumber'))))
|
||||||
|
LEFT JOIN `user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`)
|
||||||
|
AND (`custom_speed`.`name` = 'CpuSpeed'))))
|
||||||
|
LEFT JOIN `user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`)
|
||||||
|
AND (`custom_ram_size`.`name` = 'memory'))));
|
||||||
|
|
||||||
-- Quota inject tariff result into subsequent ones
|
-- Quota inject tariff result into subsequent ones
|
||||||
CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.quota_tariff', 'position', 'bigint(20) NOT NULL DEFAULT 1 COMMENT "Position in the execution sequence for tariffs of the same type"');
|
CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.quota_tariff', 'position', 'bigint(20) NOT NULL DEFAULT 1 COMMENT "Position in the execution sequence for tariffs of the same type"');
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
-- 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.
|
||||||
|
|
||||||
|
-- VIEW `cloud`.`shared_filesystem_view`;
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS `cloud`.`shared_filesystem_view`;
|
||||||
|
|
||||||
|
CREATE VIEW `cloud`.`shared_filesystem_view` AS
|
||||||
|
SELECT
|
||||||
|
`shared_filesystem`.`id` AS `id`,
|
||||||
|
`shared_filesystem`.`uuid` AS `uuid`,
|
||||||
|
`shared_filesystem`.`name` AS `name`,
|
||||||
|
`shared_filesystem`.`description` AS `description`,
|
||||||
|
`shared_filesystem`.`state` AS `state`,
|
||||||
|
`shared_filesystem`.`fs_provider_name` AS `provider`,
|
||||||
|
`shared_filesystem`.`fs_type` AS `fs_type`,
|
||||||
|
`shared_filesystem`.`volume_id` AS `volume_id`,
|
||||||
|
`shared_filesystem`.`account_id` AS `account_id`,
|
||||||
|
`shared_filesystem`.`data_center_id` AS `zone_id`,
|
||||||
|
`zone`.`uuid` AS `zone_uuid`,
|
||||||
|
`zone`.`name` AS `zone_name`,
|
||||||
|
`instance`.`id` AS `instance_id`,
|
||||||
|
`instance`.`uuid` AS `instance_uuid`,
|
||||||
|
`instance`.`name` AS `instance_name`,
|
||||||
|
`instance`.`state` AS `instance_state`,
|
||||||
|
`volumes`.`size` AS `size`,
|
||||||
|
`volumes`.`uuid` AS `volume_uuid`,
|
||||||
|
`volumes`.`name` AS `volume_name`,
|
||||||
|
`volumes`.`provisioning_type` AS `provisioning_type`,
|
||||||
|
`volumes`.`format` AS `volume_format`,
|
||||||
|
`volumes`.`path` AS `volume_path`,
|
||||||
|
`volumes`.`chain_info` AS `volume_chain_info`,
|
||||||
|
`storage_pool`.`uuid` AS `pool_uuid`,
|
||||||
|
`storage_pool`.`name` AS `pool_name`,
|
||||||
|
`account`.`account_name` AS `account_name`,
|
||||||
|
`project`.`uuid` AS `project_uuid`,
|
||||||
|
`project`.`name` AS `project_name`,
|
||||||
|
`domain`.`uuid` AS `domain_uuid`,
|
||||||
|
`domain`.`name` AS `domain_name`,
|
||||||
|
`domain`.`path` AS `domain_path`,
|
||||||
|
`service_offering`.`uuid` AS `service_offering_uuid`,
|
||||||
|
`service_offering`.`name` AS `service_offering_name`,
|
||||||
|
`disk_offering`.`uuid` AS `disk_offering_uuid`,
|
||||||
|
`disk_offering`.`name` AS `disk_offering_name`,
|
||||||
|
`disk_offering`.`display_text` AS `disk_offering_display_text`,
|
||||||
|
`disk_offering`.`disk_size` AS `disk_offering_size`,
|
||||||
|
`disk_offering`.`customized` AS `disk_offering_custom`
|
||||||
|
FROM
|
||||||
|
`cloud`.`shared_filesystem`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`data_center` AS `zone` ON `shared_filesystem`.`data_center_id` = `zone`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`vm_instance` AS `instance` ON `shared_filesystem`.`vm_id` = `instance`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`volumes` AS `volumes` ON `shared_filesystem`.`volume_id` = `volumes`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`storage_pool` AS `storage_pool` ON `volumes`.`pool_id` = `storage_pool`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`account` AS `account` ON `shared_filesystem`.`account_id` = `account`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`projects` AS `project` ON `project`.`project_account_id` = `account`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`domain` AS `domain` ON `shared_filesystem`.`domain_id` = `domain`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`service_offering` AS `service_offering` ON `shared_filesystem`.`service_offering_id` = `service_offering`.`id`
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`disk_offering` AS `disk_offering` ON `volumes`.`disk_offering_id` = `disk_offering`.`id`
|
||||||
|
GROUP BY
|
||||||
|
`shared_filesystem`.`id`;
|
||||||
@ -25,6 +25,7 @@ SELECT
|
|||||||
`vm_instance`.`name` AS `name`,
|
`vm_instance`.`name` AS `name`,
|
||||||
`user_vm`.`display_name` AS `display_name`,
|
`user_vm`.`display_name` AS `display_name`,
|
||||||
`user_vm`.`user_data` AS `user_data`,
|
`user_vm`.`user_data` AS `user_data`,
|
||||||
|
`user_vm`.`user_vm_type` AS `user_vm_type`,
|
||||||
`account`.`id` AS `account_id`,
|
`account`.`id` AS `account_id`,
|
||||||
`account`.`uuid` AS `account_uuid`,
|
`account`.`uuid` AS `account_uuid`,
|
||||||
`account`.`account_name` AS `account_name`,
|
`account`.`account_name` AS `account_name`,
|
||||||
|
|||||||
@ -90,7 +90,7 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman
|
|||||||
libvirtComputingResource.applyDefaultNetworkRules(conn, vmSpec, false);
|
libvirtComputingResource.applyDefaultNetworkRules(conn, vmSpec, false);
|
||||||
|
|
||||||
// pass cmdline info to system vms
|
// pass cmdline info to system vms
|
||||||
if (vmSpec.getType() != VirtualMachine.Type.User || (vmSpec.getBootArgs() != null && vmSpec.getBootArgs().contains(UserVmManager.CKS_NODE))) {
|
if (vmSpec.getType() != VirtualMachine.Type.User || (vmSpec.getBootArgs() != null && (vmSpec.getBootArgs().contains(UserVmManager.CKS_NODE) || vmSpec.getBootArgs().contains(UserVmManager.SHAREDFSVM)))) {
|
||||||
// try to patch and SSH into the systemvm for up to 5 minutes
|
// try to patch and SSH into the systemvm for up to 5 minutes
|
||||||
for (int count = 0; count < 10; count++) {
|
for (int count = 0; count < 10; count++) {
|
||||||
// wait and try passCmdLine for 30 seconds at most for CLOUDSTACK-2823
|
// wait and try passCmdLine for 30 seconds at most for CLOUDSTACK-2823
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
public class VolumeMetricsResponse extends VolumeResponse {
|
public class VolumeMetricsResponse extends VolumeResponse {
|
||||||
@SerializedName("sizegb")
|
@SerializedName(ApiConstants.SIZEGB)
|
||||||
@Param(description = "disk size in GiB")
|
@Param(description = "disk size in GiB")
|
||||||
private String diskSizeGB;
|
private String diskSizeGB;
|
||||||
|
|
||||||
|
|||||||
@ -120,6 +120,7 @@
|
|||||||
|
|
||||||
<module>shutdown</module>
|
<module>shutdown</module>
|
||||||
|
|
||||||
|
<module>storage/sharedfs/storagevm</module>
|
||||||
<module>storage/image/default</module>
|
<module>storage/image/default</module>
|
||||||
<module>storage/image/s3</module>
|
<module>storage/image/s3</module>
|
||||||
<module>storage/image/sample</module>
|
<module>storage/image/sample</module>
|
||||||
|
|||||||
30
plugins/storage/sharedfs/storagevm/pom.xml
Normal file
30
plugins/storage/sharedfs/storagevm/pom.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>cloud-plugin-sharedfs-provider-storagevm</artifactId>
|
||||||
|
<name>Apache CloudStack Plugin - StorageVm shared filesystem provider</name>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloudstack-plugins</artifactId>
|
||||||
|
<version>4.20.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,303 @@
|
|||||||
|
// 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.storage.sharedfs.lifecycle;
|
||||||
|
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.SharedFS.SharedFSPath;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.SharedFS.SharedFSVmNamePrefix;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.provider.StorageVmSharedFSProvider.SHAREDFSVM_MIN_CPU_COUNT;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.provider.StorageVmSharedFSProvider.SHAREDFSVM_MIN_RAM_SIZE;
|
||||||
|
|
||||||
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.ManagementServerException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.VirtualMachineMigrationException;
|
||||||
|
import com.cloud.offering.ServiceOffering;
|
||||||
|
import com.cloud.storage.LaunchPermissionVO;
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
|
import com.cloud.storage.VolumeApiService;
|
||||||
|
import com.cloud.storage.VolumeVO;
|
||||||
|
import com.cloud.storage.dao.LaunchPermissionDao;
|
||||||
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
|
import com.cloud.uservm.UserVm;
|
||||||
|
import com.cloud.utils.FileUtil;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.utils.net.NetUtils;
|
||||||
|
import com.cloud.vm.UserVmManager;
|
||||||
|
import com.cloud.vm.UserVmService;
|
||||||
|
import com.cloud.vm.UserVmVO;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSLifeCycle;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.cloud.dc.DataCenter;
|
||||||
|
import com.cloud.hypervisor.Hypervisor;
|
||||||
|
import com.cloud.network.Network;
|
||||||
|
import com.cloud.resource.ResourceManager;
|
||||||
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
|
import com.cloud.storage.VMTemplateVO;
|
||||||
|
import com.cloud.storage.dao.VMTemplateDao;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.vm.VirtualMachineManager;
|
||||||
|
|
||||||
|
public class StorageVmSharedFSLifeCycle implements SharedFSLifeCycle {
|
||||||
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AccountManager accountMgr;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected ResourceManager resourceMgr;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private VirtualMachineManager virtualMachineManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private VolumeApiService volumeApiService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected UserVmService userVmService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected UserVmManager userVmManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DataCenterDao dataCenterDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private VMTemplateDao templateDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
VolumeDao volumeDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private UserVmDao userVmDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
NicDao nicDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ServiceOfferingDao serviceOfferingDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected LaunchPermissionDao launchPermissionDao;
|
||||||
|
|
||||||
|
private String readResourceFile(String resource) {
|
||||||
|
try {
|
||||||
|
return FileUtil.readResourceFile(resource);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CloudRuntimeException("Unable to read the user data resource file due to exception " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStorageVmConfig(final String fileSystem, final String hypervisorType, final String exportPath) {
|
||||||
|
String fsVmConfig = readResourceFile("/conf/fsvm-init.yml");
|
||||||
|
final String filesystem = "{{ fsvm.filesystem }}";
|
||||||
|
final String hypervisor = "{{ fsvm.hypervisor }}";
|
||||||
|
final String exportpath = "{{ fsvm.exportpath }}";
|
||||||
|
fsVmConfig = fsVmConfig.replace(filesystem, fileSystem);
|
||||||
|
fsVmConfig = fsVmConfig.replace(hypervisor, hypervisorType);
|
||||||
|
fsVmConfig = fsVmConfig.replace(exportpath, exportPath);
|
||||||
|
return fsVmConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStorageVmName(String fileShareName) {
|
||||||
|
String prefix = String.format("%s-%s", SharedFSVmNamePrefix, fileShareName);
|
||||||
|
String suffix = Long.toHexString(System.currentTimeMillis());
|
||||||
|
|
||||||
|
if (!NetUtils.verifyDomainNameLabel(prefix, true)) {
|
||||||
|
prefix = prefix.replaceAll("[^a-zA-Z0-9-]", "");
|
||||||
|
}
|
||||||
|
int nameLength = prefix.length() + suffix.length() + SharedFSVmNamePrefix.length();
|
||||||
|
if (nameLength > 63) {
|
||||||
|
int prefixLength = prefix.length() - (nameLength - 63);
|
||||||
|
prefix = prefix.substring(0, prefixLength);
|
||||||
|
}
|
||||||
|
return (String.format("%s-%s", prefix, suffix));
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserVm deploySharedFSVM(Long zoneId, Account owner, List<Long> networkIds, String name, Long serviceOfferingId, Long diskOfferingId, SharedFS.FileSystemType fileSystem, Long size, Long minIops, Long maxIops) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
|
||||||
|
ServiceOffering serviceOffering = serviceOfferingDao.findById(serviceOfferingId);
|
||||||
|
DataCenter zone = dataCenterDao.findById(zoneId);
|
||||||
|
|
||||||
|
List<Hypervisor.HypervisorType> hypervisors = resourceMgr.getSupportedHypervisorTypes(zoneId, false, null);
|
||||||
|
if (hypervisors.size() > 0) {
|
||||||
|
Collections.shuffle(hypervisors);
|
||||||
|
} else {
|
||||||
|
throw new CloudRuntimeException(String.format("No supported hypervisor found for zone %s.", zone.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
String hostName = getStorageVmName(name);
|
||||||
|
Network.IpAddresses addrs = new Network.IpAddresses(null, null);
|
||||||
|
Map<String, String> customParameterMap = new HashMap<String, String>();
|
||||||
|
if (minIops != null) {
|
||||||
|
customParameterMap.put("minIopsDo", minIops.toString());
|
||||||
|
customParameterMap.put("maxIopsDo", maxIops.toString());
|
||||||
|
}
|
||||||
|
List<String> keypairs = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (final Iterator<Hypervisor.HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) {
|
||||||
|
final Hypervisor.HypervisorType hypervisor = iter.next();
|
||||||
|
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(zoneId, hypervisor);
|
||||||
|
if (template == null && !iter.hasNext()) {
|
||||||
|
throw new CloudRuntimeException(String.format("Unable to find the systemvm template for %s or it was not downloaded in %s.", hypervisor.toString(), zone.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchPermissionVO existingPermission = launchPermissionDao.findByTemplateAndAccount(template.getId(), owner.getId());
|
||||||
|
if (existingPermission == null) {
|
||||||
|
LaunchPermissionVO launchPermission = new LaunchPermissionVO(template.getId(), owner.getId());
|
||||||
|
launchPermissionDao.persist(launchPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserVm vm = null;
|
||||||
|
String fsVmConfig = getStorageVmConfig(fileSystem.toString().toLowerCase(), hypervisor.toString().toLowerCase(), SharedFSPath);
|
||||||
|
String base64UserData = Base64.encodeBase64String(fsVmConfig.getBytes(com.cloud.utils.StringUtils.getPreferredCharset()));
|
||||||
|
CallContext vmContext = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
|
||||||
|
try {
|
||||||
|
vm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, hostName, hostName,
|
||||||
|
diskOfferingId, size, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData,
|
||||||
|
null, null, keypairs, null, addrs, null, null, null,
|
||||||
|
customParameterMap, null, null, null, null,
|
||||||
|
true, UserVmManager.SHAREDFSVM, null);
|
||||||
|
vmContext.setEventResourceId(vm.getId());
|
||||||
|
userVmService.startVirtualMachine(vm);
|
||||||
|
} catch (InsufficientCapacityException ex) {
|
||||||
|
if (vm != null) {
|
||||||
|
expungeVm(vm.getId());
|
||||||
|
}
|
||||||
|
if (iter.hasNext()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
CallContext.unregister();
|
||||||
|
}
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkPrerequisites(DataCenter zone, Long serviceOfferingId) {
|
||||||
|
ServiceOffering serviceOffering = serviceOfferingDao.findById(serviceOfferingId);
|
||||||
|
if (serviceOffering.getCpu() < SHAREDFSVM_MIN_CPU_COUNT.valueIn(zone.getId())) {
|
||||||
|
throw new InvalidParameterValueException("Service offering's number of cpu should be greater than or equal to " + SHAREDFSVM_MIN_CPU_COUNT.key());
|
||||||
|
}
|
||||||
|
if (serviceOffering.getRamSize() < SHAREDFSVM_MIN_RAM_SIZE.valueIn(zone.getId())) {
|
||||||
|
throw new InvalidParameterValueException("Service offering's ram size should be greater than or equal to " + SHAREDFSVM_MIN_RAM_SIZE.key());
|
||||||
|
}
|
||||||
|
if (!serviceOffering.isOfferHA()) {
|
||||||
|
throw new InvalidParameterValueException("Service offering's should be HA enabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Long, Long> deploySharedFS(SharedFS sharedFS, Long networkId, Long diskOfferingId, Long size, Long minIops, Long maxIops) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException {
|
||||||
|
Account owner = accountMgr.getActiveAccountById(sharedFS.getAccountId());
|
||||||
|
UserVm vm = deploySharedFSVM(sharedFS.getDataCenterId(), owner, List.of(networkId), sharedFS.getName(), sharedFS.getServiceOfferingId(), diskOfferingId, sharedFS.getFsType(), size, minIops, maxIops);
|
||||||
|
|
||||||
|
List<VolumeVO> volumes = volumeDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
|
||||||
|
return new Pair<>(volumes.get(0).getId(), vm.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startSharedFS(SharedFS sharedFS) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException {
|
||||||
|
UserVmVO vm = userVmDao.findById(sharedFS.getVmId());
|
||||||
|
userVmService.startVirtualMachine(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopSharedFS(SharedFS sharedFS, Boolean forced) {
|
||||||
|
userVmManager.stopVirtualMachine(sharedFS.getVmId(), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expungeVm(Long vmId) {
|
||||||
|
UserVmVO userVM = userVmDao.findById(vmId);
|
||||||
|
if (userVM == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
UserVm vm = userVmService.destroyVm(userVM.getId(), true);
|
||||||
|
if (!userVmManager.expunge(userVM)) {
|
||||||
|
throw new CloudRuntimeException("Failed to expunge VM " + userVM.toString());
|
||||||
|
}
|
||||||
|
} catch (ResourceUnavailableException e) {
|
||||||
|
throw new CloudRuntimeException("Failed to expunge VM " + userVM.toString());
|
||||||
|
}
|
||||||
|
userVmDao.remove(vmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteSharedFS(SharedFS sharedFS) {
|
||||||
|
Long vmId = sharedFS.getVmId();
|
||||||
|
Long volumeId = sharedFS.getVolumeId();
|
||||||
|
if (vmId != null) {
|
||||||
|
expungeVm(vmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volumeId == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
VolumeVO volume = volumeDao.findById(volumeId);
|
||||||
|
Boolean expunge = false;
|
||||||
|
Boolean forceExpunge = false;
|
||||||
|
if (volume.getState() == Volume.State.Allocated) {
|
||||||
|
expunge = true;
|
||||||
|
forceExpunge = true;
|
||||||
|
}
|
||||||
|
volumeApiService.destroyVolume(volume.getId(), CallContext.current().getCallingAccount(), expunge, forceExpunge);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean reDeploySharedFS(SharedFS sharedFS) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException {
|
||||||
|
UserVm vm = virtualMachineManager.restoreVirtualMachine(sharedFS.getVmId(), null, null, true, null);
|
||||||
|
return (vm != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean changeSharedFSServiceOffering(SharedFS sharedFS, Long serviceOfferingId) throws ManagementServerException, ResourceUnavailableException, VirtualMachineMigrationException {
|
||||||
|
return userVmManager.upgradeVirtualMachine(sharedFS.getVmId(), serviceOfferingId, new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
// 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.storage.sharedfs.provider;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSProvider;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.lifecycle.StorageVmSharedFSLifeCycle;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.AdapterBase;
|
||||||
|
import com.cloud.utils.component.ComponentContext;
|
||||||
|
|
||||||
|
public class StorageVmSharedFSProvider extends AdapterBase implements SharedFSProvider, Configurable {
|
||||||
|
protected String name = String.valueOf(SharedFSProviderType.SHAREDFSVM);
|
||||||
|
|
||||||
|
public static final ConfigKey<Integer> SHAREDFSVM_MIN_RAM_SIZE = new ConfigKey<Integer>("Advanced",
|
||||||
|
Integer.class,
|
||||||
|
"sharedfsvm.min.ram.size",
|
||||||
|
"1024",
|
||||||
|
"minimum ram size allowed for the compute offering to be used to create the sharedfs vm.",
|
||||||
|
true,
|
||||||
|
ConfigKey.Scope.Zone,
|
||||||
|
SharedFS.SharedFSFeatureEnabled.key());
|
||||||
|
|
||||||
|
public static final ConfigKey<Integer> SHAREDFSVM_MIN_CPU_COUNT = new ConfigKey<Integer>("Advanced",
|
||||||
|
Integer.class,
|
||||||
|
"sharedfsvm.min.cpu.count",
|
||||||
|
"2",
|
||||||
|
"minimum cpu count allowed for the compute offering to be used to create the sharedfs vm.",
|
||||||
|
true,
|
||||||
|
ConfigKey.Scope.Zone,
|
||||||
|
SharedFS.SharedFSFeatureEnabled.key());
|
||||||
|
|
||||||
|
protected StorageVmSharedFSLifeCycle lifecycle;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure() {
|
||||||
|
lifecycle = ComponentContext.inject(StorageVmSharedFSLifeCycle.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StorageVmSharedFSLifeCycle getSharedFSLifeCycle() {
|
||||||
|
return lifecycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConfigComponentName() {
|
||||||
|
return StorageVmSharedFSProvider.class.getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
|
return new ConfigKey[] {
|
||||||
|
SHAREDFSVM_MIN_CPU_COUNT,
|
||||||
|
SHAREDFSVM_MIN_RAM_SIZE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# 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.
|
||||||
|
name=sharedfs-provider-storagevm
|
||||||
|
parent=storage
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd"
|
||||||
|
>
|
||||||
|
|
||||||
|
<bean id="storageVmSharedFSProvider"
|
||||||
|
class="org.apache.cloudstack.storage.sharedfs.provider.StorageVmSharedFSProvider">
|
||||||
|
<property name="name" value="StorageVmSharedFSProvider" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
||||||
@ -0,0 +1,220 @@
|
|||||||
|
#cloud-config
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
write_files:
|
||||||
|
- path: /usr/local/bin/sharedfs/common
|
||||||
|
permissions: '0700'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
LOG_FILE="/var/log/userdata.log"
|
||||||
|
log() {
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
get_block_device() {
|
||||||
|
if [ "$HYPERVISOR" == "kvm" ]; then
|
||||||
|
BLOCK_DEVICE="vdb"
|
||||||
|
elif [ "$HYPERVISOR" == "xenserver" ]; then
|
||||||
|
BLOCK_DEVICE="xvdb"
|
||||||
|
elif [ "$HYPERVISOR" == "vmware" ]; then
|
||||||
|
BLOCK_DEVICE="sdb"
|
||||||
|
else
|
||||||
|
log "Unknown hypervisor"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "$BLOCK_DEVICE"
|
||||||
|
}
|
||||||
|
|
||||||
|
- path: /usr/local/bin/sharedfs/create-shared-filesystem
|
||||||
|
permissions: '0700'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
source /usr/local/bin/sharedfs/common
|
||||||
|
|
||||||
|
log "Running add script"
|
||||||
|
|
||||||
|
DISK_SIZE_DIR="/usr/local/var/sharedfs"
|
||||||
|
DISK_SIZE_FILE="$DISK_SIZE_DIR/previous_disk_size"
|
||||||
|
|
||||||
|
PARTITION="$1"
|
||||||
|
EXPORT_DIR={{ fsvm.exportpath }}
|
||||||
|
PERMISSIONS="rw,sync,no_subtree_check,no_root_squash"
|
||||||
|
|
||||||
|
mkdir -p "$EXPORT_DIR"
|
||||||
|
|
||||||
|
FS_TYPE=$(lsblk -no FSTYPE "$PARTITION")
|
||||||
|
|
||||||
|
if [ -z "$FS_TYPE" ]; then
|
||||||
|
FILESYSTEM={{ fsvm.filesystem }}
|
||||||
|
if [ "$FILESYSTEM" == "xfs" ]; then
|
||||||
|
mkfs.xfs "$PARTITION"
|
||||||
|
log "Formatted Partition $PARTITION with XFS Filesystem."
|
||||||
|
elif [ "$FILESYSTEM" == "ext4" ]; then
|
||||||
|
mkfs.ext4 "$PARTITION"
|
||||||
|
log "Formatted Partition $PARTITION with EXT4 Filesystem."
|
||||||
|
else
|
||||||
|
log "Invalid filesystem type specified. Use 'xfs' or 'ext4'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Partition $PARTITION already has a filesystem of type $FS_TYPE. Skipping format."
|
||||||
|
fi
|
||||||
|
|
||||||
|
FS_INFO=$(blkid "$PARTITION")
|
||||||
|
UUID=$(echo "$FS_INFO" | grep -oP "UUID=\"\K[^\"]+")
|
||||||
|
TYPE=$(echo "$FS_INFO" | grep -oP "TYPE=\"\K[^\"]+")
|
||||||
|
|
||||||
|
if [ -z "$UUID" ] || [ -z "$TYPE" ]; then
|
||||||
|
log "Failed to retrieve UUID or TYPE for $PARTITION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "UUID=$UUID $EXPORT_DIR $TYPE defaults 0 2" >> /etc/fstab
|
||||||
|
log "Added fstab entry."
|
||||||
|
|
||||||
|
mount -a
|
||||||
|
|
||||||
|
if mountpoint -q "$EXPORT_DIR"; then
|
||||||
|
log "$PARTITION is successfully mounted on $EXPORT_DIR"
|
||||||
|
else
|
||||||
|
log "Failed to mount $PARTITION on $EXPORT_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Configuring NFS export..."
|
||||||
|
EXPORT_ENTRY="$EXPORT_DIR *($PERMISSIONS)"
|
||||||
|
if ! grep -qF "$EXPORT_ENTRY" /etc/exports; then
|
||||||
|
echo "$EXPORT_ENTRY" | tee -a /etc/exports > /dev/null
|
||||||
|
fi
|
||||||
|
exportfs -ra
|
||||||
|
|
||||||
|
log "Enable and restart NFS server..."
|
||||||
|
systemctl enable nfs-kernel-server
|
||||||
|
systemctl restart nfs-kernel-server
|
||||||
|
|
||||||
|
CURRENT_DISK_SIZE=$(blockdev --getsz "$PARTITION")
|
||||||
|
|
||||||
|
mkdir -p "$DISK_SIZE_DIR"
|
||||||
|
echo "$CURRENT_DISK_SIZE" > "$DISK_SIZE_FILE"
|
||||||
|
|
||||||
|
log "NFS share created successfully."
|
||||||
|
log "Finished running add script."
|
||||||
|
|
||||||
|
- path: /usr/local/bin/sharedfs/resize-filesystem
|
||||||
|
permissions: '0700'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
source /usr/local/bin/sharedfs/common
|
||||||
|
log "Running resize script."
|
||||||
|
|
||||||
|
FILESYSTEM={{ fsvm.filesystem }}
|
||||||
|
if [ "$FILESYSTEM" == "xfs" ]; then
|
||||||
|
EXPORT_DIR={{ fsvm.exportpath }}
|
||||||
|
xfs_growfs "$EXPORT_DIR"
|
||||||
|
elif [ "$FILESYSTEM" == "ext4" ]; then
|
||||||
|
PARTITION="/dev/$1"
|
||||||
|
resize2fs "$PARTITION"
|
||||||
|
else
|
||||||
|
log "Invalid filesystem type specified. Use 'xfs' or 'ext4'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Finished running resize script."
|
||||||
|
|
||||||
|
- path: /etc/systemd/system/resize-sharedfs@.service
|
||||||
|
permissions: '0700'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Resize Shared FileSystem
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/sharedfs/resize-filesystem %I
|
||||||
|
|
||||||
|
- path: /etc/systemd/system/cloud-dhclient@.service
|
||||||
|
permissions: '0700'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=CloudStack service to start dhclient
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/sbin/dhclient %I
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
- path: /usr/local/bin/sharedfs/fsvm-setup
|
||||||
|
permissions: '0700'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
source /usr/local/bin/sharedfs/common
|
||||||
|
|
||||||
|
HYPERVISOR={{ fsvm.hypervisor }}
|
||||||
|
BLOCK_DEVICE=$(get_block_device "$HYPERVISOR")
|
||||||
|
|
||||||
|
PARTITION="/dev/$BLOCK_DEVICE"
|
||||||
|
ADD_PARTITION_FILE="/usr/local/bin/sharedfs/create-shared-filesystem"
|
||||||
|
RESIZE_PARTITION_FILE="/usr/local/bin/sharedfs/resize-filesystem"
|
||||||
|
|
||||||
|
for interface in $(ls /sys/class/net/ | grep '^eth'); do
|
||||||
|
echo "Starting cloud-dhclient service for interface: $interface"
|
||||||
|
sudo systemctl start cloud-dhclient@$interface.service
|
||||||
|
done
|
||||||
|
log "Existing network interfaces configured"
|
||||||
|
|
||||||
|
UDEV_RESIZE_RULE="KERNEL==\"$BLOCK_DEVICE\", ACTION==\"add|change\", SUBSYSTEM==\"block\", ENV{SYSTEMD_WANTS}=\"resize-sharedfs@%k.service\""
|
||||||
|
UDEV_ADD_NIC_RULE="ACTION==\"add\", SUBSYSTEM==\"net\", DRIVERS==\"?*\", ENV{SYSTEMD_WANTS}=\"cloud-dhclient@%k.service\""
|
||||||
|
|
||||||
|
# Add udev rules
|
||||||
|
echo "$UDEV_RESIZE_RULE" > /etc/udev/rules.d/99-resize-filesystem.rules
|
||||||
|
echo "$UDEV_ADD_NIC_RULE" > /etc/udev/rules.d/99-add-nic.rules
|
||||||
|
|
||||||
|
if [ -b "$PARTITION" ]; then
|
||||||
|
log "Partition $PARTITION exists. Running the $ADD_PARTITION_FILE script"
|
||||||
|
$ADD_PARTITION_FILE "$PARTITION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Udev rules added."
|
||||||
|
|
||||||
|
# Reload udev rules
|
||||||
|
udevadm control --reload-rules
|
||||||
|
udevadm trigger
|
||||||
|
|
||||||
|
log "Script execution finished successfully."
|
||||||
|
|
||||||
|
sudo touch /home/cloud/success
|
||||||
|
echo "true" > /home/cloud/success
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- /usr/local/bin/sharedfs/fsvm-setup
|
||||||
@ -0,0 +1,335 @@
|
|||||||
|
// 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.storage.sharedfs.lifecycle;
|
||||||
|
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.provider.StorageVmSharedFSProvider.SHAREDFSVM_MIN_CPU_COUNT;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.provider.StorageVmSharedFSProvider.SHAREDFSVM_MIN_RAM_SIZE;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyList;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyMap;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.isNull;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import com.cloud.dc.DataCenter;
|
||||||
|
import com.cloud.dc.DataCenterVO;
|
||||||
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.hypervisor.Hypervisor;
|
||||||
|
import com.cloud.network.Network;
|
||||||
|
import com.cloud.offering.ServiceOffering;
|
||||||
|
import com.cloud.resource.ResourceManager;
|
||||||
|
import com.cloud.service.ServiceOfferingVO;
|
||||||
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
|
import com.cloud.storage.VMTemplateVO;
|
||||||
|
import com.cloud.storage.Volume;
|
||||||
|
import com.cloud.storage.VolumeApiService;
|
||||||
|
import com.cloud.storage.VolumeVO;
|
||||||
|
import com.cloud.storage.dao.DiskOfferingDao;
|
||||||
|
import com.cloud.storage.dao.LaunchPermissionDao;
|
||||||
|
import com.cloud.storage.dao.VMTemplateDao;
|
||||||
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
|
import com.cloud.template.VirtualMachineTemplate;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.uservm.UserVm;
|
||||||
|
import com.cloud.utils.FileUtil;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.vm.UserVmManager;
|
||||||
|
import com.cloud.vm.UserVmService;
|
||||||
|
import com.cloud.vm.UserVmVO;
|
||||||
|
import com.cloud.vm.VirtualMachineManager;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class StorageVmSharedFSLifeCycleTest {
|
||||||
|
@Mock
|
||||||
|
private AccountManager accountMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
protected ResourceManager resourceMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private VirtualMachineManager virtualMachineManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private VolumeApiService volumeApiService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
protected UserVmService userVmService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
protected UserVmManager userVmManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DataCenterDao dataCenterDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private VMTemplateDao templateDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VolumeDao volumeDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UserVmDao userVmDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NicDao nicDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ServiceOfferingDao serviceOfferingDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DiskOfferingDao diskOfferingDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
protected LaunchPermissionDao launchPermissionDao;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
StorageVmSharedFSLifeCycle lifeCycle;
|
||||||
|
|
||||||
|
private static final long s_ownerId = 1L;
|
||||||
|
private static final long s_zoneId = 2L;
|
||||||
|
private static final long s_diskOfferingId = 3L;
|
||||||
|
private static final long s_serviceOfferingId = 4L;
|
||||||
|
private static final long s_templateId = 5L;
|
||||||
|
private static final long s_volumeId = 6L;
|
||||||
|
private static final long s_vmId = 7L;
|
||||||
|
private static final long s_networkId = 8L;
|
||||||
|
private static final long s_size = 10L;
|
||||||
|
private static final long s_minIops = 1000L;
|
||||||
|
private static final long s_maxIops = 2000L;
|
||||||
|
private static final String s_fsFormat = "EXT4";
|
||||||
|
private static final String s_name = "TestSharedFS";
|
||||||
|
|
||||||
|
private MockedStatic<FileUtil> fileUtilMocked;
|
||||||
|
private MockedStatic<CallContext> callContextMocked;
|
||||||
|
|
||||||
|
private AutoCloseable closeable;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
closeable = MockitoAnnotations.openMocks(this);
|
||||||
|
callContextMocked = mockStatic(CallContext.class);
|
||||||
|
CallContext callContextMock = mock(CallContext.class);
|
||||||
|
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||||
|
Account owner = mock(Account.class);
|
||||||
|
when(callContextMock.getCallingAccount()).thenReturn(owner);
|
||||||
|
CallContext vmContext = mock(CallContext.class);
|
||||||
|
when(callContextMock.register(CallContext.current(), ApiCommandResourceType.VirtualMachine)).thenReturn(vmContext);
|
||||||
|
|
||||||
|
fileUtilMocked = mockStatic(FileUtil.class);
|
||||||
|
fileUtilMocked.when(() -> FileUtil.readResourceFile("/conf/fsvm-init.yml")).thenReturn("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
fileUtilMocked.close();
|
||||||
|
callContextMocked.close();
|
||||||
|
closeable.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckPrerequisites() {
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(zone.getId()).thenReturn(s_zoneId);
|
||||||
|
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
|
||||||
|
when(serviceOfferingVO.getCpu()).thenReturn(4);
|
||||||
|
when(serviceOfferingVO.getRamSize()).thenReturn(1024);
|
||||||
|
when(serviceOfferingVO.isOfferHA()).thenReturn(true);
|
||||||
|
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOfferingVO);
|
||||||
|
lifeCycle.checkPrerequisites(zone, s_serviceOfferingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckPrerequisitesMinCpuException() {
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(zone.getId()).thenReturn(s_zoneId);
|
||||||
|
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
|
||||||
|
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOfferingVO);
|
||||||
|
InvalidParameterValueException exception = Assert.assertThrows(InvalidParameterValueException.class, () -> lifeCycle.checkPrerequisites(zone, s_serviceOfferingId));
|
||||||
|
Assert.assertEquals(exception.getMessage(), "Service offering's number of cpu should be greater than or equal to " + SHAREDFSVM_MIN_CPU_COUNT.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckPrerequisitesMinRamException() {
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(zone.getId()).thenReturn(s_zoneId);
|
||||||
|
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
|
||||||
|
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOfferingVO);
|
||||||
|
when(serviceOfferingVO.getCpu()).thenReturn(4);
|
||||||
|
InvalidParameterValueException exception = Assert.assertThrows(InvalidParameterValueException.class, () -> lifeCycle.checkPrerequisites(zone, s_serviceOfferingId));
|
||||||
|
Assert.assertEquals(exception.getMessage(), "Service offering's ram size should be greater than or equal to " + SHAREDFSVM_MIN_RAM_SIZE.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckPrerequisitesHAException() {
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(zone.getId()).thenReturn(s_zoneId);
|
||||||
|
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
|
||||||
|
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOfferingVO);
|
||||||
|
when(serviceOfferingVO.getCpu()).thenReturn(4);
|
||||||
|
when(serviceOfferingVO.getRamSize()).thenReturn(1024);
|
||||||
|
InvalidParameterValueException exception = Assert.assertThrows(InvalidParameterValueException.class, () -> lifeCycle.checkPrerequisites(zone, s_serviceOfferingId));
|
||||||
|
Assert.assertEquals(exception.getMessage(), "Service offering's should be HA enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedFS prepareDeploySharedFS() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
|
||||||
|
SharedFS sharedFS = mock(SharedFS.class);
|
||||||
|
when(sharedFS.getDataCenterId()).thenReturn(s_zoneId);
|
||||||
|
when(sharedFS.getName()).thenReturn(s_name);
|
||||||
|
when(sharedFS.getServiceOfferingId()).thenReturn(s_serviceOfferingId);
|
||||||
|
when(sharedFS.getFsType()).thenReturn(SharedFS.FileSystemType.valueOf(s_fsFormat));
|
||||||
|
when(sharedFS.getAccountId()).thenReturn(s_ownerId);
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false, null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
|
||||||
|
|
||||||
|
ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class);
|
||||||
|
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOffering);
|
||||||
|
|
||||||
|
VMTemplateVO template = mock(VMTemplateVO.class);
|
||||||
|
when(templateDao.findSystemVMReadyTemplate(s_zoneId, Hypervisor.HypervisorType.KVM)).thenReturn(template);
|
||||||
|
when(template.getId()).thenReturn(s_templateId);
|
||||||
|
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeploySharedFS() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException {
|
||||||
|
SharedFS sharedFS = prepareDeploySharedFS();
|
||||||
|
when(sharedFS.getAccountId()).thenReturn(s_ownerId);
|
||||||
|
|
||||||
|
Account owner = mock(Account.class);
|
||||||
|
when(owner.getId()).thenReturn(s_ownerId);
|
||||||
|
when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(owner);
|
||||||
|
|
||||||
|
UserVm vm = mock(UserVm.class);
|
||||||
|
when(vm.getId()).thenReturn(s_vmId);
|
||||||
|
when(userVmService.createAdvancedVirtualMachine(
|
||||||
|
any(DataCenter.class), any(ServiceOffering.class), any(VirtualMachineTemplate.class), anyList(), any(Account.class), anyString(),
|
||||||
|
anyString(), anyLong(), anyLong(), isNull(), any(Hypervisor.HypervisorType.class), any(BaseCmd.HTTPMethod.class), anyString(),
|
||||||
|
isNull(), isNull(), anyList(), isNull(), any(Network.IpAddresses.class), isNull(), isNull(), isNull(),
|
||||||
|
anyMap(), isNull(), isNull(), isNull(), isNull(),
|
||||||
|
anyBoolean(), anyString(), isNull())).thenReturn(vm);
|
||||||
|
|
||||||
|
VolumeVO volume = mock(VolumeVO.class);
|
||||||
|
when(volume.getId()).thenReturn(s_volumeId);
|
||||||
|
when(volumeDao.findByInstanceAndType(s_vmId, Volume.Type.DATADISK)).thenReturn(List.of(volume));
|
||||||
|
|
||||||
|
Pair<Long, Long> result = lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops);
|
||||||
|
Assert.assertEquals(Optional.ofNullable(result.first()), Optional.ofNullable(s_volumeId));
|
||||||
|
Assert.assertEquals(Optional.ofNullable(result.second()), Optional.ofNullable(s_vmId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CloudRuntimeException.class)
|
||||||
|
public void testDeploySharedFSHypervisorNotFound() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException {
|
||||||
|
SharedFS sharedFS = mock(SharedFS.class);
|
||||||
|
when(sharedFS.getDataCenterId()).thenReturn(s_zoneId);
|
||||||
|
when(sharedFS.getName()).thenReturn(s_name);
|
||||||
|
when(sharedFS.getServiceOfferingId()).thenReturn(s_serviceOfferingId);
|
||||||
|
when(sharedFS.getFsType()).thenReturn(SharedFS.FileSystemType.valueOf(s_fsFormat));
|
||||||
|
when(sharedFS.getAccountId()).thenReturn(s_ownerId);
|
||||||
|
|
||||||
|
when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(null);
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CloudRuntimeException.class)
|
||||||
|
public void testDeploySharedFSTemplateNotFound() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException {
|
||||||
|
SharedFS sharedFS = mock(SharedFS.class);
|
||||||
|
when(sharedFS.getDataCenterId()).thenReturn(s_zoneId);
|
||||||
|
when(sharedFS.getName()).thenReturn(s_name);
|
||||||
|
when(sharedFS.getServiceOfferingId()).thenReturn(s_serviceOfferingId);
|
||||||
|
when(sharedFS.getFsType()).thenReturn(SharedFS.FileSystemType.valueOf(s_fsFormat));
|
||||||
|
when(sharedFS.getAccountId()).thenReturn(s_ownerId);
|
||||||
|
|
||||||
|
when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(null);
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false, null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
|
||||||
|
|
||||||
|
when(templateDao.findSystemVMReadyTemplate(s_zoneId, Hypervisor.HypervisorType.KVM)).thenReturn(null);
|
||||||
|
lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteSharedFS() throws ResourceUnavailableException {
|
||||||
|
SharedFS sharedFS = mock(SharedFS.class);
|
||||||
|
when(sharedFS.getVmId()).thenReturn(s_vmId);
|
||||||
|
when(sharedFS.getVolumeId()).thenReturn(s_volumeId);
|
||||||
|
|
||||||
|
UserVmVO vm = mock(UserVmVO.class);
|
||||||
|
when(vm.getId()).thenReturn(s_vmId);
|
||||||
|
when(userVmDao.findById(s_vmId)).thenReturn(vm);
|
||||||
|
when(userVmService.destroyVm(s_vmId, true)).thenReturn(vm);
|
||||||
|
when(userVmManager.expunge(vm)).thenReturn(true);
|
||||||
|
|
||||||
|
VolumeVO volume = mock(VolumeVO.class);
|
||||||
|
when(volumeDao.findById(s_volumeId)).thenReturn(volume);
|
||||||
|
when(volume.getId()).thenReturn(s_volumeId);
|
||||||
|
when(volume.getState()).thenReturn(Volume.State.Allocated);
|
||||||
|
|
||||||
|
Assert.assertEquals(lifeCycle.deleteSharedFS(sharedFS), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReDeploySharedFS() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException {
|
||||||
|
SharedFS sharedFS = mock(SharedFS.class);
|
||||||
|
Long vmId = 1L;
|
||||||
|
when(sharedFS.getVmId()).thenReturn(vmId);
|
||||||
|
UserVm vm = mock(UserVm.class);
|
||||||
|
when(virtualMachineManager.restoreVirtualMachine(vmId, null, null, true, null)).thenReturn(vm);
|
||||||
|
boolean result = lifeCycle.reDeploySharedFS(sharedFS);
|
||||||
|
Assert.assertEquals(result, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -50,6 +50,7 @@ import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
|||||||
import org.apache.cloudstack.api.response.DomainResponse;
|
import org.apache.cloudstack.api.response.DomainResponse;
|
||||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||||
import org.apache.cloudstack.api.response.EventResponse;
|
import org.apache.cloudstack.api.response.EventResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
import org.apache.cloudstack.api.response.HostForMigrationResponse;
|
import org.apache.cloudstack.api.response.HostForMigrationResponse;
|
||||||
import org.apache.cloudstack.api.response.HostResponse;
|
import org.apache.cloudstack.api.response.HostResponse;
|
||||||
import org.apache.cloudstack.api.response.HostTagResponse;
|
import org.apache.cloudstack.api.response.HostTagResponse;
|
||||||
@ -93,6 +94,9 @@ import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao;
|
|||||||
import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO;
|
import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO;
|
||||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.dao.SharedFSJoinDao;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
|
|
||||||
import com.cloud.agent.api.VgpuTypesInfo;
|
import com.cloud.agent.api.VgpuTypesInfo;
|
||||||
import com.cloud.api.query.dao.AccountJoinDao;
|
import com.cloud.api.query.dao.AccountJoinDao;
|
||||||
@ -491,6 +495,8 @@ public class ApiDBUtils {
|
|||||||
static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao;
|
static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao;
|
||||||
static ObjectStoreDao s_objectStoreDao;
|
static ObjectStoreDao s_objectStoreDao;
|
||||||
|
|
||||||
|
static SharedFSJoinDao s_sharedFSJoinDao;
|
||||||
|
|
||||||
static BucketDao s_bucketDao;
|
static BucketDao s_bucketDao;
|
||||||
static VirtualMachineManager s_virtualMachineManager;
|
static VirtualMachineManager s_virtualMachineManager;
|
||||||
|
|
||||||
@ -758,6 +764,8 @@ public class ApiDBUtils {
|
|||||||
private BucketDao bucketDao;
|
private BucketDao bucketDao;
|
||||||
@Inject
|
@Inject
|
||||||
private VirtualMachineManager virtualMachineManager;
|
private VirtualMachineManager virtualMachineManager;
|
||||||
|
@Inject
|
||||||
|
private SharedFSJoinDao sharedFSJoinDao;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
void init() {
|
void init() {
|
||||||
@ -893,6 +901,7 @@ public class ApiDBUtils {
|
|||||||
s_objectStoreDao = objectStoreDao;
|
s_objectStoreDao = objectStoreDao;
|
||||||
s_bucketDao = bucketDao;
|
s_bucketDao = bucketDao;
|
||||||
s_virtualMachineManager = virtualMachineManager;
|
s_virtualMachineManager = virtualMachineManager;
|
||||||
|
s_sharedFSJoinDao = sharedFSJoinDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///////////////////////////////////////////////////////////
|
// ///////////////////////////////////////////////////////////
|
||||||
@ -2269,4 +2278,12 @@ public class ApiDBUtils {
|
|||||||
public static ObjectStoreResponse fillObjectStoreDetails(ObjectStoreResponse storeData, ObjectStoreVO store) {
|
public static ObjectStoreResponse fillObjectStoreDetails(ObjectStoreResponse storeData, ObjectStoreVO store) {
|
||||||
return s_objectStoreDao.setObjectStoreResponse(storeData, store);
|
return s_objectStoreDao.setObjectStoreResponse(storeData, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SharedFSResponse newSharedFSResponse(ResponseView view, SharedFSJoinVO sharedFSView) {
|
||||||
|
return s_sharedFSJoinDao.newSharedFSResponse(view, sharedFSView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SharedFSJoinVO newSharedFSView(SharedFS sharedFS) {
|
||||||
|
return s_sharedFSJoinDao.newSharedFSView(sharedFS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,6 +89,7 @@ import org.apache.cloudstack.api.response.DomainResponse;
|
|||||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||||
import org.apache.cloudstack.api.response.EventResponse;
|
import org.apache.cloudstack.api.response.EventResponse;
|
||||||
import org.apache.cloudstack.api.response.ExtractResponse;
|
import org.apache.cloudstack.api.response.ExtractResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
import org.apache.cloudstack.api.response.FirewallResponse;
|
import org.apache.cloudstack.api.response.FirewallResponse;
|
||||||
import org.apache.cloudstack.api.response.FirewallRuleResponse;
|
import org.apache.cloudstack.api.response.FirewallRuleResponse;
|
||||||
import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
|
import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
|
||||||
@ -213,6 +214,8 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
|||||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
import org.apache.cloudstack.storage.object.Bucket;
|
import org.apache.cloudstack.storage.object.Bucket;
|
||||||
import org.apache.cloudstack.storage.object.ObjectStore;
|
import org.apache.cloudstack.storage.object.ObjectStore;
|
||||||
import org.apache.cloudstack.usage.Usage;
|
import org.apache.cloudstack.usage.Usage;
|
||||||
@ -5280,4 +5283,10 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||||||
populateAccount(bucketResponse, bucket.getAccountId());
|
populateAccount(bucketResponse, bucket.getAccountId());
|
||||||
return bucketResponse;
|
return bucketResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS) {
|
||||||
|
SharedFSJoinVO sharedFSView = ApiDBUtils.newSharedFSView(sharedFS);
|
||||||
|
return ApiDBUtils.newSharedFSResponse(view, sharedFSView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -172,6 +172,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
|
|||||||
userVmResponse.setCreated(userVm.getCreated());
|
userVmResponse.setCreated(userVm.getCreated());
|
||||||
userVmResponse.setLastUpdated(userVm.getLastUpdated());
|
userVmResponse.setLastUpdated(userVm.getLastUpdated());
|
||||||
userVmResponse.setDisplayVm(userVm.isDisplayVm());
|
userVmResponse.setDisplayVm(userVm.isDisplayVm());
|
||||||
|
userVmResponse.setVmType(userVm.getUserVmType());
|
||||||
|
|
||||||
if (userVm.getState() != null) {
|
if (userVm.getState() != null) {
|
||||||
userVmResponse.setState(userVm.getState().toString());
|
userVmResponse.setState(userVm.getState().toString());
|
||||||
|
|||||||
@ -365,6 +365,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
|
|||||||
@Column(name = "user_data", updatable = true, nullable = true, length = 2048)
|
@Column(name = "user_data", updatable = true, nullable = true, length = 2048)
|
||||||
private String userData;
|
private String userData;
|
||||||
|
|
||||||
|
@Column(name = "user_vm_type")
|
||||||
|
private String userVmType;
|
||||||
|
|
||||||
@Column(name = "project_id")
|
@Column(name = "project_id")
|
||||||
private long projectId;
|
private long projectId;
|
||||||
|
|
||||||
@ -819,6 +822,10 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
|
|||||||
return userData;
|
return userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserVmType() {
|
||||||
|
return userVmType;
|
||||||
|
}
|
||||||
|
|
||||||
public long getGuestOsId() {
|
public long getGuestOsId() {
|
||||||
return guestOsId;
|
return guestOsId;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4041,6 +4041,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
cmdList.add(UpdateBucketCmd.class);
|
cmdList.add(UpdateBucketCmd.class);
|
||||||
cmdList.add(DeleteBucketCmd.class);
|
cmdList.add(DeleteBucketCmd.class);
|
||||||
cmdList.add(ListBucketsCmd.class);
|
cmdList.add(ListBucketsCmd.class);
|
||||||
|
|
||||||
return cmdList;
|
return cmdList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4475,6 +4476,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
regionSecondaryEnabled = true;
|
regionSecondaryEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Integer fsVmMinCpu = Integer.parseInt(_configDao.getValue("sharedfsvm.min.cpu.count"));
|
||||||
|
final Integer fsVmMinRam = Integer.parseInt(_configDao.getValue("sharedfsvm.min.ram.size"));
|
||||||
|
|
||||||
capabilities.put("securityGroupsEnabled", securityGroupsEnabled);
|
capabilities.put("securityGroupsEnabled", securityGroupsEnabled);
|
||||||
capabilities.put("userPublicTemplateEnabled", userPublicTemplateEnabled);
|
capabilities.put("userPublicTemplateEnabled", userPublicTemplateEnabled);
|
||||||
capabilities.put("cloudStackVersion", getVersion());
|
capabilities.put("cloudStackVersion", getVersion());
|
||||||
@ -4502,6 +4506,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||||||
capabilities.put("apiLimitInterval", apiLimitInterval);
|
capabilities.put("apiLimitInterval", apiLimitInterval);
|
||||||
capabilities.put("apiLimitMax", apiLimitMax);
|
capabilities.put("apiLimitMax", apiLimitMax);
|
||||||
}
|
}
|
||||||
|
capabilities.put(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT, fsVmMinCpu);
|
||||||
|
capabilities.put(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE, fsVmMinRam);
|
||||||
|
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1035,7 +1035,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
// if VM Id is provided, attach the volume to the VM
|
// if VM Id is provided, attach the volume to the VM
|
||||||
if (cmd.getVirtualMachineId() != null) {
|
if (cmd.getVirtualMachineId() != null) {
|
||||||
try {
|
try {
|
||||||
attachVolumeToVM(cmd.getVirtualMachineId(), volume.getId(), volume.getDeviceId());
|
attachVolumeToVM(cmd.getVirtualMachineId(), volume.getId(), volume.getDeviceId(), false);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
StringBuilder message = new StringBuilder("Volume: ");
|
StringBuilder message = new StringBuilder("Volume: ");
|
||||||
message.append(volume.getUuid());
|
message.append(volume.getUuid());
|
||||||
@ -1974,7 +1974,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
boolean shrinkOk = cmd.isShrinkOk();
|
boolean shrinkOk = cmd.isShrinkOk();
|
||||||
boolean autoMigrateVolume = cmd.getAutoMigrate();
|
boolean autoMigrateVolume = cmd.getAutoMigrate();
|
||||||
|
|
||||||
VolumeVO volume = _volsDao.findById(cmd.getId());
|
return changeDiskOfferingForVolumeInternal(cmd.getId(), newDiskOfferingId, newSize, newMinIops, newMaxIops, autoMigrateVolume, shrinkOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Volume changeDiskOfferingForVolumeInternal(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean autoMigrateVolume, boolean shrinkOk) throws ResourceAllocationException {
|
||||||
|
|
||||||
|
VolumeVO volume = _volsDao.findById(volumeId);
|
||||||
if (volume == null) {
|
if (volume == null) {
|
||||||
throw new InvalidParameterValueException("No such volume");
|
throw new InvalidParameterValueException("No such volume");
|
||||||
}
|
}
|
||||||
@ -1982,10 +1988,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
/* Does the caller have authority to act on this volume? */
|
/* Does the caller have authority to act on this volume? */
|
||||||
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
|
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
|
||||||
|
|
||||||
return changeDiskOfferingForVolumeInternal(volume, newDiskOfferingId, newSize, newMinIops, newMaxIops, autoMigrateVolume, shrinkOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Volume changeDiskOfferingForVolumeInternal(VolumeVO volume, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean autoMigrateVolume, boolean shrinkOk) throws ResourceAllocationException {
|
|
||||||
long existingDiskOfferingId = volume.getDiskOfferingId();
|
long existingDiskOfferingId = volume.getDiskOfferingId();
|
||||||
DiskOfferingVO existingDiskOffering = _diskOfferingDao.findByIdIncludingRemoved(existingDiskOfferingId);
|
DiskOfferingVO existingDiskOffering = _diskOfferingDao.findByIdIncludingRemoved(existingDiskOfferingId);
|
||||||
DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newDiskOfferingId);
|
DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newDiskOfferingId);
|
||||||
@ -2345,6 +2347,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
* the actual disk size.
|
* the actual disk size.
|
||||||
*/
|
*/
|
||||||
if (currentSize > newSize) {
|
if (currentSize > newSize) {
|
||||||
|
if (shrinkOk) {
|
||||||
|
VMInstanceVO vm = _vmInstanceDao.findById(volume.getInstanceId());
|
||||||
|
if (vm != null && vm.getType().equals(VirtualMachine.Type.User)) {
|
||||||
|
UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
|
||||||
|
if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Shrink volume cannot be done on a Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (volume != null && ImageFormat.QCOW2.equals(volume.getFormat()) && !Volume.State.Allocated.equals(volume.getState()) && !StoragePoolType.StorPool.equals(volume.getPoolType())) {
|
if (volume != null && ImageFormat.QCOW2.equals(volume.getFormat()) && !Volume.State.Allocated.equals(volume.getState()) && !StoragePoolType.StorPool.equals(volume.getPoolType())) {
|
||||||
String message = "Unable to shrink volumes of type QCOW2";
|
String message = "Unable to shrink volumes of type QCOW2";
|
||||||
logger.warn(message);
|
logger.warn(message);
|
||||||
@ -2365,7 +2377,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
@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) {
|
||||||
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId());
|
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
|
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
|
||||||
@ -2473,13 +2485,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
return newVol;
|
return newVol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
|
public Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId, Boolean allowAttachForSharedFS) {
|
||||||
Account caller = CallContext.current().getCallingAccount();
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
|
||||||
VolumeInfo volumeToAttach = getAndCheckVolumeInfo(volumeId);
|
VolumeInfo volumeToAttach = getAndCheckVolumeInfo(volumeId);
|
||||||
|
|
||||||
UserVmVO vm = getAndCheckUserVmVO(vmId, volumeToAttach);
|
UserVmVO vm = getAndCheckUserVmVO(vmId, volumeToAttach);
|
||||||
|
|
||||||
|
if (!allowAttachForSharedFS && UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Can't attach a volume to a Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
checkDeviceId(deviceId, volumeToAttach, vm);
|
checkDeviceId(deviceId, volumeToAttach, vm);
|
||||||
|
|
||||||
checkNumberOfAttachedVolumes(deviceId, vm);
|
checkNumberOfAttachedVolumes(deviceId, vm);
|
||||||
@ -2894,6 +2910,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
|
|
||||||
// Check that the VM is in the correct state
|
// Check that the VM is in the correct state
|
||||||
UserVmVO vm = _userVmDao.findById(vmId);
|
UserVmVO vm = _userVmDao.findById(vmId);
|
||||||
|
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Can't detach a volume from a Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) {
|
if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) {
|
||||||
throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
|
throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
|
||||||
}
|
}
|
||||||
@ -3760,7 +3781,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
|
|
||||||
private boolean isOperationSupported(VMTemplateVO template, UserVmVO userVm) {
|
private boolean isOperationSupported(VMTemplateVO template, UserVmVO userVm) {
|
||||||
if (template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM &&
|
if (template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM &&
|
||||||
(userVm == null || !UserVmManager.CKS_NODE.equals(userVm.getUserVmType()))) {
|
(userVm == null || !UserVmManager.CKS_NODE.equals(userVm.getUserVmType()) || !UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType()))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -1055,7 +1055,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
|
|||||||
if (instanceId != null) {
|
if (instanceId != null) {
|
||||||
userVmVO = _vmDao.findById(instanceId);
|
userVmVO = _vmDao.findById(instanceId);
|
||||||
}
|
}
|
||||||
if (template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM && (userVmVO == null || !UserVmManager.CKS_NODE.equals(userVmVO.getUserVmType()))) {
|
if (template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM && (userVmVO == null || !UserVmManager.CKS_NODE.equals(userVmVO.getUserVmType()) || !UserVmManager.SHAREDFSVM.equals(userVmVO.getUserVmType()))) {
|
||||||
throw new InvalidParameterValueException("VolumeId: " + volumeId + " is for System VM , Creating snapshot against System VM volumes is not supported");
|
throw new InvalidParameterValueException("VolumeId: " + volumeId + " is for System VM , Creating snapshot against System VM volumes is not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -207,6 +207,7 @@ import com.cloud.utils.db.Transaction;
|
|||||||
import com.cloud.utils.db.TransactionCallbackNoReturn;
|
import com.cloud.utils.db.TransactionCallbackNoReturn;
|
||||||
import com.cloud.utils.db.TransactionStatus;
|
import com.cloud.utils.db.TransactionStatus;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.vm.UserVmManager;
|
||||||
import com.cloud.vm.UserVmVO;
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.VMInstanceVO;
|
import com.cloud.vm.VMInstanceVO;
|
||||||
import com.cloud.vm.VirtualMachine.State;
|
import com.cloud.vm.VirtualMachine.State;
|
||||||
@ -1185,6 +1186,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||||||
if (vm == null) {
|
if (vm == null) {
|
||||||
throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId);
|
throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId);
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
VMTemplateVO iso = _tmpltDao.findById(isoId);
|
VMTemplateVO iso = _tmpltDao.findById(isoId);
|
||||||
if (iso == null || iso.getRemoved() != null) {
|
if (iso == null || iso.getRemoved() != null) {
|
||||||
|
|||||||
@ -86,6 +86,7 @@ public interface UserVmManager extends UserVmService {
|
|||||||
static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
|
static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
|
||||||
|
|
||||||
public static final String CKS_NODE = "cksnode";
|
public static final String CKS_NODE = "cksnode";
|
||||||
|
public static final String SHAREDFSVM = "sharedfsvm";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param hostId get all of the virtual machines that belong to one host.
|
* @param hostId get all of the virtual machines that belong to one host.
|
||||||
|
|||||||
@ -957,6 +957,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
if (userVm == null) {
|
if (userVm == null) {
|
||||||
throw new InvalidParameterValueException("unable to find a virtual machine by id" + cmd.getId());
|
throw new InvalidParameterValueException("unable to find a virtual machine by id" + cmd.getId());
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
_accountMgr.checkAccess(caller, null, true, userVm);
|
_accountMgr.checkAccess(caller, null, true, userVm);
|
||||||
|
|
||||||
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId());
|
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId());
|
||||||
@ -1001,6 +1004,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
if (userVm == null) {
|
if (userVm == null) {
|
||||||
throw new InvalidParameterValueException("unable to find a virtual machine by id" + cmd.getId());
|
throw new InvalidParameterValueException("unable to find a virtual machine by id" + cmd.getId());
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId());
|
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(userVm.getTemplateId());
|
||||||
|
|
||||||
@ -1440,6 +1446,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
throw new InvalidParameterValueException("unable to find a network with id " + networkId);
|
throw new InvalidParameterValueException("unable to find a network with id " + networkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vmInstance.getUserVmType()) && network.getGuestType() == Network.GuestType.Shared) {
|
||||||
|
if ((network.getAclType() != ControlledEntity.ACLType.Account) ||
|
||||||
|
(network.getDomainId() != vmInstance.getDomainId()) ||
|
||||||
|
(network.getAccountId() != vmInstance.getAccountId())) {
|
||||||
|
throw new InvalidParameterValueException("Shared network which is not Account scoped and not belonging to the same account can not be added to a Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Account vmOwner = _accountMgr.getAccount(vmInstance.getAccountId());
|
Account vmOwner = _accountMgr.getAccount(vmInstance.getAccountId());
|
||||||
_networkModel.checkNetworkPermissions(vmOwner, network);
|
_networkModel.checkNetworkPermissions(vmOwner, network);
|
||||||
|
|
||||||
@ -1950,6 +1964,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
|
ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
|
||||||
|
|
||||||
VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
|
VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
|
||||||
|
|
||||||
Account caller = CallContext.current().getCallingAccount();
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
_accountMgr.checkAccess(caller, null, true, vmInstance);
|
_accountMgr.checkAccess(caller, null, true, vmInstance);
|
||||||
if (vmInstance == null) {
|
if (vmInstance == null) {
|
||||||
@ -2276,6 +2291,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
if (vm == null) {
|
if (vm == null) {
|
||||||
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
|
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
// When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVm is false for the caller.
|
// When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVm is false for the caller.
|
||||||
if (!_accountMgr.isAdmin(userId) && !AllowUserExpungeRecoverVm.valueIn(userId)) {
|
if (!_accountMgr.isAdmin(userId) && !AllowUserExpungeRecoverVm.valueIn(userId)) {
|
||||||
@ -2811,6 +2829,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
throw new CloudRuntimeException("Detail settings are read from OVA, it cannot be changed by API call.");
|
throw new CloudRuntimeException("Detail settings are read from OVA, it cannot be changed by API call.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UserVmVO userVm = _vmDao.findById(cmd.getId());
|
||||||
|
if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
String userData = cmd.getUserData();
|
String userData = cmd.getUserData();
|
||||||
Long userDataId = cmd.getUserdataId();
|
Long userDataId = cmd.getUserdataId();
|
||||||
String userDataDetails = null;
|
String userDataDetails = null;
|
||||||
@ -3379,6 +3402,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
if (vm == null || vm.getRemoved() != null) {
|
if (vm == null || vm.getRemoved() != null) {
|
||||||
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
|
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
if (Arrays.asList(State.Destroyed, State.Expunging).contains(vm.getState()) && !expunge) {
|
if (Arrays.asList(State.Destroyed, State.Expunging).contains(vm.getState()) && !expunge) {
|
||||||
logger.debug("Vm id=" + vmId + " is already destroyed");
|
logger.debug("Vm id=" + vmId + " is already destroyed");
|
||||||
@ -3820,7 +3846,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
|
|
||||||
// Verify that owner can use the service offering
|
// Verify that owner can use the service offering
|
||||||
_accountMgr.checkAccess(owner, serviceOffering, zone);
|
_accountMgr.checkAccess(owner, serviceOffering, zone);
|
||||||
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId), zone);
|
|
||||||
|
DiskOffering diskOffering =_diskOfferingDao.findById(diskOfferingId);
|
||||||
|
_accountMgr.checkAccess(owner, diskOffering, zone);
|
||||||
|
|
||||||
List<HypervisorType> vpcSupportedHTypes = _vpcMgr.getSupportedVpcHypervisors();
|
List<HypervisorType> vpcSupportedHTypes = _vpcMgr.getSupportedVpcHypervisors();
|
||||||
if (networkIdList == null || networkIdList.isEmpty()) {
|
if (networkIdList == null || networkIdList.isEmpty()) {
|
||||||
@ -4237,7 +4265,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.getTemplateType().equals(TemplateType.SYSTEM) && !CKS_NODE.equals(vmType)) {
|
if (template.getTemplateType().equals(TemplateType.SYSTEM) && !CKS_NODE.equals(vmType) && !SHAREDFSVM.equals(vmType)) {
|
||||||
throw new InvalidParameterValueException("Unable to use system template " + template.getId() + " to deploy a user vm");
|
throw new InvalidParameterValueException("Unable to use system template " + template.getId() + " to deploy a user vm");
|
||||||
}
|
}
|
||||||
List<VMTemplateZoneVO> listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId());
|
List<VMTemplateZoneVO> listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId());
|
||||||
@ -5084,10 +5112,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addUserVMCmdlineArgs(Long vmId, VirtualMachineProfile profile, DeployDestination dest, StringBuilder buf) {
|
private void addUserVMCmdlineArgs(Long vmId, VirtualMachineProfile profile, DeployDestination dest, StringBuilder buf) {
|
||||||
UserVmVO k8sVM = _vmDao.findById(vmId);
|
UserVmVO vm = _vmDao.findById(vmId);
|
||||||
buf.append(" template=domP");
|
buf.append(" template=domP");
|
||||||
buf.append(" name=").append(profile.getHostName());
|
buf.append(" name=").append(profile.getHostName());
|
||||||
buf.append(" type=").append(k8sVM.getUserVmType());
|
buf.append(" type=").append(vm.getUserVmType());
|
||||||
for (NicProfile nic : profile.getNics()) {
|
for (NicProfile nic : profile.getNics()) {
|
||||||
int deviceId = nic.getDeviceId();
|
int deviceId = nic.getDeviceId();
|
||||||
if (nic.getIPv4Address() == null) {
|
if (nic.getIPv4Address() == null) {
|
||||||
@ -5128,7 +5156,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vm.getId());
|
Map<String, String> details = userVmDetailsDao.listDetailsKeyPairs(vm.getId());
|
||||||
vm.setDetails(details);
|
vm.setDetails(details);
|
||||||
StringBuilder buf = profile.getBootArgsBuilder();
|
StringBuilder buf = profile.getBootArgsBuilder();
|
||||||
if (CKS_NODE.equals(vm.getUserVmType())) {
|
if (CKS_NODE.equals(vm.getUserVmType()) || SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
addUserVMCmdlineArgs(vm.getId(), profile, dest, buf);
|
addUserVMCmdlineArgs(vm.getId(), profile, dest, buf);
|
||||||
}
|
}
|
||||||
// add userdata info into vm profile
|
// add userdata info into vm profile
|
||||||
@ -5909,6 +5937,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
ex.addProxyObject(String.valueOf(vmId), "vmId");
|
ex.addProxyObject(String.valueOf(vmId), "vmId");
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
if (vm.getRemoved() != null) {
|
if (vm.getRemoved() != null) {
|
||||||
logger.trace("Vm id=" + vmId + " is already expunged");
|
logger.trace("Vm id=" + vmId + " is already expunged");
|
||||||
@ -7368,6 +7399,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
ex.addProxyObject(vm.getUuid(), "vmId");
|
ex.addProxyObject(vm.getUuid(), "vmId");
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
final Account oldAccount = _accountService.getActiveAccountById(vm.getAccountId());
|
final Account oldAccount = _accountService.getActiveAccountById(vm.getAccountId());
|
||||||
if (oldAccount == null) {
|
if (oldAccount == null) {
|
||||||
@ -7888,6 +7922,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
ex.addProxyObject(String.valueOf(vmId), "vmId");
|
ex.addProxyObject(String.valueOf(vmId), "vmId");
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
_accountMgr.checkAccess(caller, null, true, vm);
|
_accountMgr.checkAccess(caller, null, true, vm);
|
||||||
|
|
||||||
VMTemplateVO template;
|
VMTemplateVO template;
|
||||||
|
|||||||
@ -508,6 +508,9 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
|
|||||||
if (userVm == null) {
|
if (userVm == null) {
|
||||||
throw new InvalidParameterValueException("Create vm to snapshot failed due to vm: " + vmId + " is not found");
|
throw new InvalidParameterValueException("Create vm to snapshot failed due to vm: " + vmId + " is not found");
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
|
VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
|
||||||
if (vmSnapshot == null) {
|
if (vmSnapshot == null) {
|
||||||
throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found");
|
throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found");
|
||||||
|
|||||||
@ -60,6 +60,7 @@ import com.cloud.utils.db.TransactionCallbackNoReturn;
|
|||||||
import com.cloud.utils.db.TransactionStatus;
|
import com.cloud.utils.db.TransactionStatus;
|
||||||
import com.cloud.utils.fsm.StateListener;
|
import com.cloud.utils.fsm.StateListener;
|
||||||
import com.cloud.utils.fsm.StateMachine2;
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
|
import com.cloud.vm.UserVmManager;
|
||||||
import com.cloud.vm.UserVmVO;
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.VirtualMachine.Event;
|
import com.cloud.vm.VirtualMachine.Event;
|
||||||
@ -430,6 +431,9 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro
|
|||||||
if (vmInstance == null) {
|
if (vmInstance == null) {
|
||||||
throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId);
|
throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId);
|
||||||
}
|
}
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(vmInstance.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the VM is stopped
|
// Check that the VM is stopped
|
||||||
if (!vmInstance.getState().equals(State.Stopped)) {
|
if (!vmInstance.getState().equals(State.Stopped)) {
|
||||||
|
|||||||
@ -29,6 +29,8 @@ import java.util.TimerTask;
|
|||||||
|
|
||||||
import com.cloud.storage.VolumeApiService;
|
import com.cloud.storage.VolumeApiService;
|
||||||
import com.cloud.utils.fsm.NoTransitionException;
|
import com.cloud.utils.fsm.NoTransitionException;
|
||||||
|
import com.cloud.vm.UserVmManager;
|
||||||
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.VirtualMachineManager;
|
import com.cloud.vm.VirtualMachineManager;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
@ -115,6 +117,7 @@ import com.cloud.utils.db.TransactionStatus;
|
|||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.vm.VMInstanceVO;
|
import com.cloud.vm.VMInstanceVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
@ -147,6 +150,8 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||||||
@Inject
|
@Inject
|
||||||
private DiskOfferingDao diskOfferingDao;
|
private DiskOfferingDao diskOfferingDao;
|
||||||
@Inject
|
@Inject
|
||||||
|
private UserVmDao userVmDao;
|
||||||
|
@Inject
|
||||||
private ApiDispatcher apiDispatcher;
|
private ApiDispatcher apiDispatcher;
|
||||||
@Inject
|
@Inject
|
||||||
private AsyncJobManager asyncJobManager;
|
private AsyncJobManager asyncJobManager;
|
||||||
@ -279,6 +284,13 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||||||
throw new CloudRuntimeException("VM is not in running or stopped state");
|
throw new CloudRuntimeException("VM is not in running or stopped state");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vm.getType().equals(VirtualMachine.Type.User)) {
|
||||||
|
UserVmVO userVm = userVmDao.findById(vmId);
|
||||||
|
if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
validateForZone(vm.getDataCenterId());
|
validateForZone(vm.getDataCenterId());
|
||||||
|
|
||||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
||||||
@ -406,6 +418,12 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final VMInstanceVO vm = findVmById(vmId);
|
final VMInstanceVO vm = findVmById(vmId);
|
||||||
|
if (vm.getType().equals(VirtualMachine.Type.User)) {
|
||||||
|
UserVmVO userVm = userVmDao.findById(vmId);
|
||||||
|
if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
validateForZone(vm.getDataCenterId());
|
validateForZone(vm.getDataCenterId());
|
||||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
||||||
|
|
||||||
@ -473,6 +491,13 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||||||
validateForZone(vm.getDataCenterId());
|
validateForZone(vm.getDataCenterId());
|
||||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
||||||
|
|
||||||
|
if (vm.getType().equals(VirtualMachine.Type.User)) {
|
||||||
|
UserVmVO userVm = userVmDao.findById(vmId);
|
||||||
|
if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (vm.getBackupOfferingId() == null) {
|
if (vm.getBackupOfferingId() == null) {
|
||||||
throw new CloudRuntimeException("VM has not backup offering configured, cannot create backup before assigning it to a backup offering");
|
throw new CloudRuntimeException("VM has not backup offering configured, cannot create backup before assigning it to a backup offering");
|
||||||
}
|
}
|
||||||
@ -734,6 +759,12 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||||||
validateForZone(backup.getZoneId());
|
validateForZone(backup.getZoneId());
|
||||||
|
|
||||||
final VMInstanceVO vm = findVmById(vmId);
|
final VMInstanceVO vm = findVmById(vmId);
|
||||||
|
if (vm.getType().equals(VirtualMachine.Type.User)) {
|
||||||
|
UserVmVO userVm = userVmDao.findById(vmId);
|
||||||
|
if (userVm != null && UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vm);
|
||||||
|
|
||||||
if (vm.getBackupOfferingId() != null && !BackupEnableAttachDetachVolumes.value()) {
|
if (vm.getBackupOfferingId() != null && !BackupEnableAttachDetachVolumes.value()) {
|
||||||
|
|||||||
@ -0,0 +1,720 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.SharedFS.SharedFSCleanupDelay;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.SharedFS.SharedFSCleanupInterval;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.SharedFS.SharedFSFeatureEnabled;
|
||||||
|
import static org.apache.cloudstack.storage.sharedfs.SharedFS.SharedFSExpungeWorkers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import com.cloud.configuration.ConfigurationManager;
|
||||||
|
import com.cloud.dc.DataCenter;
|
||||||
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.ManagementServerException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.exception.VirtualMachineMigrationException;
|
||||||
|
import com.cloud.network.Network;
|
||||||
|
import com.cloud.network.dao.NetworkDao;
|
||||||
|
import com.cloud.network.dao.NetworkVO;
|
||||||
|
import com.cloud.org.Grouping;
|
||||||
|
import com.cloud.projects.Project;
|
||||||
|
import com.cloud.storage.DiskOfferingVO;
|
||||||
|
import com.cloud.storage.VolumeApiService;
|
||||||
|
import com.cloud.storage.VolumeVO;
|
||||||
|
import com.cloud.storage.dao.DiskOfferingDao;
|
||||||
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.Ternary;
|
||||||
|
import com.cloud.utils.component.PluggableService;
|
||||||
|
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||||
|
import com.cloud.utils.db.Filter;
|
||||||
|
import com.cloud.utils.db.GlobalLock;
|
||||||
|
import com.cloud.utils.db.JoinBuilder;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.utils.fsm.NoTransitionException;
|
||||||
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSDiskOfferingCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSServiceOfferingCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.CreateSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ExpungeSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ListSharedFSProvidersCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ListSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.DestroySharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.RecoverSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.RestartSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.StartSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.StopSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.UpdateSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
|
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.dao.SharedFSDao;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS.Event;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS.State;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.dao.SharedFSJoinDao;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
|
|
||||||
|
import com.cloud.event.ActionEvent;
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.utils.component.ManagerBase;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.vm.NicVO;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
|
||||||
|
public class SharedFSServiceImpl extends ManagerBase implements SharedFSService, Configurable, PluggableService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AccountManager accountMgr;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DataCenterDao dataCenterDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ConfigurationManager configMgr;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private VolumeApiService volumeApiService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SharedFSDao sharedFSDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SharedFSJoinDao sharedFSJoinDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private DiskOfferingDao diskOfferingDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ConfigurationDao configDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
VolumeDao volumeDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
NetworkDao networkDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
NicDao nicDao;
|
||||||
|
|
||||||
|
protected List<SharedFSProvider> sharedFSProviders;
|
||||||
|
|
||||||
|
private Map<String, SharedFSProvider> sharedFSProviderMap = new HashMap<>();
|
||||||
|
|
||||||
|
protected final StateMachine2<State, Event, SharedFS> sharedFSStateMachine;
|
||||||
|
|
||||||
|
ScheduledExecutorService _executor = null;
|
||||||
|
|
||||||
|
public SharedFSServiceImpl() {
|
||||||
|
this.sharedFSStateMachine = State.getStateMachine();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean start() {
|
||||||
|
sharedFSProviderMap.clear();
|
||||||
|
for (final SharedFSProvider provider : sharedFSProviders) {
|
||||||
|
sharedFSProviderMap.put(provider.getName(), provider);
|
||||||
|
provider.configure();
|
||||||
|
}
|
||||||
|
_executor.scheduleWithFixedDelay(new SharedFSGarbageCollector(), SharedFSCleanupInterval.value(), SharedFSCleanupInterval.value(), TimeUnit.SECONDS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean stop() {
|
||||||
|
_executor.shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SharedFSProvider> getSharedFSProviders() {
|
||||||
|
return sharedFSProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stateTransitTo(SharedFS sharedFS, Event event) {
|
||||||
|
try {
|
||||||
|
return sharedFSStateMachine.transitTo(sharedFS, event, null, sharedFSDao);
|
||||||
|
} catch (NoTransitionException e) {
|
||||||
|
String message = String.format("State transit error for Shared FileSystem %s [%s] due to exception: %s.",
|
||||||
|
sharedFS.getName(), sharedFS.getId(), e.getMessage());
|
||||||
|
logger.error(message, e);
|
||||||
|
throw new CloudRuntimeException(message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSharedFSProviders(List<SharedFSProvider> sharedFSProviders) {
|
||||||
|
this.sharedFSProviders = sharedFSProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedFSProvider getSharedFSProvider(String sharedFSProviderName) {
|
||||||
|
if (sharedFSProviderMap.containsKey(sharedFSProviderName)) {
|
||||||
|
return sharedFSProviderMap.get(sharedFSProviderName);
|
||||||
|
}
|
||||||
|
throw new CloudRuntimeException("Invalid Shared FileSystem provider name!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
|
||||||
|
int wrks = SharedFSExpungeWorkers.value();
|
||||||
|
_executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("SharedFS-Scavenger"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Class<?>> getCommands() {
|
||||||
|
final List<Class<?>> cmdList = new ArrayList<>();
|
||||||
|
if (SharedFSFeatureEnabled.value()) {
|
||||||
|
cmdList.add(ListSharedFSProvidersCmd.class);
|
||||||
|
cmdList.add(CreateSharedFSCmd.class);
|
||||||
|
cmdList.add(ListSharedFSCmd.class);
|
||||||
|
cmdList.add(UpdateSharedFSCmd.class);
|
||||||
|
cmdList.add(DestroySharedFSCmd.class);
|
||||||
|
cmdList.add(RestartSharedFSCmd.class);
|
||||||
|
cmdList.add(StartSharedFSCmd.class);
|
||||||
|
cmdList.add(StopSharedFSCmd.class);
|
||||||
|
cmdList.add(ChangeSharedFSDiskOfferingCmd.class);
|
||||||
|
cmdList.add(ChangeSharedFSServiceOfferingCmd.class);
|
||||||
|
cmdList.add(RecoverSharedFSCmd.class);
|
||||||
|
cmdList.add(ExpungeSharedFSCmd.class);
|
||||||
|
}
|
||||||
|
return cmdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataCenter validateAndGetZone(Long zoneId) {
|
||||||
|
DataCenter zone = dataCenterDao.findById(zoneId);
|
||||||
|
if (zone == null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find zone by ID: " + zoneId);
|
||||||
|
}
|
||||||
|
if (zone.getAllocationState() == Grouping.AllocationState.Disabled) {
|
||||||
|
throw new PermissionDeniedException(String.format("Cannot perform this operation, zone ID: %s is currently disabled", zone.getUuid()));
|
||||||
|
}
|
||||||
|
if (zone.getNetworkType() == DataCenter.NetworkType.Basic ||
|
||||||
|
zone.isSecurityGroupEnabled()) {
|
||||||
|
throw new PermissionDeniedException("This feature is supported only on Advanced Zone without security groups");
|
||||||
|
}
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateDiskOffering(Long diskOfferingId, Long size, Long minIops, Long maxIops, DataCenter zone) {
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
DiskOfferingVO diskOffering = diskOfferingDao.findById(diskOfferingId);
|
||||||
|
configMgr.checkDiskOfferingAccess(caller, diskOffering, zone);
|
||||||
|
|
||||||
|
if (!diskOffering.isCustomized() && size != null) {
|
||||||
|
throw new InvalidParameterValueException("Size provided with a non-custom disk offering");
|
||||||
|
}
|
||||||
|
if ((diskOffering.isCustomizedIops() == null || diskOffering.isCustomizedIops() == false) && (minIops != null || maxIops != null)) {
|
||||||
|
throw new InvalidParameterValueException("Iops provided with a non-custom-iops disk offering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_CREATE, eventDescription = "Allocating Shared FileSystem", create = true)
|
||||||
|
public SharedFS allocSharedFS(CreateSharedFSCmd cmd) {
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
|
||||||
|
long ownerId = cmd.getEntityOwnerId();
|
||||||
|
Account owner = accountMgr.getActiveAccountById(ownerId);
|
||||||
|
accountMgr.checkAccess(caller, null, true, owner);
|
||||||
|
DataCenter zone = validateAndGetZone(cmd.getZoneId());
|
||||||
|
|
||||||
|
Long diskOfferingId = cmd.getDiskOfferingId();
|
||||||
|
Long size = cmd.getSize();
|
||||||
|
Long minIops = cmd.getMinIops();
|
||||||
|
Long maxIops = cmd.getMaxIops();
|
||||||
|
validateDiskOffering(diskOfferingId, size, minIops, maxIops, zone);
|
||||||
|
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(cmd.getSharedFSProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
lifeCycle.checkPrerequisites(zone, cmd.getServiceOfferingId());
|
||||||
|
|
||||||
|
NetworkVO networkVO = networkDao.findById(cmd.getNetworkId());
|
||||||
|
if (networkVO == null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find a network with Network ID " + cmd.getNetworkId());
|
||||||
|
}
|
||||||
|
if (networkVO.getGuestType() == Network.GuestType.Shared) {
|
||||||
|
if ((networkVO.getAclType() != ControlledEntity.ACLType.Account) ||
|
||||||
|
(cmd.getDomainId() != null && (networkVO.getDomainId() != cmd.getDomainId())) ||
|
||||||
|
(networkVO.getAccountId() != owner.getAccountId())) {
|
||||||
|
throw new InvalidParameterValueException("Shared network which is not Account scoped and not belonging to the same account can not be used to create a Shared FileSystem");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedFS.FileSystemType fsType;
|
||||||
|
try {
|
||||||
|
fsType = SharedFS.FileSystemType.valueOf(cmd.getFsFormat().toUpperCase());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new InvalidParameterValueException("Invalid File system format specified. Supported formats are EXT4 and XFS");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sharedFSDao.findSharedFSByNameAccountDomain(cmd.getName(), owner.getAccountId(), cmd.getDomainId()) != null) {
|
||||||
|
throw new InvalidParameterValueException("There already exists a Shared FileSystem with this name for the given account and domain.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = new SharedFSVO(cmd.getName(), cmd.getDescription(), owner.getDomainId(),
|
||||||
|
ownerId, cmd.getZoneId(), cmd.getSharedFSProviderName(), SharedFS.Protocol.NFS,
|
||||||
|
fsType, cmd.getServiceOfferingId());
|
||||||
|
|
||||||
|
return sharedFSDao.persist(sharedFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_CREATE, eventDescription = "Deploying Shared FileSystem", async = true)
|
||||||
|
public SharedFS deploySharedFS(CreateSharedFSCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(cmd.getEntityId());
|
||||||
|
Long diskOfferingId = cmd.getDiskOfferingId();
|
||||||
|
Long size = cmd.getSize();
|
||||||
|
Long minIops = cmd.getMinIops();
|
||||||
|
Long maxIops = cmd.getMaxIops();
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(cmd.getSharedFSProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
Pair<Long, Long> result = null;
|
||||||
|
try {
|
||||||
|
result = lifeCycle.deploySharedFS(sharedFS, cmd.getNetworkId(), diskOfferingId, size, minIops, maxIops);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
stateTransitTo(sharedFS, Event.OperationFailed);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
sharedFS.setVolumeId(result.first());
|
||||||
|
sharedFS.setVmId(result.second());
|
||||||
|
sharedFSDao.update(sharedFS.getId(), sharedFS);
|
||||||
|
stateTransitTo(sharedFS, Event.OperationSucceeded);
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedFS startSharedFS(SharedFS sharedFS) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException {
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(sharedFS.getFsProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
|
||||||
|
try {
|
||||||
|
stateTransitTo(sharedFS, Event.StartRequested);
|
||||||
|
lifeCycle.startSharedFS(sharedFS);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
stateTransitTo(sharedFS, Event.OperationFailed);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
stateTransitTo(sharedFS, Event.OperationSucceeded);
|
||||||
|
sharedFS = sharedFSDao.findById(sharedFS.getId());
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_START, eventDescription = "Starting Shared FileSystem")
|
||||||
|
public SharedFS startSharedFS(Long sharedFSId) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Stopped));
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Shared FileSystem can be started only if it is in the " + validStates.toString() + " state");
|
||||||
|
}
|
||||||
|
return startSharedFS(sharedFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_STOP, eventDescription = "Stopping Shared FileSystem")
|
||||||
|
public SharedFS stopSharedFS(Long sharedFSId, Boolean forced) {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Ready));
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Shared FileSystem can be stopped only if it is in the " + State.Ready + " state");
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(sharedFS.getFsProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
try {
|
||||||
|
stateTransitTo(sharedFS, Event.StopRequested);
|
||||||
|
lifeCycle.stopSharedFS(sharedFS, forced);
|
||||||
|
} catch (Exception e) {
|
||||||
|
stateTransitTo(sharedFS, Event.OperationFailed);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
stateTransitTo(sharedFS, Event.OperationSucceeded);
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedFSVO reDeploySharedFS(SharedFSVO sharedFS) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(sharedFS.getFsProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
boolean result = lifeCycle.reDeploySharedFS(sharedFS);
|
||||||
|
return (result ? sharedFS : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_RESTART, eventDescription = "Restarting Shared FileSystem", async = true)
|
||||||
|
public SharedFS restartSharedFS(Long sharedFSId, boolean cleanup) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Ready, State.Stopped));
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Restart Shared FileSystem can be done only if the shared filesystem is in " + validStates.toString() + " states");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cleanup) {
|
||||||
|
if (!sharedFS.getState().equals(State.Stopped)) {
|
||||||
|
stopSharedFS(sharedFS.getId(), false);
|
||||||
|
}
|
||||||
|
return startSharedFS(sharedFS.getId());
|
||||||
|
} else {
|
||||||
|
return reDeploySharedFS(sharedFS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<List<Long>, Integer> searchForSharedFSIdsAndCount(ListSharedFSCmd cmd) {
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
List<Long> permittedAccounts = new ArrayList<>();
|
||||||
|
|
||||||
|
Long id = cmd.getId();
|
||||||
|
String name = cmd.getName();
|
||||||
|
Long networkId = cmd.getNetworkId();
|
||||||
|
Long diskOfferingId = cmd.getDiskOfferingId();
|
||||||
|
Long serviceOfferingId = cmd.getServiceOfferingId();
|
||||||
|
String keyword = cmd.getKeyword();
|
||||||
|
Long startIndex = cmd.getStartIndex();
|
||||||
|
Long pageSize = cmd.getPageSizeVal();
|
||||||
|
Long zoneId = cmd.getZoneId();
|
||||||
|
String accountName = cmd.getAccountName();
|
||||||
|
Long domainId = cmd.getDomainId();
|
||||||
|
Long projectId = cmd.getProjectId();
|
||||||
|
|
||||||
|
Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(domainId, cmd.isRecursive(), null);
|
||||||
|
accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false);
|
||||||
|
domainId = domainIdRecursiveListProject.first();
|
||||||
|
Boolean isRecursive = domainIdRecursiveListProject.second();
|
||||||
|
Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
||||||
|
Filter searchFilter = new Filter(SharedFSVO.class, "created", false, startIndex, pageSize);
|
||||||
|
|
||||||
|
SearchBuilder<SharedFSVO> sharedFSSearchBuilder = sharedFSDao.createSearchBuilder();
|
||||||
|
sharedFSSearchBuilder.select(null, SearchCriteria.Func.DISTINCT, sharedFSSearchBuilder.entity().getId()); // select distinct
|
||||||
|
accountMgr.buildACLSearchBuilder(sharedFSSearchBuilder, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
||||||
|
|
||||||
|
sharedFSSearchBuilder.and("id", sharedFSSearchBuilder.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
sharedFSSearchBuilder.and("name", sharedFSSearchBuilder.entity().getName(), SearchCriteria.Op.EQ);
|
||||||
|
sharedFSSearchBuilder.and("dataCenterId", sharedFSSearchBuilder.entity().getDataCenterId(), SearchCriteria.Op.EQ);
|
||||||
|
|
||||||
|
if (keyword != null) {
|
||||||
|
sharedFSSearchBuilder.and("keywordName", sharedFSSearchBuilder.entity().getName(), SearchCriteria.Op.LIKE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedFSSearchBuilder.and("serviceOfferingId", sharedFSSearchBuilder.entity().getServiceOfferingId(), SearchCriteria.Op.EQ);
|
||||||
|
|
||||||
|
if (diskOfferingId != null) {
|
||||||
|
SearchBuilder<VolumeVO> volSearch = volumeDao.createSearchBuilder();
|
||||||
|
volSearch.and("diskOfferingId", volSearch.entity().getDiskOfferingId(), SearchCriteria.Op.EQ);
|
||||||
|
sharedFSSearchBuilder.join("volSearch", volSearch, volSearch.entity().getId(), sharedFSSearchBuilder.entity().getVolumeId(), JoinBuilder.JoinType.INNER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (networkId != null) {
|
||||||
|
SearchBuilder<NicVO> nicSearch = nicDao.createSearchBuilder();
|
||||||
|
nicSearch.and("networkId", nicSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
|
||||||
|
sharedFSSearchBuilder.join("nicSearch", nicSearch, nicSearch.entity().getInstanceId(), sharedFSSearchBuilder.entity().getVmId(), JoinBuilder.JoinType.INNER);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchCriteria<SharedFSVO> sc = sharedFSSearchBuilder.create();
|
||||||
|
accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
||||||
|
|
||||||
|
if (keyword != null) {
|
||||||
|
sc.setParameters("keywordName", "%" + keyword + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
sc.setParameters("name", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
sc.setParameters("id", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoneId != null) {
|
||||||
|
sc.setParameters("dataCenterId", zoneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceOfferingId != null) {
|
||||||
|
sc.setParameters("serviceOfferingId", serviceOfferingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diskOfferingId != null) {
|
||||||
|
sc.setJoinParameters("volSearch", "diskOfferingId", diskOfferingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (networkId != null) {
|
||||||
|
sc.setJoinParameters("nicSearch", "networkId", networkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<List<SharedFSVO>, Integer> result = sharedFSDao.searchAndCount(sc, searchFilter);
|
||||||
|
List<Long> idsArray = result.first().stream().map(SharedFSVO::getId).collect(Collectors.toList());
|
||||||
|
return new Pair<List<Long>, Integer>(idsArray, result.second());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<List<SharedFSJoinVO>, Integer> searchForSharedFSInternal(ListSharedFSCmd cmd) {
|
||||||
|
Pair<List<Long>, Integer> sharedFSIds = searchForSharedFSIdsAndCount(cmd);
|
||||||
|
if (sharedFSIds.second() == 0) {
|
||||||
|
return new Pair<List<SharedFSJoinVO>, Integer>(null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SharedFSJoinVO> sharedFSs = sharedFSJoinDao.searchByIds(sharedFSIds.first().toArray(new Long[0]));
|
||||||
|
return new Pair<List<SharedFSJoinVO>, Integer>(sharedFSs, sharedFSIds.second());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListResponse<SharedFSResponse> searchForSharedFS(ResponseObject.ResponseView respView, ListSharedFSCmd cmd) {
|
||||||
|
Pair<List<SharedFSJoinVO>, Integer> result = searchForSharedFSInternal(cmd);
|
||||||
|
ListResponse<SharedFSResponse> response = new ListResponse<>();
|
||||||
|
|
||||||
|
if (cmd.getRetrieveOnlyResourceCount()) {
|
||||||
|
response.setResponses(new ArrayList<>(), result.second());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
if (accountMgr.isRootAdmin(caller.getId())) {
|
||||||
|
respView = ResponseObject.ResponseView.Full;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SharedFSResponse> sharedFSRespons = null;
|
||||||
|
if (result.second() > 0) {
|
||||||
|
sharedFSRespons = sharedFSJoinDao.createSharedFSResponses(respView, result.first().toArray(new SharedFSJoinVO[result.first().size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setResponses(sharedFSRespons, result.second());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_UPDATE, eventDescription = "Updating Shared FileSystem")
|
||||||
|
public SharedFS updateSharedFS(UpdateSharedFSCmd cmd) {
|
||||||
|
Long id = cmd.getId();
|
||||||
|
String name = cmd.getName();
|
||||||
|
String description = cmd.getDescription();
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(id);
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
|
||||||
|
if (name != null) {
|
||||||
|
sharedFS.setName(name);
|
||||||
|
}
|
||||||
|
if (description != null) {
|
||||||
|
sharedFS.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedFSDao.update(sharedFS.getId(), sharedFS);
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_CHANGE_DISK_OFFERING, eventDescription = "Change Shared FileSystem disk offering")
|
||||||
|
public SharedFS changeSharedFSDiskOffering(ChangeSharedFSDiskOfferingCmd cmd) throws ResourceAllocationException {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(cmd.getId());
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Ready, State.Stopped));
|
||||||
|
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Disk offering of the Shared FileSystem can be changed only if it is in " + validStates.toString() + " states");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long diskOfferingId = cmd.getDiskOfferingId();
|
||||||
|
Long newSize = cmd.getSize();
|
||||||
|
Long newMinIops = cmd.getMinIops();
|
||||||
|
Long newMaxIops = cmd.getMaxIops();
|
||||||
|
DataCenter zone = validateAndGetZone(sharedFS.getDataCenterId());
|
||||||
|
validateDiskOffering(diskOfferingId, newSize, newMinIops, newMaxIops, zone);
|
||||||
|
volumeApiService.changeDiskOfferingForVolumeInternal(sharedFS.getVolumeId(), diskOfferingId, newSize, newMinIops, newMaxIops, true, false);
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_CHANGE_SERVICE_OFFERING, eventDescription = "Change Shared FileSystem service offering")
|
||||||
|
public SharedFS changeSharedFSServiceOffering(ChangeSharedFSServiceOfferingCmd cmd) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ManagementServerException, VirtualMachineMigrationException {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(cmd.getId());
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Stopped));
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Service offering of the Shared FileSystem can be changed only if it is in " + validStates.toString() + " state");
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(sharedFS.getFsProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
DataCenter zone = validateAndGetZone(sharedFS.getDataCenterId());
|
||||||
|
lifeCycle.checkPrerequisites(zone, cmd.getServiceOfferingId());
|
||||||
|
|
||||||
|
sharedFS = sharedFSDao.findById(cmd.getId());
|
||||||
|
|
||||||
|
if (lifeCycle.changeSharedFSServiceOffering(sharedFS, cmd.getServiceOfferingId())) {
|
||||||
|
sharedFS.setServiceOfferingId(cmd.getServiceOfferingId());
|
||||||
|
sharedFSDao.update(sharedFS.getId(), sharedFS);
|
||||||
|
return sharedFS;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_DESTROY, eventDescription = "Destroy Shared FileSystem")
|
||||||
|
public Boolean destroySharedFS(DestroySharedFSCmd cmd) {
|
||||||
|
Long sharedFSId = cmd.getId();
|
||||||
|
Boolean expunge = cmd.isExpunge();
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
|
||||||
|
if (sharedFS.getState().equals(State.Ready) && cmd.isForced()) {
|
||||||
|
stopSharedFS(sharedFS.getId(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Stopped, State.Error));
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Shared FileSystem can be destroyed only if it is in the " + validStates.toString() + " states");
|
||||||
|
}
|
||||||
|
|
||||||
|
stateTransitTo(sharedFS, Event.DestroyRequested);
|
||||||
|
if (expunge || sharedFS.getState().equals(State.Error)) {
|
||||||
|
deleteSharedFS(sharedFSId);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_RECOVER, eventDescription = "Recover Shared FileSystem")
|
||||||
|
public SharedFS recoverSharedFS(Long sharedFSId) {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
if (!State.Destroyed.equals(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("The Shared FileSystem should be in the Destroyed state to be recovered");
|
||||||
|
}
|
||||||
|
stateTransitTo(sharedFS, Event.RecoveryRequested);
|
||||||
|
sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@ActionEvent(eventType = EventTypes.EVENT_SHAREDFS_EXPUNGE, eventDescription = "Expunge Shared FileSystem")
|
||||||
|
public void deleteSharedFS(Long sharedFSId) {
|
||||||
|
SharedFSVO sharedFS = sharedFSDao.findById(sharedFSId);
|
||||||
|
Account caller = CallContext.current().getCallingAccount();
|
||||||
|
accountMgr.checkAccess(caller, null, false, sharedFS);
|
||||||
|
|
||||||
|
Set<State> validStates = new HashSet<>(List.of(State.Destroyed, State.Expunging, State.Error));
|
||||||
|
if (!validStates.contains(sharedFS.getState())) {
|
||||||
|
throw new InvalidParameterValueException("Shared FileSystem can be expunged only if it is in the " + validStates.toString() + " states");
|
||||||
|
}
|
||||||
|
SharedFSProvider provider = getSharedFSProvider(sharedFS.getFsProviderName());
|
||||||
|
SharedFSLifeCycle lifeCycle = provider.getSharedFSLifeCycle();
|
||||||
|
stateTransitTo(sharedFS, Event.ExpungeOperation);
|
||||||
|
lifeCycle.deleteSharedFS(sharedFS);
|
||||||
|
stateTransitTo(sharedFS, Event.OperationSucceeded);
|
||||||
|
sharedFSDao.remove(sharedFS.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConfigComponentName() {
|
||||||
|
return SharedFSService.class.getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
|
return new ConfigKey<?>[]{
|
||||||
|
SharedFSCleanupInterval,
|
||||||
|
SharedFSCleanupDelay,
|
||||||
|
SharedFSFeatureEnabled,
|
||||||
|
SharedFSExpungeWorkers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected class SharedFSGarbageCollector extends ManagedContextRunnable {
|
||||||
|
|
||||||
|
public SharedFSGarbageCollector() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runInContext() {
|
||||||
|
try {
|
||||||
|
logger.trace("Shared FileSystem Garbage Collection Thread is running.");
|
||||||
|
|
||||||
|
cleanupSharedFS(true);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Caught the following Exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanupSharedFS(boolean recurring) {
|
||||||
|
GlobalLock scanLock = GlobalLock.getInternLock("sharedfsservice.cleanup");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (scanLock.lock(30)) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
List<SharedFSVO> sharedFSs = sharedFSDao.listSharedFSToBeDestroyed(new Date(System.currentTimeMillis() - ((long)SharedFSCleanupDelay.value() << 10)));
|
||||||
|
for (SharedFSVO sharedFS : sharedFSs) {
|
||||||
|
try {
|
||||||
|
stateTransitTo(sharedFS, Event.ExpungeOperation);
|
||||||
|
deleteSharedFS(sharedFS.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
stateTransitTo(sharedFS, Event.OperationFailed);
|
||||||
|
logger.error(String.format("Unable to expunge Shared FileSystem [%s] due to: [%s].", sharedFS.getUuid(), e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
scanLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
scanLock.releaseRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
// 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.storage.sharedfs.query.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
public interface SharedFSJoinDao extends GenericDao<SharedFSJoinVO, Long> {
|
||||||
|
|
||||||
|
SharedFSJoinVO newSharedFSView(SharedFS sharedFS);
|
||||||
|
|
||||||
|
SharedFSResponse newSharedFSResponse(ResponseObject.ResponseView view, SharedFSJoinVO sharedFSView);
|
||||||
|
|
||||||
|
List<SharedFSResponse> createSharedFSResponses(ResponseObject.ResponseView view, SharedFSJoinVO... sharedFSs);
|
||||||
|
|
||||||
|
List<SharedFSJoinVO> searchByIds(Long...sharedFSIds);
|
||||||
|
}
|
||||||
@ -0,0 +1,187 @@
|
|||||||
|
// 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.storage.sharedfs.query.dao;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.api.response.NicResponse;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
|
|
||||||
|
import com.cloud.api.ApiDBUtils;
|
||||||
|
import com.cloud.network.dao.NetworkDao;
|
||||||
|
import com.cloud.network.dao.NetworkVO;
|
||||||
|
import com.cloud.storage.Storage;
|
||||||
|
import com.cloud.storage.VolumeStats;
|
||||||
|
import com.cloud.user.VmDiskStatisticsVO;
|
||||||
|
import com.cloud.user.dao.VmDiskStatisticsDao;
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.vm.NicVO;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
|
||||||
|
public class SharedFSJoinDaoImpl extends GenericDaoBase<SharedFSJoinVO, Long> implements SharedFSJoinDao {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
NicDao nicDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
NetworkDao networkDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private VmDiskStatisticsDao vmDiskStatsDao;
|
||||||
|
|
||||||
|
private final SearchBuilder<SharedFSJoinVO> fsSearch;
|
||||||
|
private final SearchBuilder<SharedFSJoinVO> fsIdInSearch;
|
||||||
|
|
||||||
|
protected SharedFSJoinDaoImpl() {
|
||||||
|
fsSearch = createSearchBuilder();
|
||||||
|
fsSearch.and("id", fsSearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
fsSearch.done();
|
||||||
|
|
||||||
|
fsIdInSearch = createSearchBuilder();
|
||||||
|
fsIdInSearch.and("idIN", fsIdInSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||||
|
fsIdInSearch.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedFSJoinVO newSharedFSView(SharedFS sharedFS) {
|
||||||
|
SearchCriteria<SharedFSJoinVO> sc = fsSearch.create();
|
||||||
|
sc.setParameters("id", sharedFS.getId());
|
||||||
|
List<SharedFSJoinVO> sharedFSs = searchIncludingRemoved(sc, null, null, false);
|
||||||
|
assert sharedFSs != null && sharedFSs.size() == 1 : "No shared filesystem found for id " + sharedFS.getId();
|
||||||
|
return sharedFSs.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SharedFSResponse newSharedFSResponse(ResponseObject.ResponseView view, SharedFSJoinVO sharedFS) {
|
||||||
|
SharedFSResponse response = new SharedFSResponse();
|
||||||
|
response.setId(sharedFS.getUuid());
|
||||||
|
response.setName(sharedFS.getName());
|
||||||
|
response.setDescription(sharedFS.getDescription());
|
||||||
|
response.setState(sharedFS.getState().toString());
|
||||||
|
response.setProvider(sharedFS.getProvider());
|
||||||
|
response.setFilesystem(sharedFS.getFsType().toString());
|
||||||
|
response.setPath(SharedFS.getSharedFSPath());
|
||||||
|
response.setObjectName(SharedFS.class.getSimpleName().toLowerCase());
|
||||||
|
response.setZoneId(sharedFS.getZoneUuid());
|
||||||
|
response.setZoneName(sharedFS.getZoneName());
|
||||||
|
|
||||||
|
response.setVirtualMachineId(sharedFS.getInstanceUuid());
|
||||||
|
if (sharedFS.getInstanceState() != null) {
|
||||||
|
response.setVirtualMachineState(sharedFS.getInstanceState().toString());
|
||||||
|
}
|
||||||
|
response.setVolumeId(sharedFS.getVolumeUuid());
|
||||||
|
response.setVolumeName(sharedFS.getVolumeName());
|
||||||
|
|
||||||
|
response.setStoragePoolId(sharedFS.getPoolUuid());
|
||||||
|
response.setStoragePoolName(sharedFS.getPoolName());
|
||||||
|
|
||||||
|
final List<NicVO> nics = nicDao.listByVmId(sharedFS.getInstanceId());
|
||||||
|
if (nics.size() > 0) {
|
||||||
|
for (NicVO nicVO : nics) {
|
||||||
|
final NetworkVO network = networkDao.findById(nicVO.getNetworkId());
|
||||||
|
NicResponse nicResponse = new NicResponse();
|
||||||
|
nicResponse.setId(nicVO.getUuid());
|
||||||
|
nicResponse.setNetworkid(network.getUuid());
|
||||||
|
nicResponse.setIpaddress(nicVO.getIPv4Address());
|
||||||
|
nicResponse.setNetworkName(network.getName());
|
||||||
|
nicResponse.setObjectName("nic");
|
||||||
|
response.addNic(nicResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setAccountName(sharedFS.getAccountName());
|
||||||
|
|
||||||
|
response.setDomainId(sharedFS.getDomainUuid());
|
||||||
|
response.setDomainName(sharedFS.getDomainName());
|
||||||
|
response.setDomainName(sharedFS.getDomainPath());
|
||||||
|
|
||||||
|
response.setProjectId(sharedFS.getProjectUuid());
|
||||||
|
response.setProjectName(sharedFS.getProjectName());
|
||||||
|
|
||||||
|
response.setDiskOfferingId(sharedFS.getDiskOfferingUuid());
|
||||||
|
response.setDiskOfferingName(sharedFS.getDiskOfferingName());
|
||||||
|
response.setDiskOfferingDisplayText(sharedFS.getDiskOfferingDisplayText());
|
||||||
|
response.setIsCustomDiskOffering(sharedFS.isDiskOfferingCustom());
|
||||||
|
if (sharedFS.isDiskOfferingCustom() == true) {
|
||||||
|
response.setSize(sharedFS.getSize());
|
||||||
|
} else {
|
||||||
|
response.setSize(sharedFS.getDiskOfferingSize());
|
||||||
|
}
|
||||||
|
response.setSizeGB(sharedFS.getSize());
|
||||||
|
|
||||||
|
response.setServiceOfferingId(sharedFS.getServiceOfferingUuid());
|
||||||
|
response.setServiceOfferingName(sharedFS.getServiceOfferingName());
|
||||||
|
|
||||||
|
if (sharedFS.getProvisioningType() != null) {
|
||||||
|
response.setProvisioningType(sharedFS.getProvisioningType().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
VmDiskStatisticsVO diskStats = vmDiskStatsDao.findBy(sharedFS.getAccountId(), sharedFS.getZoneId(), sharedFS.getInstanceId(), sharedFS.getVolumeId());
|
||||||
|
if (diskStats != null) {
|
||||||
|
response.setDiskIORead(diskStats.getCurrentIORead());
|
||||||
|
response.setDiskIOWrite(diskStats.getCurrentIOWrite());
|
||||||
|
response.setDiskKbsRead((long) (diskStats.getCurrentBytesRead() / 1024.0));
|
||||||
|
response.setDiskKbsWrite((long) (diskStats.getCurrentBytesWrite() / 1024.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeStats vs = null;
|
||||||
|
if (sharedFS.getVolumeFormat() == Storage.ImageFormat.VHD || sharedFS.getVolumeFormat() == Storage.ImageFormat.QCOW2 || sharedFS.getVolumeFormat() == Storage.ImageFormat.RAW) {
|
||||||
|
if (sharedFS.getVolumePath() != null) {
|
||||||
|
vs = ApiDBUtils.getVolumeStatistics(sharedFS.getVolumePath());
|
||||||
|
}
|
||||||
|
} else if (sharedFS.getVolumeFormat() == Storage.ImageFormat.OVA) {
|
||||||
|
if (sharedFS.getVolumeChainInfo() != null) {
|
||||||
|
vs = ApiDBUtils.getVolumeStatistics(sharedFS.getVolumeChainInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vs != null) {
|
||||||
|
response.setVirtualSize(vs.getVirtualSize());
|
||||||
|
response.setPhysicalSize(vs.getPhysicalSize());
|
||||||
|
double util = (double) vs.getPhysicalSize() / vs.getVirtualSize();
|
||||||
|
DecimalFormat df = new DecimalFormat("0.0%");
|
||||||
|
response.setUtilization(df.format(util));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SharedFSResponse> createSharedFSResponses(ResponseObject.ResponseView view, SharedFSJoinVO... sharedFSs) {
|
||||||
|
List<SharedFSResponse> sharedFSRespons = new ArrayList<>();
|
||||||
|
|
||||||
|
for (SharedFSJoinVO sharedFS : sharedFSs) {
|
||||||
|
sharedFSRespons.add(newSharedFSResponse(view, sharedFS));
|
||||||
|
}
|
||||||
|
return sharedFSRespons;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SharedFSJoinVO> searchByIds(Long... sharedFSIds) {
|
||||||
|
SearchCriteria<SharedFSJoinVO> sc = fsIdInSearch.create();
|
||||||
|
sc.setParameters("idIN", sharedFSIds);
|
||||||
|
return search(sc, null, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage.sharedfs.query.vo;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS.State;
|
||||||
|
|
||||||
|
import com.cloud.api.query.vo.BaseViewVO;
|
||||||
|
import com.cloud.storage.Storage;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "shared_filesystem_view")
|
||||||
|
public class SharedFSJoinVO extends BaseViewVO implements InternalIdentity, Identity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "state")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
@Column(name = "provider")
|
||||||
|
private String provider;
|
||||||
|
|
||||||
|
@Column(name = "fs_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
SharedFS.FileSystemType fsType;
|
||||||
|
|
||||||
|
@Column(name = "size")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
@Column(name = "zone_id")
|
||||||
|
private long zoneId;
|
||||||
|
|
||||||
|
@Column(name = "zone_uuid")
|
||||||
|
private String zoneUuid;
|
||||||
|
|
||||||
|
@Column(name = "zone_name")
|
||||||
|
private String zoneName;
|
||||||
|
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private long accountId;
|
||||||
|
|
||||||
|
@Column(name = "instance_id")
|
||||||
|
private long instanceId;
|
||||||
|
|
||||||
|
@Column(name = "instance_uuid")
|
||||||
|
private String instanceUuid;
|
||||||
|
|
||||||
|
@Column(name = "instance_name")
|
||||||
|
private String instanceName;
|
||||||
|
|
||||||
|
@Column(name = "instance_state")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private VirtualMachine.State instanceState;
|
||||||
|
|
||||||
|
@Column(name = "volume_id")
|
||||||
|
private long volumeId;
|
||||||
|
|
||||||
|
@Column(name = "volume_uuid")
|
||||||
|
private String volumeUuid;
|
||||||
|
|
||||||
|
@Column(name = "volume_name")
|
||||||
|
private String volumeName;
|
||||||
|
|
||||||
|
@Column(name = "provisioning_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
Storage.ProvisioningType provisioningType;
|
||||||
|
|
||||||
|
@Column(name = "volume_format")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private Storage.ImageFormat volumeFormat;
|
||||||
|
|
||||||
|
@Column(name = "volume_path")
|
||||||
|
private String volumePath;
|
||||||
|
|
||||||
|
@Column(name = "volume_chain_info")
|
||||||
|
private String volumeChainInfo;
|
||||||
|
|
||||||
|
@Column(name = "pool_uuid")
|
||||||
|
private String poolUuid;
|
||||||
|
|
||||||
|
@Column(name = "pool_name")
|
||||||
|
private String poolName;
|
||||||
|
|
||||||
|
@Column(name = "account_name")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@Column(name = "project_uuid")
|
||||||
|
private String projectUuid;
|
||||||
|
|
||||||
|
@Column(name = "project_name")
|
||||||
|
private String projectName;
|
||||||
|
|
||||||
|
@Column(name = "domain_uuid")
|
||||||
|
private String domainUuid;
|
||||||
|
|
||||||
|
@Column(name = "domain_name")
|
||||||
|
private String domainName;
|
||||||
|
|
||||||
|
@Column(name = "domain_path")
|
||||||
|
private String domainPath;
|
||||||
|
|
||||||
|
@Column(name = "service_offering_uuid")
|
||||||
|
private String serviceOfferingUuid;
|
||||||
|
|
||||||
|
@Column(name = "service_offering_name")
|
||||||
|
private String serviceOfferingName;
|
||||||
|
|
||||||
|
@Column(name = "disk_offering_uuid")
|
||||||
|
private String diskOfferingUuid;
|
||||||
|
|
||||||
|
@Column(name = "disk_offering_name")
|
||||||
|
private String diskOfferingName;
|
||||||
|
|
||||||
|
@Column(name = "disk_offering_display_text")
|
||||||
|
private String diskOfferingDisplayText;
|
||||||
|
|
||||||
|
@Column(name = "disk_offering_size")
|
||||||
|
private long diskOfferingSize;
|
||||||
|
|
||||||
|
@Column(name = "disk_offering_custom")
|
||||||
|
private boolean diskOfferingCustom;
|
||||||
|
|
||||||
|
public SharedFSJoinVO() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProvider() {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedFS.FileSystemType getFsType() {
|
||||||
|
return fsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getZoneId() {
|
||||||
|
return zoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAccountId() {
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZoneUuid() {
|
||||||
|
return zoneUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getZoneName() {
|
||||||
|
return zoneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getInstanceId() {
|
||||||
|
return instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceUuid() {
|
||||||
|
return instanceUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceName() {
|
||||||
|
return instanceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VirtualMachine.State getInstanceState() {
|
||||||
|
return instanceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getVolumeId() {
|
||||||
|
return volumeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVolumeUuid() {
|
||||||
|
return volumeUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVolumeName() {
|
||||||
|
return volumeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Storage.ProvisioningType getProvisioningType() {
|
||||||
|
return provisioningType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Storage.ImageFormat getVolumeFormat() {
|
||||||
|
return volumeFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVolumePath() {
|
||||||
|
return volumePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVolumeChainInfo() {
|
||||||
|
return volumeChainInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPoolUuid() {
|
||||||
|
return poolUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPoolName() {
|
||||||
|
return poolName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountName() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProjectUuid() {
|
||||||
|
return projectUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProjectName() {
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomainUuid() {
|
||||||
|
return domainUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomainName() {
|
||||||
|
return domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomainPath() {
|
||||||
|
return domainPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServiceOfferingUuid() {
|
||||||
|
return serviceOfferingUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServiceOfferingName() {
|
||||||
|
return serviceOfferingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDiskOfferingUuid() {
|
||||||
|
return diskOfferingUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDiskOfferingName() {
|
||||||
|
return diskOfferingName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDiskOfferingDisplayText() {
|
||||||
|
return diskOfferingDisplayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDiskOfferingSize() {
|
||||||
|
return diskOfferingSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDiskOfferingCustom() {
|
||||||
|
return diskOfferingCustom;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2107,6 +2107,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
|||||||
throw new UnsupportedServiceException("Unmanage VM is currently allowed for guest VMs only");
|
throw new UnsupportedServiceException("Unmanage VM is currently allowed for guest VMs only");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vmVO.getType().equals(VirtualMachine.Type.User)) {
|
||||||
|
UserVmVO userVm = userVmDao.findById(vmId);
|
||||||
|
if (UserVmManager.SHAREDFSVM.equals(userVm.getUserVmType())) {
|
||||||
|
throw new InvalidParameterValueException("Operation not supported on Shared FileSystem VM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
performUnmanageVMInstancePrechecks(vmVO);
|
performUnmanageVMInstancePrechecks(vmVO);
|
||||||
|
|
||||||
Long hostId = findSuitableHostId(vmVO);
|
Long hostId = findSuitableHostId(vmVO);
|
||||||
|
|||||||
@ -370,4 +370,8 @@
|
|||||||
<bean id="vnfTemplateManager" class="org.apache.cloudstack.storage.template.VnfTemplateManagerImpl" />
|
<bean id="vnfTemplateManager" class="org.apache.cloudstack.storage.template.VnfTemplateManagerImpl" />
|
||||||
|
|
||||||
<bean id="volumeImportUnmanageManager" class="org.apache.cloudstack.storage.volume.VolumeImportUnmanageManagerImpl" />
|
<bean id="volumeImportUnmanageManager" class="org.apache.cloudstack.storage.volume.VolumeImportUnmanageManagerImpl" />
|
||||||
|
|
||||||
|
<bean id="sharedFSServiceImpl" class="org.apache.cloudstack.storage.sharedfs.SharedFSServiceImpl">
|
||||||
|
<property name="sharedFSProviders" value="#{sharedFSProvidersRegistry.registered}" />
|
||||||
|
</bean>
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -474,44 +474,44 @@ public class VolumeApiServiceImplTest {
|
|||||||
// Negative test - try to attach non-root non-datadisk volume
|
// Negative test - try to attach non-root non-datadisk volume
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
public void attachIncorrectDiskType() throws NoSuchFieldException, IllegalAccessException {
|
public void attachIncorrectDiskType() throws NoSuchFieldException, IllegalAccessException {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(1L, 5L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(1L, 5L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative test - attach root volume to running vm
|
// Negative test - attach root volume to running vm
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
public void attachRootDiskToRunningVm() throws NoSuchFieldException, IllegalAccessException {
|
public void attachRootDiskToRunningVm() throws NoSuchFieldException, IllegalAccessException {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(1L, 6L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(1L, 6L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative test - attach root volume to non-xen vm
|
// Negative test - attach root volume to non-xen vm
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
public void attachRootDiskToHyperVm() throws NoSuchFieldException, IllegalAccessException {
|
public void attachRootDiskToHyperVm() throws NoSuchFieldException, IllegalAccessException {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(3L, 6L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(3L, 6L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative test - attach root volume from the managed data store
|
// Negative test - attach root volume from the managed data store
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
public void attachRootDiskOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException {
|
public void attachRootDiskOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(2L, 7L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(2L, 7L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative test - root volume can't be attached to the vm already having a root volume attached
|
// Negative test - root volume can't be attached to the vm already having a root volume attached
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
public void attachRootDiskToVmHavingRootDisk() throws NoSuchFieldException, IllegalAccessException {
|
public void attachRootDiskToVmHavingRootDisk() throws NoSuchFieldException, IllegalAccessException {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(4L, 6L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(4L, 6L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative test - root volume in uploaded state can't be attached
|
// Negative test - root volume in uploaded state can't be attached
|
||||||
@Test(expected = InvalidParameterValueException.class)
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
public void attachRootInUploadedState() throws NoSuchFieldException, IllegalAccessException {
|
public void attachRootInUploadedState() throws NoSuchFieldException, IllegalAccessException {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(2L, 8L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(2L, 8L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Positive test - attach ROOT volume in correct state, to the vm not having root volume attached
|
// Positive test - attach ROOT volume in correct state, to the vm not having root volume attached
|
||||||
@Test
|
@Test
|
||||||
public void attachRootVolumePositive() throws NoSuchFieldException, IllegalAccessException {
|
public void attachRootVolumePositive() throws NoSuchFieldException, IllegalAccessException {
|
||||||
thrown.expect(NullPointerException.class);
|
thrown.expect(NullPointerException.class);
|
||||||
volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L);
|
volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Negative test - attach data volume, to the vm on non-kvm hypervisor
|
// Negative test - attach data volume, to the vm on non-kvm hypervisor
|
||||||
@ -520,7 +520,7 @@ public class VolumeApiServiceImplTest {
|
|||||||
DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class);
|
DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class);
|
||||||
when(diskOffering.getEncrypt()).thenReturn(true);
|
when(diskOffering.getEncrypt()).thenReturn(true);
|
||||||
when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering);
|
when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering);
|
||||||
volumeApiServiceImpl.attachVolumeToVM(2L, 10L, 1L);
|
volumeApiServiceImpl.attachVolumeToVM(2L, 10L, 1L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Positive test - attach data volume, to the vm on kvm hypervisor
|
// Positive test - attach data volume, to the vm on kvm hypervisor
|
||||||
@ -530,7 +530,7 @@ public class VolumeApiServiceImplTest {
|
|||||||
DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class);
|
DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class);
|
||||||
when(diskOffering.getEncrypt()).thenReturn(true);
|
when(diskOffering.getEncrypt()).thenReturn(true);
|
||||||
when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering);
|
when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering);
|
||||||
volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L);
|
volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume not Ready
|
// volume not Ready
|
||||||
@ -640,7 +640,7 @@ public class VolumeApiServiceImplTest {
|
|||||||
when(_dcDao.findById(anyLong())).thenReturn(zoneWithDisabledLocalStorage);
|
when(_dcDao.findById(anyLong())).thenReturn(zoneWithDisabledLocalStorage);
|
||||||
when(zoneWithDisabledLocalStorage.isLocalStorageEnabled()).thenReturn(true);
|
when(zoneWithDisabledLocalStorage.isLocalStorageEnabled()).thenReturn(true);
|
||||||
try {
|
try {
|
||||||
volumeApiServiceImpl.attachVolumeToVM(2L, 9L, null);
|
volumeApiServiceImpl.attachVolumeToVM(2L, 9L, null, false);
|
||||||
} catch (InvalidParameterValueException e) {
|
} catch (InvalidParameterValueException e) {
|
||||||
Assert.assertEquals(e.getMessage(), ("primary storage resource limit check failed"));
|
Assert.assertEquals(e.getMessage(), ("primary storage resource limit check failed"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -264,4 +264,5 @@ public class MockNetworkDaoImpl extends GenericDaoBase<NetworkVO, Long> implemen
|
|||||||
public List<NetworkVO> getAllPersistentNetworksFromZone(long dataCenterId) {
|
public List<NetworkVO> getAllPersistentNetworksFromZone(long dataCenterId) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,665 @@
|
|||||||
|
// 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.storage.sharedfs;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSDiskOfferingCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ChangeSharedFSServiceOfferingCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.CreateSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.DestroySharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.ListSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.api.command.user.storage.sharedfs.UpdateSharedFSCmd;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.dao.SharedFSDao;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.dao.SharedFSJoinDao;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.configuration.ConfigurationManager;
|
||||||
|
import com.cloud.dc.DataCenterVO;
|
||||||
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.ManagementServerException;
|
||||||
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.exception.VirtualMachineMigrationException;
|
||||||
|
import com.cloud.network.Network;
|
||||||
|
import com.cloud.network.dao.NetworkDao;
|
||||||
|
import com.cloud.network.dao.NetworkVO;
|
||||||
|
import com.cloud.org.Grouping;
|
||||||
|
import com.cloud.storage.DiskOfferingVO;
|
||||||
|
import com.cloud.storage.VolumeApiService;
|
||||||
|
import com.cloud.storage.VolumeVO;
|
||||||
|
import com.cloud.storage.dao.DiskOfferingDao;
|
||||||
|
import com.cloud.storage.dao.VolumeDao;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.db.GlobalLock;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.utils.fsm.NoTransitionException;
|
||||||
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
|
import com.cloud.vm.NicVO;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class SharedFSServiceImplTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private AccountManager accountMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SharedFSDao sharedFSDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SharedFSJoinDao sharedFSJoinDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DataCenterDao dataCenterDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DiskOfferingDao diskOfferingDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VolumeDao volumeDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NicDao nicDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NetworkDao networkDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ConfigurationManager configMgr;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private VolumeApiService volumeApiService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SharedFSProvider provider;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SharedFSLifeCycle lifeCycle;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
private SharedFSServiceImpl sharedFSServiceImpl;
|
||||||
|
|
||||||
|
private static final long s_ownerId = 1L;
|
||||||
|
private static final long s_zoneId = 2L;
|
||||||
|
private static final long s_diskOfferingId = 3L;
|
||||||
|
private static final long s_serviceOfferingId = 4L;
|
||||||
|
private static final long s_domainId = 5L;
|
||||||
|
private static final long s_volumeId = 6L;
|
||||||
|
private static final long s_vmId = 7L;
|
||||||
|
private static final long s_networkId = 8L;
|
||||||
|
private static final long s_sharedFSId = 9L;
|
||||||
|
private static final long s_size = 10L;
|
||||||
|
private static final long s_minIops = 1000L;
|
||||||
|
private static final long s_maxIops = 2000L;
|
||||||
|
private static final String s_providerName = "SHAREDFSVM";
|
||||||
|
private static final String s_fsFormat = "EXT4";
|
||||||
|
private static final String s_name = "TestSharedFS";
|
||||||
|
private static final String s_description = "Test Description";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
Account owner;
|
||||||
|
@Mock
|
||||||
|
protected StateMachine2<SharedFS.State, SharedFS.Event, SharedFS> _stateMachine;
|
||||||
|
|
||||||
|
private MockedStatic<CallContext> callContextMocked;
|
||||||
|
|
||||||
|
private AutoCloseable closeable;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
closeable = MockitoAnnotations.openMocks(this);
|
||||||
|
callContextMocked = mockStatic(CallContext.class);
|
||||||
|
CallContext callContextMock = mock(CallContext.class);
|
||||||
|
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||||
|
when(callContextMock.getCallingAccount()).thenReturn(owner);
|
||||||
|
when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(owner);
|
||||||
|
|
||||||
|
Map<String, SharedFSProvider> mockProviderMap = new HashMap<>();
|
||||||
|
mockProviderMap.put(s_providerName, provider);
|
||||||
|
ReflectionTestUtils.setField(sharedFSServiceImpl, "sharedFSProviderMap", mockProviderMap);
|
||||||
|
when(sharedFSServiceImpl.getSharedFSProvider(s_providerName)).thenReturn(provider);
|
||||||
|
when(provider.getSharedFSLifeCycle()).thenReturn(lifeCycle);
|
||||||
|
ReflectionTestUtils.setField(sharedFSServiceImpl, "sharedFSStateMachine", _stateMachine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
callContextMocked.close();
|
||||||
|
closeable.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CreateSharedFSCmd getMockCreateSharedFSCmd() {
|
||||||
|
CreateSharedFSCmd cmd = mock(CreateSharedFSCmd.class);
|
||||||
|
when(cmd.getEntityOwnerId()).thenReturn(s_ownerId);
|
||||||
|
when(cmd.getZoneId()).thenReturn(s_zoneId);
|
||||||
|
when(cmd.getDiskOfferingId()).thenReturn(s_diskOfferingId);
|
||||||
|
when(cmd.getSize()).thenReturn(s_size);
|
||||||
|
when(cmd.getMinIops()).thenReturn(s_minIops);
|
||||||
|
when(cmd.getMaxIops()).thenReturn(s_maxIops);
|
||||||
|
when(cmd.getSharedFSProviderName()).thenReturn(s_providerName);
|
||||||
|
when(cmd.getServiceOfferingId()).thenReturn(s_serviceOfferingId);
|
||||||
|
when(cmd.getNetworkId()).thenReturn(s_networkId);
|
||||||
|
when(cmd.getFsFormat()).thenReturn(s_fsFormat);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedFSVO getMockSharedFS() {
|
||||||
|
SharedFSVO sharedFS = new SharedFSVO(s_name, s_description, s_domainId, s_ownerId, s_zoneId,
|
||||||
|
s_providerName, SharedFS.Protocol.NFS, SharedFS.FileSystemType.valueOf(s_fsFormat), s_serviceOfferingId);
|
||||||
|
return sharedFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeploySharedFS() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, NoTransitionException, OperationTimedoutException {
|
||||||
|
CreateSharedFSCmd cmd = getMockCreateSharedFSCmd();
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(0L)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
Pair<Long, Long> result = new Pair<>(s_volumeId, s_vmId);
|
||||||
|
when(lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops)).thenReturn(result);
|
||||||
|
when(sharedFSDao.update(sharedFS.getId(), sharedFS)).thenReturn(true);
|
||||||
|
|
||||||
|
Assert.assertEquals(sharedFSServiceImpl.deploySharedFS(cmd), sharedFS);
|
||||||
|
Assert.assertEquals(Optional.ofNullable(sharedFS.getVmId()), Optional.ofNullable(s_vmId));
|
||||||
|
Assert.assertEquals(Optional.ofNullable(sharedFS.getVolumeId()), Optional.ofNullable(s_volumeId));
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationSucceeded, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeploySharedFSException() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, NoTransitionException, OperationTimedoutException {
|
||||||
|
CreateSharedFSCmd cmd = getMockCreateSharedFSCmd();
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(0L)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
when(lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops)).thenThrow(new CloudRuntimeException(""));
|
||||||
|
|
||||||
|
Assert.assertThrows(CloudRuntimeException.class, () -> sharedFSServiceImpl.deploySharedFS(cmd));
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationFailed, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, never()).transitTo(sharedFS, SharedFS.Event.OperationSucceeded, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllocSharedFS() throws NoTransitionException {
|
||||||
|
CreateSharedFSCmd cmd = getMockCreateSharedFSCmd();
|
||||||
|
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(null);
|
||||||
|
Assert.assertThrows(InvalidParameterValueException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||||
|
|
||||||
|
DiskOfferingVO diskOfferingVO = mock(DiskOfferingVO.class);
|
||||||
|
when(diskOfferingDao.findById(s_diskOfferingId)).thenReturn(diskOfferingVO);
|
||||||
|
when(diskOfferingVO.isCustomized()).thenReturn(true);
|
||||||
|
when(diskOfferingVO.isCustomizedIops()).thenReturn(true);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
|
||||||
|
when(cmd.getNetworkId()).thenReturn(s_networkId);
|
||||||
|
NetworkVO networkVO = mock(NetworkVO.class);
|
||||||
|
when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
|
||||||
|
when(networkDao.findById(s_networkId)).thenReturn(networkVO);
|
||||||
|
|
||||||
|
sharedFSServiceImpl.allocSharedFS(cmd);
|
||||||
|
Assert.assertEquals(Optional.ofNullable(sharedFS.getAccountId()), Optional.ofNullable(s_ownerId));
|
||||||
|
Assert.assertEquals(Optional.ofNullable(sharedFS.getDataCenterId()), Optional.ofNullable(s_zoneId));
|
||||||
|
Assert.assertEquals(Optional.ofNullable(sharedFS.getServiceOfferingId()), Optional.ofNullable(s_serviceOfferingId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllocSharedFSInvalidZone() {
|
||||||
|
CreateSharedFSCmd cmd = getMockCreateSharedFSCmd();
|
||||||
|
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(null);
|
||||||
|
Assert.assertThrows(InvalidParameterValueException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Disabled);
|
||||||
|
Assert.assertThrows(PermissionDeniedException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||||
|
when(zone.isSecurityGroupEnabled()).thenReturn(true);
|
||||||
|
Assert.assertThrows(PermissionDeniedException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tesAllocSharedFSInvalidDiskOffering() {
|
||||||
|
CreateSharedFSCmd cmd = getMockCreateSharedFSCmd();
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||||
|
|
||||||
|
DiskOfferingVO diskOfferingVO = mock(DiskOfferingVO.class);
|
||||||
|
when(diskOfferingDao.findById(s_diskOfferingId)).thenReturn(diskOfferingVO);
|
||||||
|
when(diskOfferingVO.isCustomized()).thenReturn(false);
|
||||||
|
Assert.assertThrows(InvalidParameterValueException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
|
||||||
|
when(diskOfferingVO.isCustomized()).thenReturn(true);
|
||||||
|
when(diskOfferingVO.isCustomizedIops()).thenReturn(false);
|
||||||
|
Assert.assertThrows(InvalidParameterValueException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllocSharedFSInvalidFsFormat() {
|
||||||
|
CreateSharedFSCmd cmd = getMockCreateSharedFSCmd();
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||||
|
|
||||||
|
DiskOfferingVO diskOfferingVO = mock(DiskOfferingVO.class);
|
||||||
|
when(diskOfferingDao.findById(s_diskOfferingId)).thenReturn(diskOfferingVO);
|
||||||
|
when(diskOfferingVO.isCustomized()).thenReturn(true);
|
||||||
|
when(diskOfferingVO.isCustomizedIops()).thenReturn(true);
|
||||||
|
|
||||||
|
when(cmd.getNetworkId()).thenReturn(s_networkId);
|
||||||
|
NetworkVO networkVO = mock(NetworkVO.class);
|
||||||
|
when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
|
||||||
|
when(networkDao.findById(s_networkId)).thenReturn(networkVO);
|
||||||
|
|
||||||
|
when(cmd.getFsFormat()).thenReturn("ext2");
|
||||||
|
Assert.assertThrows(InvalidParameterValueException.class, () -> sharedFSServiceImpl.allocSharedFS(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartSharedFS() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException, NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
|
||||||
|
Assert.assertEquals(sharedFSServiceImpl.startSharedFS(s_sharedFSId), sharedFS);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.StartRequested, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationSucceeded, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartSharedFSException() throws ResourceUnavailableException, InsufficientCapacityException, OperationTimedoutException, NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
doThrow(CloudRuntimeException.class).when(lifeCycle).startSharedFS(sharedFS);
|
||||||
|
|
||||||
|
Assert.assertThrows(CloudRuntimeException.class, () -> sharedFSServiceImpl.startSharedFS(s_sharedFSId));
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.StartRequested, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationFailed, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testStartSharedFSInvalidState() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, OperationTimedoutException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Ready);
|
||||||
|
sharedFSServiceImpl.startSharedFS(s_sharedFSId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStopSharedFS() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Ready);
|
||||||
|
Assert.assertEquals(sharedFSServiceImpl.stopSharedFS(s_sharedFSId, false), sharedFS);
|
||||||
|
verify(lifeCycle, Mockito.times(1)).stopSharedFS(any(), any());
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.StopRequested, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationSucceeded, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStopSharedFSException() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Ready);
|
||||||
|
doThrow(CloudRuntimeException.class).when(lifeCycle).stopSharedFS(sharedFS, false);
|
||||||
|
|
||||||
|
Assert.assertThrows(CloudRuntimeException.class, () -> sharedFSServiceImpl.stopSharedFS(s_sharedFSId, false));
|
||||||
|
verify(lifeCycle, Mockito.times(1)).stopSharedFS(any(), any());
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.StopRequested, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationFailed, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testStopSharedFSInvalidState() {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
sharedFSServiceImpl.stopSharedFS(s_sharedFSId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestartSharedFSWithoutCleanup() throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
sharedFSServiceImpl.restartSharedFS(s_sharedFSId, false);
|
||||||
|
verify(lifeCycle, never()).stopSharedFS(any(), any());
|
||||||
|
verify(lifeCycle, Mockito.times(1)).startSharedFS(any());
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.StartRequested, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationSucceeded, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestartSharedFSWithCleanup() throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Ready);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
|
||||||
|
when(lifeCycle.reDeploySharedFS(sharedFS)).thenReturn(true);
|
||||||
|
sharedFSServiceImpl.restartSharedFS(s_sharedFSId, true);
|
||||||
|
verify(lifeCycle, never()).stopSharedFS(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateSharedFS() {
|
||||||
|
String newName = "New SharedFS";
|
||||||
|
String newDescription = "New SharedFS Description";
|
||||||
|
UpdateSharedFSCmd cmd = mock(UpdateSharedFSCmd.class);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
when(cmd.getName()).thenReturn(newName);
|
||||||
|
when(cmd.getDescription()).thenReturn(newDescription);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
sharedFSServiceImpl.updateSharedFS(cmd);
|
||||||
|
Assert.assertEquals(sharedFS.getName(), newName);
|
||||||
|
Assert.assertEquals(sharedFS.getDescription(), newDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangeSharedFSDiskOffering() throws ResourceAllocationException {
|
||||||
|
Long newSize = 200L;
|
||||||
|
Long newMinIops = 2000L;
|
||||||
|
Long newMaxIops = 4000L;
|
||||||
|
Long newDiskOfferingId = 10L;
|
||||||
|
ChangeSharedFSDiskOfferingCmd cmd = mock(ChangeSharedFSDiskOfferingCmd.class);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
when(cmd.getDiskOfferingId()).thenReturn(newDiskOfferingId);
|
||||||
|
when(cmd.getSize()).thenReturn(newSize);
|
||||||
|
when(cmd.getMinIops()).thenReturn(newMinIops);
|
||||||
|
when(cmd.getMaxIops()).thenReturn(newMaxIops);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Ready);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||||
|
|
||||||
|
DiskOfferingVO diskOfferingVO = mock(DiskOfferingVO.class);
|
||||||
|
when(diskOfferingDao.findById(newDiskOfferingId)).thenReturn(diskOfferingVO);
|
||||||
|
when(diskOfferingVO.isCustomized()).thenReturn(true);
|
||||||
|
when(diskOfferingVO.isCustomizedIops()).thenReturn(true);
|
||||||
|
|
||||||
|
sharedFSServiceImpl.changeSharedFSDiskOffering(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testChangeSharedFSDiskOfferingInvalidState() throws ResourceAllocationException {
|
||||||
|
ChangeSharedFSDiskOfferingCmd cmd = mock(ChangeSharedFSDiskOfferingCmd.class);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Destroyed);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
sharedFSServiceImpl.changeSharedFSDiskOffering(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangeSharedFSServiceOffering() throws ResourceUnavailableException, InsufficientCapacityException, ManagementServerException, OperationTimedoutException, NoTransitionException, VirtualMachineMigrationException {
|
||||||
|
ChangeSharedFSServiceOfferingCmd cmd = mock(ChangeSharedFSServiceOfferingCmd.class);
|
||||||
|
Long newServiceOfferingId = 100L;
|
||||||
|
when(cmd.getServiceOfferingId()).thenReturn(newServiceOfferingId);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
DataCenterVO zone = mock(DataCenterVO.class);
|
||||||
|
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
|
||||||
|
when(zone.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
|
||||||
|
|
||||||
|
when(lifeCycle.changeSharedFSServiceOffering(sharedFS, newServiceOfferingId)).thenReturn(true);
|
||||||
|
|
||||||
|
sharedFSServiceImpl.changeSharedFSServiceOffering(cmd);
|
||||||
|
Assert.assertEquals(Optional.ofNullable(sharedFS.getServiceOfferingId()), Optional.ofNullable(newServiceOfferingId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testChangeSharedFSServiceOfferingInvalidState() throws ResourceUnavailableException, InsufficientCapacityException, ManagementServerException, OperationTimedoutException, VirtualMachineMigrationException {
|
||||||
|
ChangeSharedFSServiceOfferingCmd cmd = mock(ChangeSharedFSServiceOfferingCmd.class);
|
||||||
|
Long newServiceOfferingId = 100L;
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Starting);
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
|
||||||
|
sharedFSServiceImpl.changeSharedFSServiceOffering(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDestroySharedFS() throws NoTransitionException {
|
||||||
|
DestroySharedFSCmd cmd = mock(DestroySharedFSCmd.class);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
when(cmd.isExpunge()).thenReturn(false);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
|
||||||
|
Assert.assertEquals(sharedFSServiceImpl.destroySharedFS(cmd), true);
|
||||||
|
verify(lifeCycle, never()).deleteSharedFS(any());
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.DestroyRequested, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testDestroySharedFSInvalidState() {
|
||||||
|
DestroySharedFSCmd cmd = mock(DestroySharedFSCmd.class);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
when(cmd.isExpunge()).thenReturn(false);
|
||||||
|
when(cmd.isForced()).thenReturn(false);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Ready);
|
||||||
|
|
||||||
|
sharedFSServiceImpl.destroySharedFS(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRecoverSharedFS() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Destroyed);
|
||||||
|
Assert.assertEquals(sharedFSServiceImpl.recoverSharedFS(s_sharedFSId), sharedFS);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.RecoveryRequested, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testRecoverSharedFSInvalidState() {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Expunged);
|
||||||
|
sharedFSServiceImpl.recoverSharedFS(s_sharedFSId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteSharedFS() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Destroyed);
|
||||||
|
sharedFSServiceImpl.deleteSharedFS(s_sharedFSId);
|
||||||
|
verify(lifeCycle, Mockito.times(1)).deleteSharedFS(any());
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.ExpungeOperation, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test (expected = CloudRuntimeException.class)
|
||||||
|
public void testDeleteSharedFSTransitionException() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Destroyed);
|
||||||
|
when(_stateMachine.transitTo(sharedFS, SharedFS.Event.ExpungeOperation, null, sharedFSDao)).thenThrow(new NoTransitionException(""));
|
||||||
|
sharedFSServiceImpl.deleteSharedFS(s_sharedFSId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testDeleteSharedFSInvalidState() {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sharedFSDao.findById(s_sharedFSId)).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
sharedFSServiceImpl.deleteSharedFS(s_sharedFSId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListSharedFSCmd getMockListSharedFSCmd() {
|
||||||
|
ListSharedFSCmd cmd = mock(ListSharedFSCmd.class);
|
||||||
|
when(cmd.getId()).thenReturn(s_sharedFSId);
|
||||||
|
when(cmd.getName()).thenReturn(s_name);
|
||||||
|
when(cmd.getZoneId()).thenReturn(s_zoneId);
|
||||||
|
when(cmd.getDiskOfferingId()).thenReturn(s_diskOfferingId);
|
||||||
|
when(cmd.getServiceOfferingId()).thenReturn(s_serviceOfferingId);
|
||||||
|
when(cmd.getAccountName()).thenReturn("account");
|
||||||
|
when(cmd.getDomainId()).thenReturn(s_domainId);
|
||||||
|
when(cmd.getNetworkId()).thenReturn(s_networkId);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchForSharedFS() {
|
||||||
|
SearchBuilder<SharedFSVO> sb = mock(SearchBuilder.class);
|
||||||
|
when(sharedFSDao.createSearchBuilder()).thenReturn(sb);
|
||||||
|
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
when(sb.entity()).thenReturn(sharedFS);
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "id", s_sharedFSId);
|
||||||
|
|
||||||
|
VolumeVO volume = mock(VolumeVO.class);
|
||||||
|
SearchBuilder<VolumeVO> volumeSb = mock(SearchBuilder.class);
|
||||||
|
when(volumeSb.entity()).thenReturn(volume);
|
||||||
|
when(volumeDao.createSearchBuilder()).thenReturn(volumeSb);
|
||||||
|
|
||||||
|
NicVO nic = mock(NicVO.class);
|
||||||
|
SearchBuilder<NicVO> nicSb = mock(SearchBuilder.class);
|
||||||
|
when(nicSb.entity()).thenReturn(nic);
|
||||||
|
when(nicDao.createSearchBuilder()).thenReturn(nicSb);
|
||||||
|
|
||||||
|
SearchCriteria<SharedFSVO> sc = mock(SearchCriteria.class);
|
||||||
|
Mockito.when(sb.create()).thenReturn(sc);
|
||||||
|
|
||||||
|
Pair<List<SharedFSVO>, Integer> result = new Pair<>(List.of(sharedFS), 1);
|
||||||
|
when(sharedFSDao.searchAndCount(any(), any())).thenReturn(result);
|
||||||
|
SharedFSJoinVO sharedFSJoinVO = mock(SharedFSJoinVO.class);
|
||||||
|
when(sharedFSJoinDao.searchByIds(List.of(s_sharedFSId).toArray(new Long[0]))).thenReturn(List.of(sharedFSJoinVO));
|
||||||
|
|
||||||
|
when(owner.getId()).thenReturn(s_ownerId);
|
||||||
|
when(accountMgr.isRootAdmin(any())).thenReturn(true);
|
||||||
|
when(sharedFSJoinDao.createSharedFSResponses(any(), any())).thenReturn(null);
|
||||||
|
|
||||||
|
ListSharedFSCmd cmd = getMockListSharedFSCmd();
|
||||||
|
sharedFSServiceImpl.searchForSharedFS(ResponseObject.ResponseView.Restricted, cmd);
|
||||||
|
|
||||||
|
verify(sc, times(1)).setParameters("id", s_sharedFSId);
|
||||||
|
verify(sc, times(1)).setParameters("name", s_name);
|
||||||
|
verify(sc, times(1)).setParameters("dataCenterId", s_zoneId);
|
||||||
|
verify(sc, times(1)).setParameters("serviceOfferingId", s_serviceOfferingId);
|
||||||
|
verify(sc, times(1)).setJoinParameters("volSearch", "diskOfferingId", s_diskOfferingId);
|
||||||
|
verify(sc, times(1)).setJoinParameters("nicSearch", "networkId", s_networkId);
|
||||||
|
verify(sharedFSDao, times(1)).searchAndCount(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanupSharedFS() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Destroyed);
|
||||||
|
when(sharedFSDao.listSharedFSToBeDestroyed(any(Date.class))).thenReturn(List.of(sharedFS));
|
||||||
|
try (MockedStatic<GlobalLock> globalLockMocked = Mockito.mockStatic(GlobalLock.class)) {
|
||||||
|
GlobalLock scanlock = mock(GlobalLock.class);
|
||||||
|
when(GlobalLock.getInternLock("sharedfsservice.cleanup")).thenReturn(scanlock);
|
||||||
|
when(scanlock.lock(30)).thenReturn(true);
|
||||||
|
sharedFSServiceImpl.cleanupSharedFS(true);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.ExpungeOperation, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationFailed, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanupSharedFSInvalidState() throws NoTransitionException {
|
||||||
|
SharedFSVO sharedFS = getMockSharedFS();
|
||||||
|
ReflectionTestUtils.setField(sharedFS, "state", SharedFS.State.Stopped);
|
||||||
|
when(sharedFSDao.listSharedFSToBeDestroyed(any(Date.class))).thenReturn(List.of(sharedFS));
|
||||||
|
try (MockedStatic<GlobalLock> globalLockMocked = Mockito.mockStatic(GlobalLock.class)) {
|
||||||
|
GlobalLock scanlock = mock(GlobalLock.class);
|
||||||
|
when(GlobalLock.getInternLock("sharedfsservice.cleanup")).thenReturn(scanlock);
|
||||||
|
when(scanlock.lock(30)).thenReturn(true);
|
||||||
|
sharedFSServiceImpl.cleanupSharedFS(true);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.ExpungeOperation, null, sharedFSDao);
|
||||||
|
verify(_stateMachine, times(1)).transitTo(sharedFS, SharedFS.Event.OperationFailed, null, sharedFSDao);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
// 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.storage.sharedfs.query.dao;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.SharedFSVO;
|
||||||
|
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.api.ApiDBUtils;
|
||||||
|
import com.cloud.network.dao.NetworkDao;
|
||||||
|
import com.cloud.network.dao.NetworkVO;
|
||||||
|
import com.cloud.storage.Storage;
|
||||||
|
import com.cloud.storage.VolumeStats;
|
||||||
|
import com.cloud.user.VmDiskStatisticsVO;
|
||||||
|
import com.cloud.user.dao.VmDiskStatisticsDao;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.vm.NicVO;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class SharedFSJoinDaoImplTest {
|
||||||
|
@Mock
|
||||||
|
NicDao nicDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
NetworkDao networkDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private VmDiskStatisticsDao vmDiskStatsDao;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
SharedFSJoinDaoImpl sharedFSJoinDao;
|
||||||
|
|
||||||
|
private AutoCloseable closeable;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
closeable = MockitoAnnotations.openMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
closeable.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNewSharedFSView() {
|
||||||
|
SharedFSVO sharedfs = mock(SharedFSVO.class);
|
||||||
|
Long id = 1L;
|
||||||
|
when(sharedfs.getId()).thenReturn(id);
|
||||||
|
SharedFSJoinVO sharedFSJoinVO = mock(SharedFSJoinVO.class);
|
||||||
|
|
||||||
|
SearchBuilder<SharedFSVO> sb = Mockito.mock(SearchBuilder.class);
|
||||||
|
ReflectionTestUtils.setField(sharedFSJoinDao, "fsSearch", sb);
|
||||||
|
SearchCriteria<SharedFSVO> sc = Mockito.mock(SearchCriteria.class);
|
||||||
|
Mockito.when(sb.create()).thenReturn(sc);
|
||||||
|
Mockito.doReturn(List.of(sharedFSJoinVO)).when(sharedFSJoinDao).searchIncludingRemoved(
|
||||||
|
Mockito.any(SearchCriteria.class), Mockito.eq(null), Mockito.eq(null),
|
||||||
|
Mockito.eq(false));
|
||||||
|
|
||||||
|
sharedFSJoinDao.newSharedFSView(sharedfs);
|
||||||
|
|
||||||
|
Mockito.verify(sc).setParameters("id", id);
|
||||||
|
Mockito.verify(sharedFSJoinDao, Mockito.times(1)).searchIncludingRemoved(
|
||||||
|
Mockito.any(SearchCriteria.class), Mockito.eq(null), Mockito.eq(null),
|
||||||
|
Mockito.eq(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void newSharedFSResponse() {
|
||||||
|
Long s_ownerId = 1L;
|
||||||
|
Long s_zoneId = 2L;
|
||||||
|
Long s_volumeId = 3L;
|
||||||
|
Long s_vmId = 4L;
|
||||||
|
Long s_networkId = 5L;
|
||||||
|
String s_fsFormat = "EXT4";
|
||||||
|
SharedFS.State state = SharedFS.State.Ready;
|
||||||
|
VirtualMachine.State vmState = VirtualMachine.State.Running;
|
||||||
|
Storage.ProvisioningType provisioningType = Storage.ProvisioningType.THIN;
|
||||||
|
|
||||||
|
SharedFSJoinVO sharedFSJoinVO = mock(SharedFSJoinVO.class);
|
||||||
|
when(sharedFSJoinVO.getAccountId()).thenReturn(s_ownerId);
|
||||||
|
when(sharedFSJoinVO.getZoneId()).thenReturn(s_zoneId);
|
||||||
|
when(sharedFSJoinVO.getVolumeId()).thenReturn(s_volumeId);
|
||||||
|
when(sharedFSJoinVO.getInstanceId()).thenReturn(s_vmId);
|
||||||
|
when(sharedFSJoinVO.getState()).thenReturn(state);
|
||||||
|
when(sharedFSJoinVO.getFsType()).thenReturn(SharedFS.FileSystemType.valueOf(s_fsFormat));
|
||||||
|
when(sharedFSJoinVO.getInstanceState()).thenReturn(vmState);
|
||||||
|
when(sharedFSJoinVO.getProvisioningType()).thenReturn(provisioningType);
|
||||||
|
|
||||||
|
NicVO nic = mock(NicVO.class);
|
||||||
|
NetworkVO network = mock(NetworkVO.class);
|
||||||
|
when(nic.getNetworkId()).thenReturn(s_networkId);
|
||||||
|
when(nicDao.listByVmId(s_vmId)).thenReturn(List.of(nic));
|
||||||
|
when(networkDao.findById(s_networkId)).thenReturn(network);
|
||||||
|
|
||||||
|
VmDiskStatisticsVO diskStats = mock(VmDiskStatisticsVO.class);
|
||||||
|
when(vmDiskStatsDao.findBy(s_ownerId, s_zoneId, s_vmId, s_volumeId)).thenReturn(diskStats);
|
||||||
|
|
||||||
|
VolumeStats vs = mock(VolumeStats.class);
|
||||||
|
String path = "volumepath";
|
||||||
|
when(sharedFSJoinVO.getVolumeFormat()).thenReturn(Storage.ImageFormat.QCOW2);
|
||||||
|
when(sharedFSJoinVO.getVolumePath()).thenReturn(path);
|
||||||
|
|
||||||
|
try (MockedStatic<ApiDBUtils> apiDBUtilsMocked = Mockito.mockStatic(ApiDBUtils.class)) {
|
||||||
|
when(ApiDBUtils.getVolumeStatistics(path)).thenReturn(vs);
|
||||||
|
SharedFSResponse response = sharedFSJoinDao.newSharedFSResponse(ResponseObject.ResponseView.Restricted, sharedFSJoinVO);
|
||||||
|
Assert.assertEquals(ReflectionTestUtils.getField(response, "state"), state.toString());
|
||||||
|
Assert.assertEquals(ReflectionTestUtils.getField(response, "virtualMachineState"), vmState.toString());
|
||||||
|
Assert.assertEquals(ReflectionTestUtils.getField(response, "provisioningType"), provisioningType.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -63,7 +63,7 @@ patch() {
|
|||||||
export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
|
export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
|
||||||
retry=60
|
retry=60
|
||||||
local patched=false
|
local patched=false
|
||||||
if [ "$TYPE" != "cksnode" ]; then
|
if [ "$TYPE" != "cksnode" ] && [ "$TYPE" != "sharedfsvm" ]; then
|
||||||
while [ $retry -gt 0 ]
|
while [ $retry -gt 0 ]
|
||||||
do
|
do
|
||||||
if tar tf $patchfile &> /dev/null; then
|
if tar tf $patchfile &> /dev/null; then
|
||||||
|
|||||||
@ -326,7 +326,10 @@ setup_common() {
|
|||||||
then
|
then
|
||||||
setup_interface "0" $ETH0_IP $ETH0_MASK $GW
|
setup_interface "0" $ETH0_IP $ETH0_MASK $GW
|
||||||
fi
|
fi
|
||||||
setup_interface "1" $ETH1_IP $ETH1_MASK $GW
|
if [ -n "$ETH1_IP" ]
|
||||||
|
then
|
||||||
|
setup_interface "1" $ETH1_IP $ETH1_MASK $GW
|
||||||
|
fi
|
||||||
if [ -n "$ETH2_IP" ]
|
if [ -n "$ETH2_IP" ]
|
||||||
then
|
then
|
||||||
setup_interface "2" $ETH2_IP $ETH2_MASK $GW
|
setup_interface "2" $ETH2_IP $ETH2_MASK $GW
|
||||||
|
|||||||
@ -151,7 +151,7 @@ config_guest() {
|
|||||||
|
|
||||||
setup_interface_sshd() {
|
setup_interface_sshd() {
|
||||||
|
|
||||||
if [ "$TYPE" != "cksnode" ]; then
|
if [ "$TYPE" != "cksnode" ] && [ "$TYPE" != "sharedfsvm" ]; then
|
||||||
log_it "Applying iptables rules"
|
log_it "Applying iptables rules"
|
||||||
if [ "$TYPE" != "dhcpsrvr" ]; then
|
if [ "$TYPE" != "dhcpsrvr" ]; then
|
||||||
cp /etc/iptables/iptables-$TYPE /etc/iptables/rules.v4
|
cp /etc/iptables/iptables-$TYPE /etc/iptables/rules.v4
|
||||||
@ -206,8 +206,12 @@ setup_interface_sshd() {
|
|||||||
else
|
else
|
||||||
setup_sshd $ETH1_IP "eth1"
|
setup_sshd $ETH1_IP "eth1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
elif [ "$TYPE" == "cksnode" ]; then
|
elif [ "$TYPE" == "cksnode" ]; then
|
||||||
setup_common eth0
|
setup_common eth0
|
||||||
|
|
||||||
|
elif [ "$TYPE" == "sharedfsvm" ]; then
|
||||||
|
setup_common eth0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
systemctl restart systemd-journald
|
systemctl restart systemd-journald
|
||||||
|
|||||||
@ -49,7 +49,7 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$TYPE" == "cksnode" ]; then
|
if [ "$TYPE" == "cksnode" ] || [ "$TYPE" == "sharedfsvm" ]; then
|
||||||
pkill -9 dhclient
|
pkill -9 dhclient
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
64
systemvm/debian/opt/cloud/bin/setup/sharedfsvm.sh
Normal file
64
systemvm/debian/opt/cloud/bin/setup/sharedfsvm.sh
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
. /opt/cloud/bin/setup/common.sh
|
||||||
|
|
||||||
|
setup_sharedfsvm() {
|
||||||
|
log_it "Setting up sharedfsvm"
|
||||||
|
|
||||||
|
update-alternatives --set iptables /usr/sbin/iptables-legacy
|
||||||
|
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
||||||
|
update-alternatives --set arptables /usr/sbin/arptables-legacy
|
||||||
|
update-alternatives --set ebtables /usr/sbin/ebtables-legacy
|
||||||
|
|
||||||
|
log_it "Setting up entry in hosts"
|
||||||
|
sed -i /$NAME/d /etc/hosts
|
||||||
|
echo "$ETH0_IP $NAME" >> /etc/hosts
|
||||||
|
|
||||||
|
# set default ssh port and restart sshd service
|
||||||
|
sed -i 's/3922/22/g' /etc/ssh/sshd_config
|
||||||
|
systemctl restart ssh
|
||||||
|
|
||||||
|
> /root/.ssh/authorized_keys
|
||||||
|
swapoff -a
|
||||||
|
sudo sed -i '/ swap / s/^/#/' /etc/fstab
|
||||||
|
log_it "Swap disabled"
|
||||||
|
|
||||||
|
echo "export PATH='$PATH:/opt/bin/'">> ~/.bashrc
|
||||||
|
|
||||||
|
disable_rpfilter
|
||||||
|
enable_fwding 0
|
||||||
|
enable_irqbalance 0
|
||||||
|
setup_ntp
|
||||||
|
dhclient -1
|
||||||
|
|
||||||
|
rm -f /etc/logrotate.d/cloud
|
||||||
|
|
||||||
|
log_it "Starting cloud-init services"
|
||||||
|
if [ -f /home/cloud/success ]; then
|
||||||
|
systemctl stop cloud-init cloud-config cloud-final
|
||||||
|
systemctl disable cloud-init cloud-config cloud-final
|
||||||
|
else
|
||||||
|
systemctl start --no-block cloud-init
|
||||||
|
systemctl start --no-block cloud-config
|
||||||
|
systemctl start --no-block cloud-final
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_sharedfsvm
|
||||||
|
. /opt/cloud/bin/setup/patch.sh && patch_sshd_config
|
||||||
277
test/integration/smoke/test_sharedfs_lifecycle.py
Normal file
277
test/integration/smoke/test_sharedfs_lifecycle.py
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
# 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 Shared FileSystem
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Import Local Modules
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
from marvin.cloudstackAPI import (createFirewallRule,
|
||||||
|
createPortForwardingRule)
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||||
|
from marvin.lib.utils import (cleanup_resources,
|
||||||
|
wait_until)
|
||||||
|
from marvin.lib.base import (Account,
|
||||||
|
VirtualMachine,
|
||||||
|
Network,
|
||||||
|
SharedFS,
|
||||||
|
ServiceOffering,
|
||||||
|
NetworkOffering,
|
||||||
|
DiskOffering,
|
||||||
|
PublicIPAddress,
|
||||||
|
)
|
||||||
|
from marvin.lib.common import (get_domain,
|
||||||
|
get_zone,
|
||||||
|
get_template)
|
||||||
|
from marvin.codes import FAILED
|
||||||
|
|
||||||
|
from marvin.lib.decoratorGenerators import skipTestIf
|
||||||
|
|
||||||
|
|
||||||
|
class TestSharedFSLifecycle(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
|
||||||
|
cls.testClient = super(TestSharedFSLifecycle, cls).getClsTestClient()
|
||||||
|
cls.apiclient = cls.testClient.getApiClient()
|
||||||
|
cls.services = cls.testClient.getParsedTestDataConfig()
|
||||||
|
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
||||||
|
cls._cleanup = []
|
||||||
|
|
||||||
|
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
||||||
|
cls.hypervisorNotSupported = False
|
||||||
|
if cls.hypervisor.lower() not in ["kvm", "vmware"]:
|
||||||
|
cls.hypervisorNotSupported = True
|
||||||
|
return
|
||||||
|
|
||||||
|
cls.services["service_offering"]["name"] = 'FSVM offering';
|
||||||
|
cls.services["service_offering"]["offerha"] = True;
|
||||||
|
cls.services["service_offering"]["cpunumber"] = 2;
|
||||||
|
cls.services["service_offering"]["cpuspeed"] = 500;
|
||||||
|
cls.services["service_offering"]["memory"] = 1024;
|
||||||
|
cls.service_offering = ServiceOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["service_offering"]
|
||||||
|
)
|
||||||
|
cls._cleanup.append(cls.service_offering)
|
||||||
|
|
||||||
|
cls.services["disk_offering"]["disksize"] = 1;
|
||||||
|
cls.disk_offering = DiskOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["disk_offering"],
|
||||||
|
custom=True
|
||||||
|
)
|
||||||
|
cls._cleanup.append(cls.disk_offering)
|
||||||
|
|
||||||
|
cls.useraccount = Account.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["account"],
|
||||||
|
domainid=cls.domain.id
|
||||||
|
)
|
||||||
|
cls._cleanup.append(cls.useraccount)
|
||||||
|
|
||||||
|
cls.adminaccount = Account.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["account"],
|
||||||
|
domainid=cls.domain.id,
|
||||||
|
admin=True
|
||||||
|
)
|
||||||
|
cls._cleanup.append(cls.adminaccount)
|
||||||
|
|
||||||
|
cls.network_offering_isolated = NetworkOffering.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["isolated_network_offering"]
|
||||||
|
)
|
||||||
|
cls.network_offering_isolated.update(cls.apiclient, state='Enabled')
|
||||||
|
cls._cleanup.append(cls.network_offering_isolated)
|
||||||
|
cls.services["network"]["name"] = "Test Network Isolated"
|
||||||
|
cls.user_network = Network.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["network"],
|
||||||
|
networkofferingid=cls.network_offering_isolated.id,
|
||||||
|
domainid=cls.domain.id,
|
||||||
|
accountid=cls.adminaccount.name,
|
||||||
|
zoneid=cls.zone.id
|
||||||
|
)
|
||||||
|
cls._cleanup.insert(0, cls.user_network)
|
||||||
|
cls.public_ipaddress = None
|
||||||
|
cls.sshpublicport = 1000
|
||||||
|
|
||||||
|
cls.template = get_template(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.zone.id,
|
||||||
|
cls.services["ostype"])
|
||||||
|
if cls.template == FAILED:
|
||||||
|
assert False, "get_template() failed to return template with description %s" % cls.services["ostype"]
|
||||||
|
|
||||||
|
cls.services["domainid"] = cls.domain.id
|
||||||
|
cls.services["zoneid"] = cls.zone.id
|
||||||
|
cls.services["diskofferingid"] = cls.disk_offering.id
|
||||||
|
cls.services["serviceofferingid"] = cls.service_offering.id
|
||||||
|
cls.services["networkid"] = cls.user_network.id
|
||||||
|
cls.services["account"] = cls.adminaccount.name
|
||||||
|
|
||||||
|
cls.sharedfs = SharedFS.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services,
|
||||||
|
name='Test Shared FileSystem 1',
|
||||||
|
size=2,
|
||||||
|
filesystem='XFS'
|
||||||
|
)
|
||||||
|
cls._cleanup.insert(0, cls.sharedfs)
|
||||||
|
|
||||||
|
cls.virtual_machine1 = VirtualMachine.create(
|
||||||
|
cls.apiclient,
|
||||||
|
cls.services["virtual_machine"],
|
||||||
|
templateid=cls.template.id,
|
||||||
|
serviceofferingid=cls.service_offering.id,
|
||||||
|
networkids=cls.user_network.id,
|
||||||
|
domainid=cls.domain.id,
|
||||||
|
accountid=cls.adminaccount.name,
|
||||||
|
zoneid=cls.zone.id
|
||||||
|
)
|
||||||
|
cls._cleanup.insert(0, cls.virtual_machine1)
|
||||||
|
|
||||||
|
cls.public_ipaddress = cls.setUpSNAT(cls, cls.user_network)
|
||||||
|
cls.debug("Public ipaddress: " + cls.public_ipaddress.ipaddress)
|
||||||
|
port = cls.setUpPortForwarding(cls, cls.virtual_machine1.id)
|
||||||
|
cls.vm1_ssh_client = cls.getSSHClient(cls, cls.virtual_machine1, port)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
try:
|
||||||
|
cleanup_resources(cls.apiclient, cls._cleanup)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.apiclient = self.testClient.getApiClient()
|
||||||
|
self.dbclient = self.testClient.getDbConnection()
|
||||||
|
self.cleanup = []
|
||||||
|
return
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
try:
|
||||||
|
self.debug("Cleaning up the resources")
|
||||||
|
cleanup_resources(self.apiclient, self.cleanup)
|
||||||
|
self.debug("Cleanup complete!")
|
||||||
|
except Exception as e:
|
||||||
|
self.debug("Warning! Exception in tearDown: %s" % e)
|
||||||
|
|
||||||
|
def setUpSNAT(self, network):
|
||||||
|
public_ipaddress = PublicIPAddress.list(
|
||||||
|
self.apiclient,
|
||||||
|
account=self.adminaccount.name,
|
||||||
|
domainid=self.domain.id,
|
||||||
|
associatednetworkid=network.id
|
||||||
|
)
|
||||||
|
createFwRule = createFirewallRule.createFirewallRuleCmd()
|
||||||
|
createFwRule.cidrlist = "0.0.0.0/0"
|
||||||
|
createFwRule.startport = 22
|
||||||
|
createFwRule.endport = 22
|
||||||
|
createFwRule.ipaddressid = public_ipaddress[0].id
|
||||||
|
createFwRule.protocol = "tcp"
|
||||||
|
self.apiclient.createFirewallRule(createFwRule)
|
||||||
|
return public_ipaddress[0]
|
||||||
|
|
||||||
|
def setUpPortForwarding(self, virtualmachineid):
|
||||||
|
createPfRule = createPortForwardingRule.createPortForwardingRuleCmd()
|
||||||
|
self.sshpublicport += 1
|
||||||
|
createPfRule.publicport = self.sshpublicport
|
||||||
|
createPfRule.privateport = 22
|
||||||
|
createPfRule.virtualmachineid = virtualmachineid
|
||||||
|
createPfRule.ipaddressid = self.public_ipaddress.id
|
||||||
|
createPfRule.protocol = "tcp"
|
||||||
|
self.apiclient.createPortForwardingRule(createPfRule)
|
||||||
|
self.debug("Successfully programmed PF rule for :%s"%self.public_ipaddress.ipaddress)
|
||||||
|
return createPfRule.publicport
|
||||||
|
|
||||||
|
def getSSHClient(self, virtual_machine, port):
|
||||||
|
try:
|
||||||
|
ssh_client = virtual_machine.get_ssh_client(ipaddress=self.public_ipaddress.ipaddress, port=port)
|
||||||
|
except Exception as e:
|
||||||
|
self.fail("SSH failed for virtual machine: %s - %s" % (virtual_machine.ipaddress, e))
|
||||||
|
return ssh_client
|
||||||
|
|
||||||
|
def mountSharedFSOnVM(self, ssh_client, sharedfs):
|
||||||
|
sharedfs_ip = sharedfs.nic[0].ipaddress
|
||||||
|
ssh_client.execute("mkdir /mnt/fs1")
|
||||||
|
cmd = "mount -t nfs -o nolock " + sharedfs_ip + ":/export /mnt/fs1"
|
||||||
|
ssh_client.execute(cmd)
|
||||||
|
|
||||||
|
@attr( tags=[ "advanced", "advancedns", "smokes"], required_hardware="true")
|
||||||
|
@skipTestIf("hypervisorNotSupported")
|
||||||
|
def test_mount_shared_fs(self):
|
||||||
|
"""Mount Shared FileSystem on two VMs and match contents
|
||||||
|
"""
|
||||||
|
self.mountSharedFSOnVM(self.vm1_ssh_client, self.sharedfs)
|
||||||
|
self.vm1_ssh_client.execute("df -Th /mnt/fs1")
|
||||||
|
self.vm1_ssh_client.execute("touch /mnt/fs1/test")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.virtual_machine2 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
templateid=self.template.id,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
networkids=self.user_network.id,
|
||||||
|
domainid=self.domain.id,
|
||||||
|
accountid=self.adminaccount.name,
|
||||||
|
zoneid=self.zone.id
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.vm1_ssh_client.execute("rm /mnt/fs1/test")
|
||||||
|
self.fail(e)
|
||||||
|
|
||||||
|
self.cleanup.append(self.virtual_machine2)
|
||||||
|
|
||||||
|
port = self.setUpPortForwarding(self.virtual_machine2.id)
|
||||||
|
ssh_client = self.getSSHClient(self.virtual_machine2, port)
|
||||||
|
self.assertIsNotNone(ssh_client)
|
||||||
|
|
||||||
|
self.mountSharedFSOnVM(ssh_client, self.sharedfs)
|
||||||
|
ssh_client.execute("df -Th /mnt/fs1")
|
||||||
|
result = ssh_client.execute("ls /mnt/fs1/test")
|
||||||
|
self.assertEqual(result[0], "/mnt/fs1/test")
|
||||||
|
|
||||||
|
@attr( tags=[ "advanced", "advancedns", "smokes"], required_hardware="true")
|
||||||
|
@skipTestIf("hypervisorNotSupported")
|
||||||
|
def test_resize_shared_fs(self):
|
||||||
|
"""Resize the shared filesystem by changing the disk offering and validate
|
||||||
|
"""
|
||||||
|
self.mountSharedFSOnVM(self.vm1_ssh_client, self.sharedfs)
|
||||||
|
result = self.vm1_ssh_client.execute("df -Th /mnt/fs1 | grep nfs")[0]
|
||||||
|
self.debug(result)
|
||||||
|
size = result.split()[-5]
|
||||||
|
self.debug("Size of the filesystem is " + size)
|
||||||
|
self.assertEqual(size, "2.0G", "SharedFS size should be 2.0G")
|
||||||
|
|
||||||
|
response = SharedFS.stop(self.sharedfs, self.apiclient)
|
||||||
|
response = SharedFS.changediskoffering(self.sharedfs, self.apiclient, self.disk_offering.id, 3)
|
||||||
|
self.debug(response)
|
||||||
|
response = SharedFS.start(self.sharedfs, self.apiclient)
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
result = self.vm1_ssh_client.execute("df -Th /mnt/fs1 | grep nfs")[0]
|
||||||
|
size = result.split()[-5]
|
||||||
|
self.debug("Size of the filesystem is " + size)
|
||||||
|
self.assertEqual(size, "3.0G", "SharedFS size should be 3.0G")
|
||||||
@ -276,6 +276,8 @@ known_categories = {
|
|||||||
'listBuckets': 'Object Store',
|
'listBuckets': 'Object Store',
|
||||||
'listVmsForImport': 'Virtual Machine',
|
'listVmsForImport': 'Virtual Machine',
|
||||||
'importVm': 'Virtual Machine',
|
'importVm': 'Virtual Machine',
|
||||||
|
'SharedFS': 'Shared FileSystem',
|
||||||
|
'SharedFileSystem': 'Shared FileSystem',
|
||||||
'Webhook': 'Webhook',
|
'Webhook': 'Webhook',
|
||||||
'Webhooks': 'Webhook',
|
'Webhooks': 'Webhook',
|
||||||
'purgeExpungedResources': 'Resource'
|
'purgeExpungedResources': 'Resource'
|
||||||
|
|||||||
@ -117,6 +117,7 @@ function configure_services() {
|
|||||||
systemctl disable vgauth
|
systemctl disable vgauth
|
||||||
systemctl disable sshd
|
systemctl disable sshd
|
||||||
systemctl disable nfs-common
|
systemctl disable nfs-common
|
||||||
|
systemctl disable nfs-server
|
||||||
systemctl disable portmap
|
systemctl disable portmap
|
||||||
|
|
||||||
# Disable guest services which will selectively be started based on hypervisor
|
# Disable guest services which will selectively be started based on hypervisor
|
||||||
|
|||||||
@ -60,7 +60,7 @@ function install_packages() {
|
|||||||
sysstat \
|
sysstat \
|
||||||
apache2 ssl-cert \
|
apache2 ssl-cert \
|
||||||
dnsmasq dnsmasq-utils \
|
dnsmasq dnsmasq-utils \
|
||||||
nfs-common \
|
nfs-common nfs-server xfsprogs \
|
||||||
samba-common cifs-utils \
|
samba-common cifs-utils \
|
||||||
xl2tpd bcrelay ppp tdb-tools \
|
xl2tpd bcrelay ppp tdb-tools \
|
||||||
xenstore-utils libxenstore4 \
|
xenstore-utils libxenstore4 \
|
||||||
|
|||||||
@ -7293,3 +7293,108 @@ class Webhook:
|
|||||||
cmd.webhookid = self.id
|
cmd.webhookid = self.id
|
||||||
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
return apiclient.deleteWebhookDelivery(cmd)
|
return apiclient.deleteWebhookDelivery(cmd)
|
||||||
|
|
||||||
|
class SharedFS:
|
||||||
|
|
||||||
|
def __init__(self, items):
|
||||||
|
self.__dict__.update(items)
|
||||||
|
|
||||||
|
"""Manage Shared FileSystem"""
|
||||||
|
@classmethod
|
||||||
|
def create(cls, apiclient, services, name, description=None, account=None, domainid=None, projectid=None,
|
||||||
|
size=None, zoneid=None, diskofferingid=None, serviceofferingid=None,
|
||||||
|
filesystem=None, provider=None, networkid=None):
|
||||||
|
"""Create Shared FileSystem"""
|
||||||
|
cmd = createSharedFileSystem.createSharedFileSystemCmd()
|
||||||
|
cmd.name = name
|
||||||
|
|
||||||
|
if description:
|
||||||
|
cmd.description = description
|
||||||
|
if diskofferingid:
|
||||||
|
cmd.diskofferingid = diskofferingid
|
||||||
|
elif "diskofferingid" in services:
|
||||||
|
cmd.diskofferingid = services["diskofferingid"]
|
||||||
|
|
||||||
|
if zoneid:
|
||||||
|
cmd.zoneid = zoneid
|
||||||
|
elif "zoneid" in services:
|
||||||
|
cmd.zoneid = services["zoneid"]
|
||||||
|
|
||||||
|
if account:
|
||||||
|
cmd.account = account
|
||||||
|
elif "account" in services:
|
||||||
|
cmd.account = services["account"]
|
||||||
|
|
||||||
|
if domainid:
|
||||||
|
cmd.domainid = domainid
|
||||||
|
elif "domainid" in services:
|
||||||
|
cmd.domainid = services["domainid"]
|
||||||
|
|
||||||
|
if projectid:
|
||||||
|
cmd.projectid = projectid
|
||||||
|
|
||||||
|
if size:
|
||||||
|
cmd.size = size
|
||||||
|
|
||||||
|
if networkid:
|
||||||
|
cmd.networkid = networkid
|
||||||
|
elif "networkid" in services:
|
||||||
|
cmd.networkid = services["networkid"]
|
||||||
|
|
||||||
|
if filesystem:
|
||||||
|
cmd.filesystem = filesystem
|
||||||
|
|
||||||
|
if provider:
|
||||||
|
cmd.provider = provider
|
||||||
|
|
||||||
|
if serviceofferingid:
|
||||||
|
cmd.serviceofferingid = serviceofferingid
|
||||||
|
elif "serviceofferingid" in services:
|
||||||
|
cmd.serviceofferingid = services["serviceofferingid"]
|
||||||
|
|
||||||
|
return SharedFS(apiclient.createSharedFileSystem(cmd).__dict__)
|
||||||
|
|
||||||
|
def delete(self, apiclient, expunge=True, forced=True):
|
||||||
|
"""Delete Shared FileSystem"""
|
||||||
|
cmd = destroySharedFileSystem.destroySharedFileSystemCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
cmd.expunge = expunge
|
||||||
|
cmd.forced = forced
|
||||||
|
apiclient.destroySharedFileSystem(cmd)
|
||||||
|
|
||||||
|
def stop(self, apiclient, forced=True):
|
||||||
|
"""Stop Shared FileSystem"""
|
||||||
|
cmd = stopSharedFileSystem.stopSharedFileSystemCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
cmd.forced = forced
|
||||||
|
apiclient.stopSharedFileSystem(cmd)
|
||||||
|
|
||||||
|
def start(self, apiclient):
|
||||||
|
"""Start Shared FileSystem"""
|
||||||
|
cmd = startSharedFileSystem.startSharedFileSystemCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
apiclient.startSharedFileSystem(cmd)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls, apiclient, **kwargs):
|
||||||
|
cmd = listSharedFileSystems.listSharedFileSystemCmd()
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
return (apiclient.listSharedFileSystems(cmd))
|
||||||
|
|
||||||
|
def update(self, apiclient, name=None, description=None):
|
||||||
|
"""Update Shared FileSystem"""
|
||||||
|
cmd = updateSharedFileSystem.updateSharedFileSystemCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
if name:
|
||||||
|
cmd.name = name
|
||||||
|
if description:
|
||||||
|
cmd.description = description
|
||||||
|
return (apiclient.updateSharedFileSystem(cmd))
|
||||||
|
|
||||||
|
def changediskoffering(self, apiclient, diskofferingid=None, size=None):
|
||||||
|
"""Change Disk Offering/Size of the Shared FileSystem"""
|
||||||
|
cmd = changeSharedFileSystemDiskOffering.changeSharedFileSystemDiskOfferingCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
cmd.diskofferingid = diskofferingid
|
||||||
|
cmd.size = size
|
||||||
|
return (apiclient.changeSharedFileSystemDiskOffering(cmd))
|
||||||
|
|||||||
@ -171,6 +171,8 @@
|
|||||||
"label.action.reboot.router": "Reboot router",
|
"label.action.reboot.router": "Reboot router",
|
||||||
"label.action.reboot.systemvm": "Reboot System VM",
|
"label.action.reboot.systemvm": "Reboot System VM",
|
||||||
"label.action.recover.volume": "Recover volume",
|
"label.action.recover.volume": "Recover volume",
|
||||||
|
"label.action.resize.sharedfs": "Resize Shared FileSystem",
|
||||||
|
"label.action.restart.sharedfs": "Restart Shared FileSystem",
|
||||||
"label.action.recurring.snapshot": "Recurring Snapshots",
|
"label.action.recurring.snapshot": "Recurring Snapshots",
|
||||||
"label.action.disable.2FA.user.auth": "Disable User Two Factor Authentication",
|
"label.action.disable.2FA.user.auth": "Disable User Two Factor Authentication",
|
||||||
"label.action.register.iso": "Register ISO",
|
"label.action.register.iso": "Register ISO",
|
||||||
@ -193,9 +195,11 @@
|
|||||||
"label.action.secure.host": "Provision host security keys",
|
"label.action.secure.host": "Provision host security keys",
|
||||||
"label.action.set.as.source.nat.ip": "make source NAT",
|
"label.action.set.as.source.nat.ip": "make source NAT",
|
||||||
"label.action.setup.2FA.user.auth": "Setup User Two Factor Authentication",
|
"label.action.setup.2FA.user.auth": "Setup User Two Factor Authentication",
|
||||||
|
"label.action.start.sharedfs": "Start Shared FileSystem",
|
||||||
"label.action.start.instance": "Start Instance",
|
"label.action.start.instance": "Start Instance",
|
||||||
"label.action.start.router": "Start router",
|
"label.action.start.router": "Start router",
|
||||||
"label.action.start.systemvm": "Start system VM",
|
"label.action.start.systemvm": "Start system VM",
|
||||||
|
"label.action.stop.sharedfs": "Stop Shared FileSystem",
|
||||||
"label.action.stop.instance": "Stop Instance",
|
"label.action.stop.instance": "Stop Instance",
|
||||||
"label.action.stop.router": "Stop router",
|
"label.action.stop.router": "Stop router",
|
||||||
"label.action.stop.systemvm": "Stop system VM",
|
"label.action.stop.systemvm": "Stop system VM",
|
||||||
@ -461,6 +465,7 @@
|
|||||||
"label.change.affinity": "Change affinity",
|
"label.change.affinity": "Change affinity",
|
||||||
"label.change.ip.address": "Change IP address",
|
"label.change.ip.address": "Change IP address",
|
||||||
"label.change.ipaddress": "Change IP address for NIC",
|
"label.change.ipaddress": "Change IP address for NIC",
|
||||||
|
"label.change.disk.offering": "Change disk offering",
|
||||||
"label.change.offering.for.volume": "Change disk offering for the volume",
|
"label.change.offering.for.volume": "Change disk offering for the volume",
|
||||||
"label.change.service.offering": "Change service offering",
|
"label.change.service.offering": "Change service offering",
|
||||||
"label.character": "Character",
|
"label.character": "Character",
|
||||||
@ -505,6 +510,7 @@
|
|||||||
"label.complete": "Complete",
|
"label.complete": "Complete",
|
||||||
"label.compute": "Compute",
|
"label.compute": "Compute",
|
||||||
"label.compute.offerings": "Compute offerings",
|
"label.compute.offerings": "Compute offerings",
|
||||||
|
"label.compute.offering.for.vm": "Compute offering for VM",
|
||||||
"label.computeonly.offering": "Compute only disk offering",
|
"label.computeonly.offering": "Compute only disk offering",
|
||||||
"label.computeonly.offering.tooltip": "Option to specify root disk related information in the compute offering or to directly link a disk offering to the compute offering",
|
"label.computeonly.offering.tooltip": "Option to specify root disk related information in the compute offering or to directly link a disk offering to the compute offering",
|
||||||
"label.conditions": "Conditions",
|
"label.conditions": "Conditions",
|
||||||
@ -578,6 +584,7 @@
|
|||||||
"label.create.instance": "Create cloud server",
|
"label.create.instance": "Create cloud server",
|
||||||
"label.create.account": "Create Account",
|
"label.create.account": "Create Account",
|
||||||
"label.create.backup": "Start backup",
|
"label.create.backup": "Start backup",
|
||||||
|
"label.create.sharedfs": "Create Shared FileSystem",
|
||||||
"label.create.network": "Create new Network",
|
"label.create.network": "Create new Network",
|
||||||
"label.create.nfs.secondary.staging.storage": "Create NFS secondary staging storage",
|
"label.create.nfs.secondary.staging.storage": "Create NFS secondary staging storage",
|
||||||
"label.create.project": "Create project",
|
"label.create.project": "Create project",
|
||||||
@ -663,6 +670,7 @@
|
|||||||
"label.delete.domain": "Delete domain",
|
"label.delete.domain": "Delete domain",
|
||||||
"label.delete.events": "Delete events",
|
"label.delete.events": "Delete events",
|
||||||
"label.delete.f5": "Delete F5",
|
"label.delete.f5": "Delete F5",
|
||||||
|
"label.destroy.sharedfs": "Destroy Shared FileSystem",
|
||||||
"label.delete.gateway": "Delete gateway",
|
"label.delete.gateway": "Delete gateway",
|
||||||
"label.delete.icon": "Delete icon",
|
"label.delete.icon": "Delete icon",
|
||||||
"label.delete.instance.group": "Delete Instance group",
|
"label.delete.instance.group": "Delete Instance group",
|
||||||
@ -916,6 +924,7 @@
|
|||||||
"label.existing": "Existing",
|
"label.existing": "Existing",
|
||||||
"label.execute": "Execute",
|
"label.execute": "Execute",
|
||||||
"label.expunge": "Expunge",
|
"label.expunge": "Expunge",
|
||||||
|
"label.expunge.sharedfs": "Expunge Shared FileSystem",
|
||||||
"label.expungevmgraceperiod": "Expunge Instance grace period (in sec)",
|
"label.expungevmgraceperiod": "Expunge Instance grace period (in sec)",
|
||||||
"label.expunged": "Expunged",
|
"label.expunged": "Expunged",
|
||||||
"label.expunging": "Expunging",
|
"label.expunging": "Expunging",
|
||||||
@ -936,6 +945,8 @@
|
|||||||
"label.filename": "File Name",
|
"label.filename": "File Name",
|
||||||
"label.fetched": "Fetched",
|
"label.fetched": "Fetched",
|
||||||
"label.files": "Alternate files to retrieve",
|
"label.files": "Alternate files to retrieve",
|
||||||
|
"label.sharedfs": "Shared FileSystem",
|
||||||
|
"label.filesystem": "Filesystem",
|
||||||
"label.filter": "Filter",
|
"label.filter": "Filter",
|
||||||
"label.filter.annotations.all": "All comments",
|
"label.filter.annotations.all": "All comments",
|
||||||
"label.filter.annotations.self": "Created by me",
|
"label.filter.annotations.self": "Created by me",
|
||||||
@ -967,6 +978,7 @@
|
|||||||
"label.friday": "Friday",
|
"label.friday": "Friday",
|
||||||
"label.from": "from",
|
"label.from": "from",
|
||||||
"label.from.lb": "from LB",
|
"label.from.lb": "from LB",
|
||||||
|
"label.fsprovidername": "Shared FileSystem Provider",
|
||||||
"label.full": "Full",
|
"label.full": "Full",
|
||||||
"label.full.path": "Full path",
|
"label.full.path": "Full path",
|
||||||
"label.fwdeviceid": "ID",
|
"label.fwdeviceid": "ID",
|
||||||
@ -1420,6 +1432,7 @@
|
|||||||
"label.monitor.url": "URL Path",
|
"label.monitor.url": "URL Path",
|
||||||
"label.monthly": "Monthly",
|
"label.monthly": "Monthly",
|
||||||
"label.more.access.dashboard.ui": "More about accessing dashboard UI",
|
"label.more.access.dashboard.ui": "More about accessing dashboard UI",
|
||||||
|
"label.mount.sharedfs": "Mount Shared FileSystem via NFS",
|
||||||
"label.move.down.row": "Move down one row",
|
"label.move.down.row": "Move down one row",
|
||||||
"label.move.to.bottom": "Move to bottom",
|
"label.move.to.bottom": "Move to bottom",
|
||||||
"label.move.to.top": "Move to top",
|
"label.move.to.top": "Move to top",
|
||||||
@ -1774,6 +1787,7 @@
|
|||||||
"label.reboot": "Reboot",
|
"label.reboot": "Reboot",
|
||||||
"label.recent.deliveries": "Recent deliveries",
|
"label.recent.deliveries": "Recent deliveries",
|
||||||
"label.receivedbytes": "Bytes received",
|
"label.receivedbytes": "Bytes received",
|
||||||
|
"label.recover.sharedfs": "Recover Shared FileSystem",
|
||||||
"label.recover.vm": "Recover Instance",
|
"label.recover.vm": "Recover Instance",
|
||||||
"label.recovering": "Recovering",
|
"label.recovering": "Recovering",
|
||||||
"label.redeliver": "Redeliver",
|
"label.redeliver": "Redeliver",
|
||||||
@ -2289,6 +2303,7 @@
|
|||||||
"label.updateinsequence": "Update in sequence",
|
"label.updateinsequence": "Update in sequence",
|
||||||
"label.update.autoscale.vmgroup": "Update AutoScaling Group",
|
"label.update.autoscale.vmgroup": "Update AutoScaling Group",
|
||||||
"label.update.condition": "Update condition",
|
"label.update.condition": "Update condition",
|
||||||
|
"label.update.sharedfs": "Update Shared FileSystem",
|
||||||
"label.update.instance.group": "Update Instance group",
|
"label.update.instance.group": "Update Instance group",
|
||||||
"label.update.ip.range": "Update IP range",
|
"label.update.ip.range": "Update IP range",
|
||||||
"label.update.network": "Update Network",
|
"label.update.network": "Update Network",
|
||||||
@ -2448,8 +2463,9 @@
|
|||||||
"label.volgroup": "Volume group",
|
"label.volgroup": "Volume group",
|
||||||
"label.volume": "Volume",
|
"label.volume": "Volume",
|
||||||
"label.volume.empty": "No data volumes attached to this Instance",
|
"label.volume.empty": "No data volumes attached to this Instance",
|
||||||
"label.volume.volumefileupload.description": "Click or drag file to this area to upload.",
|
|
||||||
"label.volume.encryption.support": "Volume Encryption Supported",
|
"label.volume.encryption.support": "Volume Encryption Supported",
|
||||||
|
"label.volume.metrics": "Volume Metrics",
|
||||||
|
"label.volume.volumefileupload.description": "Click or drag file to this area to upload.",
|
||||||
"label.volumechecksum": "MD5 checksum",
|
"label.volumechecksum": "MD5 checksum",
|
||||||
"label.volumechecksum.description": "Use the hash that you created at the start of the volume upload procedure.",
|
"label.volumechecksum.description": "Use the hash that you created at the start of the volume upload procedure.",
|
||||||
"label.volumefileupload": "Local file",
|
"label.volumefileupload": "Local file",
|
||||||
@ -2567,6 +2583,7 @@
|
|||||||
"message.action.delete.volume": "Please confirm that you want to delete this volume. Note: this will not delete any Snapshots of this volume.",
|
"message.action.delete.volume": "Please confirm that you want to delete this volume. Note: this will not delete any Snapshots of this volume.",
|
||||||
"message.action.delete.vpn.user": "Please confirm that you want to delete the VPN user.",
|
"message.action.delete.vpn.user": "Please confirm that you want to delete the VPN user.",
|
||||||
"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.sharedfs": "Please confirm that you want to destroy this Shared FileSystem.<br><b>Caution: This will delete all the data of the Shared FileSystem as well.</b>",
|
||||||
"message.action.destroy.instance": "Please confirm that you want to destroy the Instance.",
|
"message.action.destroy.instance": "Please confirm that you want to destroy the Instance.",
|
||||||
"message.action.destroy.instance.with.backups": "Please confirm that you want to destroy the Instance. There may be backups associated with the Instance which will not be deleted.",
|
"message.action.destroy.instance.with.backups": "Please confirm that you want to destroy the Instance. There may be backups associated with the Instance which will not be deleted.",
|
||||||
"message.action.destroy.systemvm": "Please confirm that you want to destroy the System VM.",
|
"message.action.destroy.systemvm": "Please confirm that you want to destroy the System VM.",
|
||||||
@ -2592,6 +2609,7 @@
|
|||||||
"message.action.enable.physical.network": "Please confirm that you want to enable this physical Network.",
|
"message.action.enable.physical.network": "Please confirm that you want to enable this physical Network.",
|
||||||
"message.action.enable.pod": "Please confirm that you want to enable this pod.",
|
"message.action.enable.pod": "Please confirm that you want to enable this pod.",
|
||||||
"message.action.enable.zone": "Please confirm that you want to enable this zone.",
|
"message.action.enable.zone": "Please confirm that you want to enable this zone.",
|
||||||
|
"message.action.expunge.sharedfs": "Please confirm that you want to expunge this Shared FileSystem.",
|
||||||
"message.action.expunge.instance": "Please confirm that you want to expunge this Instance.",
|
"message.action.expunge.instance": "Please confirm that you want to expunge this Instance.",
|
||||||
"message.action.expunge.instance.with.backups": "Please confirm that you want to expunge this Instance. There may be backups associated with the Instance which will not be deleted.",
|
"message.action.expunge.instance.with.backups": "Please confirm that you want to expunge this Instance. There may be backups associated with the Instance which will not be deleted.",
|
||||||
"message.action.host.enable.maintenance.mode": "Enabling maintenance mode will cause a live migration of all running Instances on this host to any available host.",
|
"message.action.host.enable.maintenance.mode": "Enabling maintenance mode will cause a live migration of all running Instances on this host to any available host.",
|
||||||
@ -2609,6 +2627,7 @@
|
|||||||
"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.sharedfs": "Please confirm that you would like to recover this Shared FileSystem.",
|
||||||
"message.action.recover.volume": "Please confirm that you would like to recover this volume.",
|
"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.",
|
||||||
@ -2616,6 +2635,7 @@
|
|||||||
"message.action.remove.routing.policy": "Please confirm that you want to remove Routing Policy from this Network",
|
"message.action.remove.routing.policy": "Please confirm that you want to remove Routing Policy from this Network",
|
||||||
"message.action.release.reserved.ip": "Please confirm that you want to release this reserved IP.",
|
"message.action.release.reserved.ip": "Please confirm that you want to release this reserved IP.",
|
||||||
"message.action.reserve.ip": "Please confirm that you want to reserve this IP.",
|
"message.action.reserve.ip": "Please confirm that you want to reserve this IP.",
|
||||||
|
"message.action.restart.sharedfs": "Please confirm that you want to restart this Shared FileSystem. This will cause a downtime to the user.<br>Restart with cleanup will re-initialize the Shared FileSystem VM without affecting the installed file system.",
|
||||||
"message.action.revert.snapshot": "Please confirm that you want to revert the owning volume to this Snapshot.",
|
"message.action.revert.snapshot": "Please confirm that you want to revert the owning volume to this Snapshot.",
|
||||||
"message.action.router.health.checks": "Health checks result will be fetched from router.",
|
"message.action.router.health.checks": "Health checks result will be fetched from router.",
|
||||||
"message.action.router.health.checks.disabled.warning": "Please enable router health checks.",
|
"message.action.router.health.checks.disabled.warning": "Please enable router health checks.",
|
||||||
@ -2624,9 +2644,11 @@
|
|||||||
"message.action.secondary.storage.read.write": "Please confirm that you want to make this secondary storage read write.",
|
"message.action.secondary.storage.read.write": "Please confirm that you want to make this secondary storage read write.",
|
||||||
"message.action.secure.host": "This will restart the host agent and libvirtd process after applying new X509 certificates, please confirm?",
|
"message.action.secure.host": "This will restart the host agent and libvirtd process after applying new X509 certificates, please confirm?",
|
||||||
"message.action.settings.warning.vm.running": "Please stop the Instance to access settings.",
|
"message.action.settings.warning.vm.running": "Please stop the Instance to access settings.",
|
||||||
|
"message.action.start.sharedfs": "Please confirm that you want to start this Shared FileSystem.",
|
||||||
"message.action.start.instance": "Please confirm that you want to start this Instance.",
|
"message.action.start.instance": "Please confirm that you want to start this Instance.",
|
||||||
"message.action.start.router": "Please confirm that you want to start this router.",
|
"message.action.start.router": "Please confirm that you want to start this router.",
|
||||||
"message.action.start.systemvm": "Please confirm that you want to start this system VM.",
|
"message.action.start.systemvm": "Please confirm that you want to start this system VM.",
|
||||||
|
"message.action.stop.sharedfs": "Please confirm that you want to stop this Shared FileSystem.",
|
||||||
"message.action.stop.instance": "Please confirm that you want to stop this Instance.",
|
"message.action.stop.instance": "Please confirm that you want to stop this Instance.",
|
||||||
"message.action.stop.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to stop this router.",
|
"message.action.stop.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to stop this router.",
|
||||||
"message.action.stop.systemvm": "Please confirm that you want to stop this system VM.",
|
"message.action.stop.systemvm": "Please confirm that you want to stop this system VM.",
|
||||||
@ -2719,13 +2741,17 @@
|
|||||||
"message.backup.restore": "Please confirm that you want to restore the Instance backup?",
|
"message.backup.restore": "Please confirm that you want to restore the Instance backup?",
|
||||||
"message.cancel.shutdown": "Please confirm that you would like to cancel the shutdown on this Management server. It will resume accepting any new Async Jobs.",
|
"message.cancel.shutdown": "Please confirm that you would like to cancel the shutdown on this Management server. It will resume accepting any new Async Jobs.",
|
||||||
"message.certificate.upload.processing": "Certificate upload in progress",
|
"message.certificate.upload.processing": "Certificate upload in progress",
|
||||||
|
"message.change.disk.offering.sharedfs.failed": "Failed to change disk offering for the Shared FileSystem.",
|
||||||
|
"message.change.disk.offering.sharedfs.processing": "Changing disk offering for the Shared FileSystem.",
|
||||||
"message.change.offering.confirm": "Please confirm that you wish to change the service offering of this virtual Instance.",
|
"message.change.offering.confirm": "Please confirm that you wish to change the service offering of this virtual Instance.",
|
||||||
"message.change.offering.for.volume": "Successfully changed offering for the volume",
|
"message.change.offering.for.volume": "Successfully changed offering for the volume",
|
||||||
"message.change.offering.for.volume.failed": "Change offering for the volume failed",
|
"message.change.offering.for.volume.failed": "Change offering for the volume failed",
|
||||||
"message.change.offering.for.volume.processing": "Changing offering for the volume...",
|
"message.change.offering.processing": "Changing offering for the volume...",
|
||||||
"message.change.password": "Please change your password.",
|
"message.change.password": "Please change your password.",
|
||||||
"message.change.scope.failed": "Scope change failed",
|
"message.change.scope.failed": "Scope change failed",
|
||||||
"message.change.scope.processing": "Scope change in progress",
|
"message.change.scope.processing": "Scope change in progress",
|
||||||
|
"message.change.service.offering.sharedfs.failed": "Failed to change service offering for the Shared FileSystem.",
|
||||||
|
"message.change.service.offering.sharedfs.processing": "Changing service offering for the Shared FileSystem.",
|
||||||
"message.cluster.dedicated": "Cluster Dedicated",
|
"message.cluster.dedicated": "Cluster Dedicated",
|
||||||
"message.cluster.dedication.released": "Cluster dedication released.",
|
"message.cluster.dedication.released": "Cluster dedication released.",
|
||||||
"message.config.health.monitor.failed": "Configure Health Monitor failed",
|
"message.config.health.monitor.failed": "Configure Health Monitor failed",
|
||||||
@ -2744,7 +2770,9 @@
|
|||||||
"message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts",
|
"message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts",
|
||||||
"message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events",
|
"message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events",
|
||||||
"message.confirm.attach.disk": "Are you sure you want to attach disk?",
|
"message.confirm.attach.disk": "Are you sure you want to attach disk?",
|
||||||
|
"message.confirm.change.disk.offering.for.sharedfs": "Please confirm that you want to change the disk offering for the Shared FileSystem. This might migrate the underlying volume to a different storage pool if required.",
|
||||||
"message.confirm.change.offering.for.volume": "Please confirm that you want to change disk offering for the volume",
|
"message.confirm.change.offering.for.volume": "Please confirm that you want to change disk offering for the volume",
|
||||||
|
"message.confirm.change.service.offering.for.sharedfs": "Please confirm that you want to change the service offering for the Shared FileSystem.",
|
||||||
"message.confirm.configure.ovs": "Are you sure you want to configure Ovs?",
|
"message.confirm.configure.ovs": "Are you sure you want to configure Ovs?",
|
||||||
"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?",
|
"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?",
|
||||||
"message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller.",
|
"message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller.",
|
||||||
@ -2795,6 +2823,8 @@
|
|||||||
"message.create.bucket.failed": "Failed to create bucket.",
|
"message.create.bucket.failed": "Failed to create bucket.",
|
||||||
"message.create.bucket.processing": "Bucket creation in progress",
|
"message.create.bucket.processing": "Bucket creation in progress",
|
||||||
"message.create.compute.offering": "Compute offering created",
|
"message.create.compute.offering": "Compute offering created",
|
||||||
|
"message.create.sharedfs.failed": "Failed to create Shared FileSystem.",
|
||||||
|
"message.create.sharedfs.processing": "Shared FileSystem creation in progress.",
|
||||||
"message.create.tungsten.public.network": "Create Tungsten-Fabric public Network",
|
"message.create.tungsten.public.network": "Create Tungsten-Fabric public Network",
|
||||||
"message.create.internallb": "Creating internal LB",
|
"message.create.internallb": "Creating internal LB",
|
||||||
"message.create.internallb.failed": "Failed to create internal LB.",
|
"message.create.internallb.failed": "Failed to create internal LB.",
|
||||||
@ -3124,6 +3154,7 @@
|
|||||||
"message.instances.unmanaged": "Instances not controlled by CloudStack.",
|
"message.instances.unmanaged": "Instances not controlled by CloudStack.",
|
||||||
"message.instances.migrate.vmware": "Instances that can be migrated from VMware.",
|
"message.instances.migrate.vmware": "Instances that can be migrated from VMware.",
|
||||||
"message.interloadbalance.not.return.elementid": "error: listInternalLoadBalancerElements API doesn't return internal LB element ID.",
|
"message.interloadbalance.not.return.elementid": "error: listInternalLoadBalancerElements API doesn't return internal LB element ID.",
|
||||||
|
"message.ip.address": "IP address : ",
|
||||||
"message.ip.address.changes.effect.after.vm.restart": "IP address changes takes effect only after Instance restart.",
|
"message.ip.address.changes.effect.after.vm.restart": "IP address changes takes effect only after Instance restart.",
|
||||||
"message.ip.v6.prefix.delete": "IPv6 prefix deleted",
|
"message.ip.v6.prefix.delete": "IPv6 prefix deleted",
|
||||||
"message.iso.desc": "Disc image containing data or bootable media for OS.",
|
"message.iso.desc": "Disc image containing data or bootable media for OS.",
|
||||||
@ -3203,6 +3234,7 @@
|
|||||||
"message.offering.internet.protocol.warning": "WARNING: IPv6 supported Networks use static routing and will require upstream routes to be configured manually.",
|
"message.offering.internet.protocol.warning": "WARNING: IPv6 supported Networks use static routing and will require upstream routes to be configured manually.",
|
||||||
"message.offering.ipv6.warning": "Please refer documentation for creating IPv6 enabled Network/VPC offering <a href='http://docs.cloudstack.apache.org/en/latest/plugins/ipv6.html#isolated-network-and-vpc-tier'>IPv6 support in CloudStack - Isolated Networks and VPC Network Tiers</a>",
|
"message.offering.ipv6.warning": "Please refer documentation for creating IPv6 enabled Network/VPC offering <a href='http://docs.cloudstack.apache.org/en/latest/plugins/ipv6.html#isolated-network-and-vpc-tier'>IPv6 support in CloudStack - Isolated Networks and VPC Network Tiers</a>",
|
||||||
"message.ovf.configurations": "OVF configurations available for the selected appliance. Please select the desired value. Incompatible compute offerings will get disabled.",
|
"message.ovf.configurations": "OVF configurations available for the selected appliance. Please select the desired value. Incompatible compute offerings will get disabled.",
|
||||||
|
"message.path": "Path : ",
|
||||||
"message.path.description": "NFS: exported path from the server. VMFS: /datacenter name/datastore name. SharedMountPoint: path where primary storage is mounted, such as /mnt/primary.",
|
"message.path.description": "NFS: exported path from the server. VMFS: /datacenter name/datastore name. SharedMountPoint: path where primary storage is mounted, such as /mnt/primary.",
|
||||||
"message.please.confirm.remove.ssh.key.pair": "Please confirm that you want to remove this SSH key pair.",
|
"message.please.confirm.remove.ssh.key.pair": "Please confirm that you want to remove this SSH key pair.",
|
||||||
"message.please.confirm.remove.user.data": "Please confirm that you want to remove this Userdata",
|
"message.please.confirm.remove.user.data": "Please confirm that you want to remove this Userdata",
|
||||||
@ -3287,6 +3319,7 @@
|
|||||||
"message.select.temporary.storage.instance.conversion": "(Optional) Select a Storage temporary destination for the converted disks through virt-v2v",
|
"message.select.temporary.storage.instance.conversion": "(Optional) Select a Storage temporary destination for the converted disks through virt-v2v",
|
||||||
"message.select.zone.description": "Select type of zone basic/advanced.",
|
"message.select.zone.description": "Select type of zone basic/advanced.",
|
||||||
"message.select.zone.hint": "This is the type of zone deployment that you want to use. Basic zone: provides a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). Advanced zone: For more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.",
|
"message.select.zone.hint": "This is the type of zone deployment that you want to use. Basic zone: provides a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). Advanced zone: For more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.",
|
||||||
|
"message.server": "Server : ",
|
||||||
"message.server.description": "NFS, iSCSI, or PreSetup: IP address or DNS name of the storage device. VMWare PreSetup: IP address or DNS name of the vCenter server. Linstor: http(s) url of the linstor-controller.",
|
"message.server.description": "NFS, iSCSI, or PreSetup: IP address or DNS name of the storage device. VMWare PreSetup: IP address or DNS name of the vCenter server. Linstor: http(s) url of the linstor-controller.",
|
||||||
"message.set.default.nic": "Please confirm that you would like to make this NIC the default for this Instance.",
|
"message.set.default.nic": "Please confirm that you would like to make this NIC the default for this Instance.",
|
||||||
"message.set.default.nic.manual": "Please manually update the default NIC on the Instance now.",
|
"message.set.default.nic.manual": "Please manually update the default NIC on the Instance now.",
|
||||||
@ -3353,6 +3386,7 @@
|
|||||||
"message.success.copy.clipboard": "Successfully copied to clipboard",
|
"message.success.copy.clipboard": "Successfully copied to clipboard",
|
||||||
"message.success.create.account": "Successfully created Account",
|
"message.success.create.account": "Successfully created Account",
|
||||||
"message.success.create.bucket": "Successfully created bucket",
|
"message.success.create.bucket": "Successfully created bucket",
|
||||||
|
"message.success.create.sharedfs": "Successfully created Shared FileSystem",
|
||||||
"message.success.create.internallb": "Successfully created Internal Load Balancer",
|
"message.success.create.internallb": "Successfully created Internal Load Balancer",
|
||||||
"message.success.create.isolated.network": "Successfully created isolated Network",
|
"message.success.create.isolated.network": "Successfully created isolated Network",
|
||||||
"message.success.create.keypair": "Successfully created SSH key pair",
|
"message.success.create.keypair": "Successfully created SSH key pair",
|
||||||
@ -3419,6 +3453,7 @@
|
|||||||
"message.success.unmanage.volume": "Successfully unmanaged Volume",
|
"message.success.unmanage.volume": "Successfully unmanaged Volume",
|
||||||
"message.success.update.bucket": "Successfully updated bucket",
|
"message.success.update.bucket": "Successfully updated bucket",
|
||||||
"message.success.update.condition": "Successfully updated condition",
|
"message.success.update.condition": "Successfully updated condition",
|
||||||
|
"message.success.update.sharedfs": "Successfully updated Shared FileSystem",
|
||||||
"message.success.update.ipaddress": "Successfully updated IP address",
|
"message.success.update.ipaddress": "Successfully updated IP address",
|
||||||
"message.success.update.iprange": "Successfully updated IP range",
|
"message.success.update.iprange": "Successfully updated IP range",
|
||||||
"message.success.update.kubeversion": "Successfully updated Kubernetes supported version",
|
"message.success.update.kubeversion": "Successfully updated Kubernetes supported version",
|
||||||
|
|||||||
@ -697,7 +697,7 @@ export default {
|
|||||||
'/project', '/account', 'buckets', 'objectstore',
|
'/project', '/account', 'buckets', 'objectstore',
|
||||||
'/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
|
'/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
|
||||||
'/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering',
|
'/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering',
|
||||||
'/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping', '/webhook', 'webhookdeliveries', '/quotatariff'].join('|'))
|
'/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping', '/webhook', 'webhookdeliveries', '/quotatariff', '/sharedfs'].join('|'))
|
||||||
.test(this.$route.path)
|
.test(this.$route.path)
|
||||||
},
|
},
|
||||||
enableGroupAction () {
|
enableGroupAction () {
|
||||||
@ -705,7 +705,7 @@ export default {
|
|||||||
'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
|
'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
|
||||||
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
|
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
|
||||||
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment', 'buckets',
|
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment', 'buckets',
|
||||||
'webhook', 'webhookdeliveries'
|
'webhook', 'webhookdeliveries', 'sharedfs'
|
||||||
].includes(this.$route.name)
|
].includes(this.$route.name)
|
||||||
},
|
},
|
||||||
getDateAtTimeZone (date, timezone) {
|
getDateAtTimeZone (date, timezone) {
|
||||||
|
|||||||
@ -303,7 +303,7 @@ export default {
|
|||||||
}
|
}
|
||||||
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
|
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
|
||||||
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
|
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
|
||||||
'type', 'scope', 'managementserverid', 'serviceofferingid', 'diskofferingid', 'usagetype', 'restartrequired'].includes(item)
|
'type', 'scope', 'managementserverid', 'serviceofferingid', 'diskofferingid', 'networkid', 'usagetype', 'restartrequired'].includes(item)
|
||||||
) {
|
) {
|
||||||
type = 'list'
|
type = 'list'
|
||||||
} else if (item === 'tags') {
|
} else if (item === 'tags') {
|
||||||
@ -437,6 +437,7 @@ export default {
|
|||||||
let managementServerIdIndex = -1
|
let managementServerIdIndex = -1
|
||||||
let serviceOfferingIndex = -1
|
let serviceOfferingIndex = -1
|
||||||
let diskOfferingIndex = -1
|
let diskOfferingIndex = -1
|
||||||
|
let networkIndex = -1
|
||||||
let usageTypeIndex = -1
|
let usageTypeIndex = -1
|
||||||
let volumeIndex = -1
|
let volumeIndex = -1
|
||||||
|
|
||||||
@ -524,6 +525,12 @@ export default {
|
|||||||
promises.push(await this.fetchDiskOfferings(searchKeyword))
|
promises.push(await this.fetchDiskOfferings(searchKeyword))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('networkid')) {
|
||||||
|
networkIndex = this.fields.findIndex(item => item.name === 'networkid')
|
||||||
|
this.fields[networkIndex].loading = true
|
||||||
|
promises.push(await this.fetchNetworks(searchKeyword))
|
||||||
|
}
|
||||||
|
|
||||||
if (arrayField.includes('usagetype')) {
|
if (arrayField.includes('usagetype')) {
|
||||||
usageTypeIndex = this.fields.findIndex(item => item.name === 'usagetype')
|
usageTypeIndex = this.fields.findIndex(item => item.name === 'usagetype')
|
||||||
this.fields[usageTypeIndex].loading = true
|
this.fields[usageTypeIndex].loading = true
|
||||||
@ -618,6 +625,14 @@ export default {
|
|||||||
this.fields[diskOfferingIndex].opts = this.sortArray(diskOfferings[0].data)
|
this.fields[diskOfferingIndex].opts = this.sortArray(diskOfferings[0].data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (networkIndex > -1) {
|
||||||
|
const networks = response.filter(item => item.type === 'networkid')
|
||||||
|
if (networks && networks.length > 0) {
|
||||||
|
this.fields[networkIndex].opts = this.sortArray(networks[0].data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (usageTypeIndex > -1) {
|
if (usageTypeIndex > -1) {
|
||||||
const usageTypes = response.filter(item => item.type === 'usagetype')
|
const usageTypes = response.filter(item => item.type === 'usagetype')
|
||||||
if (usageTypes?.length > 0) {
|
if (usageTypes?.length > 0) {
|
||||||
@ -661,6 +676,9 @@ export default {
|
|||||||
if (diskOfferingIndex > -1) {
|
if (diskOfferingIndex > -1) {
|
||||||
this.fields[diskOfferingIndex].loading = false
|
this.fields[diskOfferingIndex].loading = false
|
||||||
}
|
}
|
||||||
|
if (networkIndex > -1) {
|
||||||
|
this.fields[networkIndex].loading = false
|
||||||
|
}
|
||||||
if (usageTypeIndex > -1) {
|
if (usageTypeIndex > -1) {
|
||||||
this.fields[usageTypeIndex].loading = false
|
this.fields[usageTypeIndex].loading = false
|
||||||
}
|
}
|
||||||
@ -851,6 +869,19 @@ export default {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
fetchNetworks (searchKeyword) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listNetworks', { listAll: true, keyword: searchKeyword }).then(json => {
|
||||||
|
const networks = json.listnetworksresponse.network
|
||||||
|
resolve({
|
||||||
|
type: 'networkid',
|
||||||
|
data: networks
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error.response.headers['x-description'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
fetchAlertTypes () {
|
fetchAlertTypes () {
|
||||||
if (this.alertTypes.length > 0) {
|
if (this.alertTypes.length > 0) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
@ -108,6 +108,7 @@ export default {
|
|||||||
docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group',
|
docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
popup: true,
|
popup: true,
|
||||||
|
show: (record) => { return record.vmtype !== 'sharedfsvm' },
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/EditVM.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/EditVM.vue')))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -169,7 +170,7 @@ export default {
|
|||||||
message: 'message.reinstall.vm',
|
message: 'message.reinstall.vm',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
popup: true,
|
popup: true,
|
||||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) },
|
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' },
|
||||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
disabled: (record) => { return record.hostcontrolstate === 'Offline' },
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ReinstallVm.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ReinstallVm.vue')))
|
||||||
},
|
},
|
||||||
@ -181,9 +182,9 @@ export default {
|
|||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['virtualmachineid', 'name', 'description', 'snapshotmemory', 'quiescevm'],
|
args: ['virtualmachineid', 'name', 'description', 'snapshotmemory', 'quiescevm'],
|
||||||
show: (record) => {
|
show: (record) => {
|
||||||
return ((['Running'].includes(record.state) && record.hypervisor !== 'LXC') ||
|
return (((['Running'].includes(record.state) && record.hypervisor !== 'LXC') ||
|
||||||
(['Stopped'].includes(record.state) && ((record.hypervisor !== 'KVM' && record.hypervisor !== 'LXC') ||
|
(['Stopped'].includes(record.state) && ((record.hypervisor !== 'KVM' && record.hypervisor !== 'LXC') ||
|
||||||
(record.hypervisor === 'KVM' && record.pooltype === 'PowerFlex'))))
|
(record.hypervisor === 'KVM' && record.pooltype === 'PowerFlex')))) && record.vmtype !== 'sharedfsvm')
|
||||||
},
|
},
|
||||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' },
|
disabled: (record) => { return record.hostcontrolstate === 'Offline' && record.hypervisor === 'KVM' },
|
||||||
mapping: {
|
mapping: {
|
||||||
@ -214,7 +215,7 @@ export default {
|
|||||||
docHelp: 'adminguide/virtual_machines.html#backup-offerings',
|
docHelp: 'adminguide/virtual_machines.html#backup-offerings',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['virtualmachineid', 'backupofferingid'],
|
args: ['virtualmachineid', 'backupofferingid'],
|
||||||
show: (record) => { return !record.backupofferingid },
|
show: (record) => { return !record.backupofferingid && record.vmtype !== 'sharedfsvm' },
|
||||||
mapping: {
|
mapping: {
|
||||||
virtualmachineid: {
|
virtualmachineid: {
|
||||||
value: (record, params) => { return record.id }
|
value: (record, params) => { return record.id }
|
||||||
@ -229,7 +230,7 @@ export default {
|
|||||||
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
|
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['virtualmachineid'],
|
args: ['virtualmachineid'],
|
||||||
show: (record) => { return record.backupofferingid },
|
show: (record) => { return record.backupofferingid && record.vmtype !== 'sharedfsvm' },
|
||||||
mapping: {
|
mapping: {
|
||||||
virtualmachineid: {
|
virtualmachineid: {
|
||||||
value: (record, params) => { return record.id }
|
value: (record, params) => { return record.id }
|
||||||
@ -243,7 +244,7 @@ export default {
|
|||||||
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
|
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
popup: true,
|
popup: true,
|
||||||
show: (record) => { return record.backupofferingid },
|
show: (record) => { return record.backupofferingid && record.vmtype !== 'sharedfsvm' },
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/BackupScheduleWizard.vue'))),
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/BackupScheduleWizard.vue'))),
|
||||||
mapping: {
|
mapping: {
|
||||||
virtualmachineid: {
|
virtualmachineid: {
|
||||||
@ -262,7 +263,7 @@ export default {
|
|||||||
docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups',
|
docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['virtualmachineid', 'forced'],
|
args: ['virtualmachineid', 'forced'],
|
||||||
show: (record) => { return record.backupofferingid },
|
show: (record) => { return record.backupofferingid && record.vmtype !== 'sharedfsvm' },
|
||||||
mapping: {
|
mapping: {
|
||||||
virtualmachineid: {
|
virtualmachineid: {
|
||||||
value: (record, params) => { return record.id }
|
value: (record, params) => { return record.id }
|
||||||
@ -276,7 +277,7 @@ export default {
|
|||||||
docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm',
|
docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
popup: true,
|
popup: true,
|
||||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid },
|
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid && record.vmtype !== 'sharedfsvm' },
|
||||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
|
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AttachIso.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AttachIso.vue')))
|
||||||
},
|
},
|
||||||
@ -293,7 +294,7 @@ export default {
|
|||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
},
|
},
|
||||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid },
|
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid && record.vmtype !== 'sharedfsvm' },
|
||||||
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
|
disabled: (record) => { return record.hostcontrolstate === 'Offline' || record.hostcontrolstate === 'Maintenance' },
|
||||||
mapping: {
|
mapping: {
|
||||||
virtualmachineid: {
|
virtualmachineid: {
|
||||||
@ -308,7 +309,7 @@ export default {
|
|||||||
docHelp: 'adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm',
|
docHelp: 'adminguide/virtual_machines.html#change-affinity-group-for-an-existing-vm',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['affinitygroupids'],
|
args: ['affinitygroupids'],
|
||||||
show: (record) => { return ['Stopped'].includes(record.state) },
|
show: (record) => { return ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' },
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ChangeAffinity'))),
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ChangeAffinity'))),
|
||||||
popup: true
|
popup: true
|
||||||
},
|
},
|
||||||
@ -318,7 +319,7 @@ export default {
|
|||||||
label: 'label.scale.vm',
|
label: 'label.scale.vm',
|
||||||
docHelp: 'adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram',
|
docHelp: 'adminguide/virtual_machines.html#how-to-dynamically-scale-cpu-and-ram',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return ['Stopped'].includes(record.state) || (['Running'].includes(record.state) && record.hypervisor !== 'LXC') },
|
show: (record) => { return (['Stopped'].includes(record.state) || (['Running'].includes(record.state) && record.hypervisor !== 'LXC')) && record.vmtype !== 'sharedfsvm' },
|
||||||
disabled: (record) => { return record.state === 'Running' && !record.isdynamicallyscalable },
|
disabled: (record) => { return record.state === 'Running' && !record.isdynamicallyscalable },
|
||||||
popup: true,
|
popup: true,
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ScaleVM.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ScaleVM.vue')))
|
||||||
@ -369,7 +370,7 @@ export default {
|
|||||||
message: 'message.desc.reset.ssh.key.pair',
|
message: 'message.desc.reset.ssh.key.pair',
|
||||||
docHelp: 'adminguide/virtual_machines.html#resetting-ssh-keys',
|
docHelp: 'adminguide/virtual_machines.html#resetting-ssh-keys',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return ['Stopped'].includes(record.state) },
|
show: (record) => { return ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' },
|
||||||
popup: true,
|
popup: true,
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetSshKeyPair')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetSshKeyPair')))
|
||||||
},
|
},
|
||||||
@ -380,7 +381,7 @@ export default {
|
|||||||
message: 'message.desc.reset.userdata',
|
message: 'message.desc.reset.userdata',
|
||||||
docHelp: 'adminguide/virtual_machines.html#resetting-userdata',
|
docHelp: 'adminguide/virtual_machines.html#resetting-userdata',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return ['Stopped'].includes(record.state) },
|
show: (record) => { return ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' },
|
||||||
popup: true,
|
popup: true,
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetUserData')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/ResetUserData')))
|
||||||
},
|
},
|
||||||
@ -391,7 +392,7 @@ export default {
|
|||||||
dataView: true,
|
dataView: true,
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AssignInstance'))),
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/AssignInstance'))),
|
||||||
popup: true,
|
popup: true,
|
||||||
show: (record) => { return ['Stopped'].includes(record.state) }
|
show: (record) => { return ['Stopped'].includes(record.state) && record.vmtype !== 'sharedfsvm' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'recoverVirtualMachine',
|
api: 'recoverVirtualMachine',
|
||||||
@ -399,7 +400,7 @@ export default {
|
|||||||
label: 'label.recover.vm',
|
label: 'label.recover.vm',
|
||||||
message: 'message.recover.vm',
|
message: 'message.recover.vm',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record, store) => { return ['Destroyed'].includes(record.state) && store.features.allowuserexpungerecovervm }
|
show: (record, store) => { return ['Destroyed'].includes(record.state) && store.features.allowuserexpungerecovervm && record.vmtype !== 'sharedfsvm' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'unmanageVirtualMachine',
|
api: 'unmanageVirtualMachine',
|
||||||
@ -407,7 +408,7 @@ export default {
|
|||||||
label: 'label.action.unmanage.virtualmachine',
|
label: 'label.action.unmanage.virtualmachine',
|
||||||
message: 'message.action.unmanage.virtualmachine',
|
message: 'message.action.unmanage.virtualmachine',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) }
|
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && ['VMware', 'KVM'].includes(record.hypervisor) && record.vmtype !== 'sharedfsvm' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'expungeVirtualMachine',
|
api: 'expungeVirtualMachine',
|
||||||
@ -416,7 +417,7 @@ export default {
|
|||||||
message: (record) => { return record.backupofferingid ? 'message.action.expunge.instance.with.backups' : 'message.action.expunge.instance' },
|
message: (record) => { return record.backupofferingid ? 'message.action.expunge.instance.with.backups' : 'message.action.expunge.instance' },
|
||||||
docHelp: 'adminguide/virtual_machines.html#deleting-vms',
|
docHelp: 'adminguide/virtual_machines.html#deleting-vms',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record, store) => { return ['Destroyed', 'Expunging'].includes(record.state) && store.features.allowuserexpungerecovervm }
|
show: (record, store) => { return ['Destroyed', 'Expunging'].includes(record.state) && store.features.allowuserexpungerecovervm && record.vmtype !== 'sharedfsvm' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'destroyVirtualMachine',
|
api: 'destroyVirtualMachine',
|
||||||
@ -432,7 +433,7 @@ export default {
|
|||||||
},
|
},
|
||||||
popup: true,
|
popup: true,
|
||||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge } }) },
|
groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge } }) },
|
||||||
show: (record) => { return ['Running', 'Stopped', 'Error'].includes(record.state) },
|
show: (record) => { return ['Running', 'Stopped', 'Error'].includes(record.state) && record.vmtype !== 'sharedfsvm' },
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/DestroyVM.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/DestroyVM.vue')))
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -543,6 +543,149 @@ export default {
|
|||||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sharedfs',
|
||||||
|
title: 'label.sharedfs',
|
||||||
|
icon: 'file-text-outlined',
|
||||||
|
permission: ['listSharedFileSystems'],
|
||||||
|
resourceType: 'SharedFS',
|
||||||
|
columns: () => {
|
||||||
|
const fields = ['name', 'state', 'sizegb']
|
||||||
|
const metricsFields = ['diskkbsread', 'diskkbswrite', 'utilization', 'physicalsize']
|
||||||
|
|
||||||
|
if (store.getters.metrics) {
|
||||||
|
fields.push(...metricsFields)
|
||||||
|
}
|
||||||
|
if (store.getters.userInfo.roletype === 'Admin') {
|
||||||
|
fields.push('storage')
|
||||||
|
fields.push('account')
|
||||||
|
} else if (store.getters.userInfo.roletype === 'DomainAdmin') {
|
||||||
|
fields.push('account')
|
||||||
|
}
|
||||||
|
if (store.getters.listAllProjects) {
|
||||||
|
fields.push('project')
|
||||||
|
}
|
||||||
|
fields.push('zonename')
|
||||||
|
|
||||||
|
return fields
|
||||||
|
},
|
||||||
|
details: ['id', 'name', 'description', 'state', 'filesystem', 'diskofferingdisplaytext', 'ipaddress', 'sizegb', 'provider', 'protocol', 'provisioningtype', 'utilization', 'diskkbsread', 'diskkbswrite', 'diskioread', 'diskiowrite', 'account', 'domain', 'created'],
|
||||||
|
tabs: [{
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/storage/SharedFSTab.vue')))
|
||||||
|
}],
|
||||||
|
searchFilters: () => {
|
||||||
|
var filters = ['name', 'zoneid', 'domainid', 'account', 'networkid', 'serviceofferingid', 'diskofferingid']
|
||||||
|
return filters
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
api: 'createSharedFileSystem',
|
||||||
|
icon: 'plus-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#creating-a-new-file-share',
|
||||||
|
label: 'label.create.sharedfs',
|
||||||
|
listView: true,
|
||||||
|
popup: true,
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/storage/CreateSharedFS.vue')))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'updateSharedFileSystem',
|
||||||
|
icon: 'edit-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.update.sharedfs',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UpdateSharedFS.vue')))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'startSharedFileSystem',
|
||||||
|
icon: 'caret-right-outlined',
|
||||||
|
label: 'label.action.start.sharedfs',
|
||||||
|
message: 'message.action.start.sharedfs',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
groupAction: true,
|
||||||
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) },
|
||||||
|
show: (record) => { return ['Stopped'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'stopSharedFileSystem',
|
||||||
|
icon: 'poweroff-outlined',
|
||||||
|
label: 'label.action.stop.sharedfs',
|
||||||
|
message: 'message.action.stop.sharedfs',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
groupAction: true,
|
||||||
|
groupMap: (selection, values) => { return selection.map(x => { return { id: x, forced: values.forced } }) },
|
||||||
|
args: ['forced'],
|
||||||
|
show: (record) => { return ['Ready'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'restartSharedFileSystem',
|
||||||
|
icon: 'reload-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.action.restart.sharedfs',
|
||||||
|
message: 'message.action.restart.sharedfs',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
args: ['cleanup'],
|
||||||
|
show: (record) => { return ['Stopped', 'Ready', 'Detached'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'changeSharedFileSystemDiskOffering',
|
||||||
|
icon: 'swap-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.change.disk.offering',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/storage/ChangeSharedFSDiskOffering.vue'))),
|
||||||
|
show: (record) => { return ['Stopped', 'Ready'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'changeSharedFileSystemServiceOffering',
|
||||||
|
icon: 'arrows-alt-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.change.service.offering',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/storage/ChangeSharedFSServiceOffering.vue'))),
|
||||||
|
show: (record) => { return ['Stopped'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'destroySharedFileSystem',
|
||||||
|
icon: 'delete-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.destroy.sharedfs',
|
||||||
|
message: 'message.action.destroy.sharedfs',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
groupAction: true,
|
||||||
|
groupMap: (selection, values) => { return selection.map(x => { return { id: x, expunge: values.expunge, forced: values.forced } }) },
|
||||||
|
args: ['expunge', 'forced'],
|
||||||
|
show: (record) => { return !['Destroyed', 'Expunging', 'Error'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'recoverSharedFileSystem',
|
||||||
|
icon: 'medicine-box-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.recover.sharedfs',
|
||||||
|
message: 'message.action.recover.sharedfs',
|
||||||
|
dataView: true,
|
||||||
|
show: (record) => { return record.state === 'Destroyed' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'expungeSharedFileSystem',
|
||||||
|
icon: 'delete-outlined',
|
||||||
|
docHelp: 'adminguide/storage.html#lifecycle-operations',
|
||||||
|
label: 'label.expunge.sharedfs',
|
||||||
|
message: 'message.action.expunge.sharedfs',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
show: (record) => { return ['Destroyed', 'Expunging', 'Error'].includes(record.state) }
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,6 +82,7 @@ import {
|
|||||||
FieldTimeOutlined,
|
FieldTimeOutlined,
|
||||||
FileDoneOutlined,
|
FileDoneOutlined,
|
||||||
FileProtectOutlined,
|
FileProtectOutlined,
|
||||||
|
FileTextOutlined,
|
||||||
FilterOutlined,
|
FilterOutlined,
|
||||||
FilterTwoTone,
|
FilterTwoTone,
|
||||||
FireOutlined,
|
FireOutlined,
|
||||||
@ -248,6 +249,7 @@ export default {
|
|||||||
app.component('FieldTimeOutlined', FieldTimeOutlined)
|
app.component('FieldTimeOutlined', FieldTimeOutlined)
|
||||||
app.component('FileDoneOutlined', FileDoneOutlined)
|
app.component('FileDoneOutlined', FileDoneOutlined)
|
||||||
app.component('FileProtectOutlined', FileProtectOutlined)
|
app.component('FileProtectOutlined', FileProtectOutlined)
|
||||||
|
app.component('FileTextOutlined', FileTextOutlined)
|
||||||
app.component('FilterOutlined', FilterOutlined)
|
app.component('FilterOutlined', FilterOutlined)
|
||||||
app.component('FilterTwoTone', FilterTwoTone)
|
app.component('FilterTwoTone', FilterTwoTone)
|
||||||
app.component('FireOutlined', FireOutlined)
|
app.component('FireOutlined', FireOutlined)
|
||||||
|
|||||||
@ -51,7 +51,7 @@
|
|||||||
<template #suffixIcon><filter-outlined class="ant-select-suffix" /></template>
|
<template #suffixIcon><filter-outlined class="ant-select-suffix" /></template>
|
||||||
<a-select-option
|
<a-select-option
|
||||||
v-if="['Admin', 'DomainAdmin'].includes($store.getters.userInfo.roletype) &&
|
v-if="['Admin', 'DomainAdmin'].includes($store.getters.userInfo.roletype) &&
|
||||||
['vm', 'iso', 'template', 'pod', 'cluster', 'host', 'systemvm', 'router', 'storagepool', 'kubernetes', 'computeoffering', 'systemoffering', 'diskoffering'].includes($route.name) ||
|
['vm', 'iso', 'template', 'pod', 'cluster', 'host', 'systemvm', 'router', 'storagepool', 'kubernetes', 'computeoffering', 'systemoffering', 'diskoffering', 'sharedfs'].includes($route.name) ||
|
||||||
['account'].includes($route.name)"
|
['account'].includes($route.name)"
|
||||||
key="all"
|
key="all"
|
||||||
:label="$t('label.all')">
|
:label="$t('label.all')">
|
||||||
@ -67,7 +67,7 @@
|
|||||||
</a-select>
|
</a-select>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-switch
|
<a-switch
|
||||||
v-if="!dataView && ['vm', 'volume', 'zone', 'cluster', 'host', 'storagepool', 'managementserver'].includes($route.name)"
|
v-if="!dataView && ['vm', 'volume', 'zone', 'cluster', 'host', 'storagepool', 'managementserver', 'sharedfs'].includes($route.name)"
|
||||||
style="margin-left: 8px; min-height: 23px; margin-bottom: 4px"
|
style="margin-left: 8px; min-height: 23px; margin-bottom: 4px"
|
||||||
:checked-children="$t('label.metrics')"
|
:checked-children="$t('label.metrics')"
|
||||||
:un-checked-children="$t('label.metrics')"
|
:un-checked-children="$t('label.metrics')"
|
||||||
@ -800,7 +800,7 @@ export default {
|
|||||||
|
|
||||||
this.projectView = Boolean(store.getters.project && store.getters.project.id)
|
this.projectView = Boolean(store.getters.project && store.getters.project.id)
|
||||||
this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'userdata', 'volume', 'snapshot', 'buckets', 'vmsnapshot', 'guestnetwork',
|
this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'userdata', 'volume', 'snapshot', 'buckets', 'vmsnapshot', 'guestnetwork',
|
||||||
'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes',
|
'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes', 'sharedfs',
|
||||||
'autoscalevmgroup', 'vnfapp', 'webhook'].includes(this.$route.name)
|
'autoscalevmgroup', 'vnfapp', 'webhook'].includes(this.$route.name)
|
||||||
|
|
||||||
if ((this.$route && this.$route.params && this.$route.params.id) || this.$route.query.dataView) {
|
if ((this.$route && this.$route.params && this.$route.params.id) || this.$route.query.dataView) {
|
||||||
|
|||||||
@ -45,60 +45,7 @@
|
|||||||
<volumes-tab :resource="vm" :loading="loading" />
|
<volumes-tab :resource="vm" :loading="loading" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane :tab="$t('label.nics')" key="nics" v-if="'listNics' in $store.getters.apis">
|
<a-tab-pane :tab="$t('label.nics')" key="nics" v-if="'listNics' in $store.getters.apis">
|
||||||
<a-button
|
<NicsTab :resource="vm"/>
|
||||||
type="primary"
|
|
||||||
style="width: 100%; margin-bottom: 10px"
|
|
||||||
@click="showAddNicModal"
|
|
||||||
:loading="loadingNic"
|
|
||||||
:disabled="!('addNicToVirtualMachine' in $store.getters.apis)">
|
|
||||||
<template #icon><plus-outlined /></template> {{ $t('label.network.addvm') }}
|
|
||||||
</a-button>
|
|
||||||
<NicsTable :resource="vm" :loading="loading">
|
|
||||||
<template #actions="record">
|
|
||||||
<a-popconfirm
|
|
||||||
:title="$t('label.set.default.nic')"
|
|
||||||
@confirm="setAsDefault(record.nic)"
|
|
||||||
:okText="$t('label.yes')"
|
|
||||||
:cancelText="$t('label.no')"
|
|
||||||
v-if="!record.nic.isdefault"
|
|
||||||
>
|
|
||||||
<tooltip-button
|
|
||||||
tooltipPlacement="bottom"
|
|
||||||
:tooltip="$t('label.set.default.nic')"
|
|
||||||
:disabled="!('updateDefaultNicForVirtualMachine' in $store.getters.apis)"
|
|
||||||
icon="check-square-outlined" />
|
|
||||||
</a-popconfirm>
|
|
||||||
<tooltip-button
|
|
||||||
v-if="record.nic.type !== 'L2'"
|
|
||||||
tooltipPlacement="bottom"
|
|
||||||
:tooltip="$t('label.change.ip.address')"
|
|
||||||
icon="swap-outlined"
|
|
||||||
:disabled="!('updateVmNicIp' in $store.getters.apis)"
|
|
||||||
@onClick="onChangeIPAddress(record)" />
|
|
||||||
<tooltip-button
|
|
||||||
v-if="record.nic.type !== 'L2'"
|
|
||||||
tooltipPlacement="bottom"
|
|
||||||
:tooltip="$t('label.edit.secondary.ips')"
|
|
||||||
icon="environment-outlined"
|
|
||||||
:disabled="(!('addIpToNic' in $store.getters.apis) && !('addIpToNic' in $store.getters.apis))"
|
|
||||||
@onClick="onAcquireSecondaryIPAddress(record)" />
|
|
||||||
<a-popconfirm
|
|
||||||
:title="$t('message.network.removenic')"
|
|
||||||
@confirm="removeNIC(record.nic)"
|
|
||||||
:okText="$t('label.yes')"
|
|
||||||
:cancelText="$t('label.no')"
|
|
||||||
v-if="!record.nic.isdefault"
|
|
||||||
>
|
|
||||||
<tooltip-button
|
|
||||||
tooltipPlacement="bottom"
|
|
||||||
:tooltip="$t('label.action.remove.nic')"
|
|
||||||
:disabled="!('removeNicFromVirtualMachine' in $store.getters.apis)"
|
|
||||||
type="primary"
|
|
||||||
:danger="true"
|
|
||||||
icon="delete-outlined" />
|
|
||||||
</a-popconfirm>
|
|
||||||
</template>
|
|
||||||
</NicsTable>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane :tab="$t('label.vm.snapshots')" key="vmsnapshots" v-if="'listVMSnapshot' in $store.getters.apis">
|
<a-tab-pane :tab="$t('label.vm.snapshots')" key="vmsnapshots" v-if="'listVMSnapshot' in $store.getters.apis">
|
||||||
<ListResourceTable
|
<ListResourceTable
|
||||||
@ -175,153 +122,6 @@
|
|||||||
<CreateVolume :resource="resource" @close-action="closeModals" />
|
<CreateVolume :resource="resource" @close-action="closeModals" />
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<a-modal
|
|
||||||
:visible="showAddNetworkModal"
|
|
||||||
:title="$t('label.network.addvm')"
|
|
||||||
:maskClosable="false"
|
|
||||||
:closable="true"
|
|
||||||
:footer="null"
|
|
||||||
@cancel="closeModals">
|
|
||||||
{{ $t('message.network.addvm.desc') }}
|
|
||||||
<a-form @finish="submitAddNetwork" v-ctrl-enter="submitAddNetwork">
|
|
||||||
<div class="modal-form">
|
|
||||||
<p class="modal-form__label">{{ $t('label.network') }}:</p>
|
|
||||||
<a-select
|
|
||||||
:value="addNetworkData.network"
|
|
||||||
@change="e => addNetworkData.network = e"
|
|
||||||
v-focus="true"
|
|
||||||
showSearch
|
|
||||||
optionFilterProp="label"
|
|
||||||
:filterOption="(input, option) => {
|
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
|
||||||
}" >
|
|
||||||
<a-select-option
|
|
||||||
v-for="network in addNetworkData.allNetworks"
|
|
||||||
:key="network.id"
|
|
||||||
:value="network.id"
|
|
||||||
:label="network.name">
|
|
||||||
<span>
|
|
||||||
<resource-icon v-if="network.icon" :image="network.icon.base64image" size="1x" style="margin-right: 5px"/>
|
|
||||||
<apartment-outlined v-else style="margin-right: 5px" />
|
|
||||||
{{ network.name }}
|
|
||||||
</span>
|
|
||||||
</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
<p class="modal-form__label">{{ $t('label.publicip') }}:</p>
|
|
||||||
<a-input v-model:value="addNetworkData.ip"></a-input>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :span="24" class="action-button">
|
|
||||||
<a-button @click="closeModals">{{ $t('label.cancel') }}</a-button>
|
|
||||||
<a-button type="primary" ref="submit" @click="submitAddNetwork">{{ $t('label.ok') }}</a-button>
|
|
||||||
</div>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
|
|
||||||
<a-modal
|
|
||||||
:visible="showUpdateIpModal"
|
|
||||||
:title="$t('label.change.ipaddress')"
|
|
||||||
:maskClosable="false"
|
|
||||||
:closable="true"
|
|
||||||
:footer="null"
|
|
||||||
@cancel="closeModals"
|
|
||||||
>
|
|
||||||
{{ $t('message.network.updateip') }}
|
|
||||||
|
|
||||||
<a-form @finish="submitUpdateIP" v-ctrl-enter="submitUpdateIP">
|
|
||||||
<div class="modal-form">
|
|
||||||
<p class="modal-form__label">{{ $t('label.publicip') }}:</p>
|
|
||||||
<a-select
|
|
||||||
v-if="editNicResource.type==='Shared'"
|
|
||||||
v-model:value="editIpAddressValue"
|
|
||||||
:loading="listIps.loading"
|
|
||||||
v-focus="editNicResource.type==='Shared'"
|
|
||||||
showSearch
|
|
||||||
optionFilterProp="value"
|
|
||||||
:filterOption="(input, option) => {
|
|
||||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
|
||||||
}">
|
|
||||||
<a-select-option v-for="ip in listIps.opts" :key="ip.ipaddress">
|
|
||||||
{{ ip.ipaddress }}
|
|
||||||
</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
<a-input
|
|
||||||
v-else
|
|
||||||
v-model:value="editIpAddressValue"
|
|
||||||
v-focus="editNicResource.type!=='Shared'"></a-input>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :span="24" class="action-button">
|
|
||||||
<a-button @click="closeModals">{{ $t('label.cancel') }}</a-button>
|
|
||||||
<a-button type="primary" ref="submit" @click="submitUpdateIP">{{ $t('label.ok') }}</a-button>
|
|
||||||
</div>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
|
|
||||||
<a-modal
|
|
||||||
:visible="showSecondaryIpModal"
|
|
||||||
:title="$t('label.acquire.new.secondary.ip')"
|
|
||||||
:maskClosable="false"
|
|
||||||
:footer="null"
|
|
||||||
:closable="false"
|
|
||||||
class="wide-modal"
|
|
||||||
@cancel="closeModals"
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
{{ $t('message.network.secondaryip') }}
|
|
||||||
</p>
|
|
||||||
<a-divider />
|
|
||||||
<div v-ctrl-enter="submitSecondaryIP">
|
|
||||||
<div class="modal-form">
|
|
||||||
<p class="modal-form__label">{{ $t('label.publicip') }}:</p>
|
|
||||||
<a-select
|
|
||||||
v-if="editNicResource.type==='Shared'"
|
|
||||||
v-model:value="newSecondaryIp"
|
|
||||||
:loading="listIps.loading"
|
|
||||||
v-focus="editNicResource.type==='Shared'"
|
|
||||||
showSearch
|
|
||||||
optionFilterProp="value"
|
|
||||||
:filterOption="(input, option) => {
|
|
||||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
|
||||||
}">
|
|
||||||
<a-select-option v-for="ip in listIps.opts" :key="ip.ipaddress">
|
|
||||||
{{ ip.ipaddress }}
|
|
||||||
</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
<a-input
|
|
||||||
v-else
|
|
||||||
:placeholder="$t('label.new.secondaryip.description')"
|
|
||||||
v-model:value="newSecondaryIp"
|
|
||||||
v-focus="editNicResource.type!=='Shared'"></a-input>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 10px; display: flex; justify-content:flex-end;">
|
|
||||||
<a-button @click="submitSecondaryIP" ref="submit" type="primary" style="margin-right: 10px;">{{ $t('label.add.secondary.ip') }}</a-button>
|
|
||||||
<a-button @click="closeModals">{{ $t('label.close') }}</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-divider />
|
|
||||||
<a-list itemLayout="vertical">
|
|
||||||
<a-list-item v-for="(ip, index) in secondaryIPs" :key="index">
|
|
||||||
<a-popconfirm
|
|
||||||
:title="`${$t('label.action.release.ip')}?`"
|
|
||||||
@confirm="removeSecondaryIP(ip.id)"
|
|
||||||
:okText="$t('label.yes')"
|
|
||||||
:cancelText="$t('label.no')"
|
|
||||||
>
|
|
||||||
<tooltip-button
|
|
||||||
tooltipPlacement="top"
|
|
||||||
:tooltip="$t('label.action.release.ip')"
|
|
||||||
type="primary"
|
|
||||||
:danger="true"
|
|
||||||
icon="delete-outlined" />
|
|
||||||
{{ ip.ipaddress }}
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-list-item>
|
|
||||||
</a-list>
|
|
||||||
</a-modal>
|
|
||||||
|
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -335,7 +135,7 @@ import StatsTab from '@/components/view/StatsTab'
|
|||||||
import EventsTab from '@/components/view/EventsTab'
|
import EventsTab from '@/components/view/EventsTab'
|
||||||
import DetailSettings from '@/components/view/DetailSettings'
|
import DetailSettings from '@/components/view/DetailSettings'
|
||||||
import CreateVolume from '@/views/storage/CreateVolume'
|
import CreateVolume from '@/views/storage/CreateVolume'
|
||||||
import NicsTable from '@/views/network/NicsTable'
|
import NicsTab from '@/views/network/NicsTab'
|
||||||
import InstanceSchedules from '@/views/compute/InstanceSchedules.vue'
|
import InstanceSchedules from '@/views/compute/InstanceSchedules.vue'
|
||||||
import ListResourceTable from '@/components/view/ListResourceTable'
|
import ListResourceTable from '@/components/view/ListResourceTable'
|
||||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||||
@ -353,7 +153,7 @@ export default {
|
|||||||
EventsTab,
|
EventsTab,
|
||||||
DetailSettings,
|
DetailSettings,
|
||||||
CreateVolume,
|
CreateVolume,
|
||||||
NicsTable,
|
NicsTab,
|
||||||
InstanceSchedules,
|
InstanceSchedules,
|
||||||
ListResourceTable,
|
ListResourceTable,
|
||||||
SecurityGroupSelection,
|
SecurityGroupSelection,
|
||||||
@ -380,29 +180,7 @@ export default {
|
|||||||
totalStorage: 0,
|
totalStorage: 0,
|
||||||
currentTab: 'details',
|
currentTab: 'details',
|
||||||
showAddVolumeModal: false,
|
showAddVolumeModal: false,
|
||||||
showAddNetworkModal: false,
|
|
||||||
showUpdateIpModal: false,
|
|
||||||
showSecondaryIpModal: false,
|
|
||||||
showUpdateSecurityGroupsModal: false,
|
|
||||||
diskOfferings: [],
|
diskOfferings: [],
|
||||||
addNetworkData: {
|
|
||||||
allNetworks: [],
|
|
||||||
network: '',
|
|
||||||
ip: ''
|
|
||||||
},
|
|
||||||
loadingNic: false,
|
|
||||||
loadingSG: false,
|
|
||||||
editIpAddressNic: '',
|
|
||||||
editIpAddressValue: '',
|
|
||||||
editNetworkId: '',
|
|
||||||
secondaryIPs: [],
|
|
||||||
selectedNicId: '',
|
|
||||||
newSecondaryIp: '',
|
|
||||||
editNicResource: {},
|
|
||||||
listIps: {
|
|
||||||
loading: false,
|
|
||||||
opts: []
|
|
||||||
},
|
|
||||||
annotations: [],
|
annotations: [],
|
||||||
dataResource: {},
|
dataResource: {},
|
||||||
dataPreFill: {},
|
dataPreFill: {},
|
||||||
@ -473,62 +251,10 @@ export default {
|
|||||||
this.diskOfferings = response.listdiskofferingsresponse.diskoffering
|
this.diskOfferings = response.listdiskofferingsresponse.diskoffering
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
listNetworks () {
|
|
||||||
api('listNetworks', {
|
|
||||||
listAll: 'true',
|
|
||||||
showicon: true,
|
|
||||||
zoneid: this.vm.zoneid
|
|
||||||
}).then(response => {
|
|
||||||
this.addNetworkData.allNetworks = response.listnetworksresponse.network.filter(network => !this.vm.nic.map(nic => nic.networkid).includes(network.id))
|
|
||||||
this.addNetworkData.network = this.addNetworkData.allNetworks[0].id
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchSecondaryIPs (nicId) {
|
|
||||||
this.showSecondaryIpModal = true
|
|
||||||
this.selectedNicId = nicId
|
|
||||||
api('listNics', {
|
|
||||||
nicId: nicId,
|
|
||||||
keyword: '',
|
|
||||||
virtualmachineid: this.vm.id
|
|
||||||
}).then(response => {
|
|
||||||
this.secondaryIPs = response.listnicsresponse.nic[0].secondaryip
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchPublicIps (networkid) {
|
|
||||||
this.listIps.loading = true
|
|
||||||
this.listIps.opts = []
|
|
||||||
api('listPublicIpAddresses', {
|
|
||||||
networkid: networkid,
|
|
||||||
allocatedonly: false,
|
|
||||||
forvirtualnetwork: false
|
|
||||||
}).then(json => {
|
|
||||||
const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || []
|
|
||||||
listPublicIps.forEach(item => {
|
|
||||||
if (item.state === 'Free') {
|
|
||||||
this.listIps.opts.push({
|
|
||||||
ipaddress: item.ipaddress
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.listIps.opts.sort(function (a, b) {
|
|
||||||
const currentIp = a.ipaddress.replaceAll('.', '')
|
|
||||||
const nextIp = b.ipaddress.replaceAll('.', '')
|
|
||||||
if (parseInt(currentIp) < parseInt(nextIp)) { return -1 }
|
|
||||||
if (parseInt(currentIp) > parseInt(nextIp)) { return 1 }
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
}).finally(() => {
|
|
||||||
this.listIps.loading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
showAddVolModal () {
|
showAddVolModal () {
|
||||||
this.showAddVolumeModal = true
|
this.showAddVolumeModal = true
|
||||||
this.listDiskOfferings()
|
this.listDiskOfferings()
|
||||||
},
|
},
|
||||||
showAddNicModal () {
|
|
||||||
this.showAddNetworkModal = true
|
|
||||||
this.listNetworks()
|
|
||||||
},
|
|
||||||
showUpdateSGModal () {
|
showUpdateSGModal () {
|
||||||
this.loadingSG = true
|
this.loadingSG = true
|
||||||
if (this.vm.securitygroup && this.vm.securitygroup?.length > 0) {
|
if (this.vm.securitygroup && this.vm.securitygroup?.length > 0) {
|
||||||
@ -543,234 +269,7 @@ export default {
|
|||||||
},
|
},
|
||||||
closeModals () {
|
closeModals () {
|
||||||
this.showAddVolumeModal = false
|
this.showAddVolumeModal = false
|
||||||
this.showAddNetworkModal = false
|
|
||||||
this.showUpdateIpModal = false
|
|
||||||
this.showSecondaryIpModal = false
|
|
||||||
this.showUpdateSecurityGroupsModal = false
|
this.showUpdateSecurityGroupsModal = false
|
||||||
this.addNetworkData.network = ''
|
|
||||||
this.addNetworkData.ip = ''
|
|
||||||
this.editIpAddressValue = ''
|
|
||||||
this.newSecondaryIp = ''
|
|
||||||
},
|
|
||||||
onChangeIPAddress (record) {
|
|
||||||
this.editNicResource = record.nic
|
|
||||||
this.editIpAddressNic = record.nic.id
|
|
||||||
this.showUpdateIpModal = true
|
|
||||||
if (record.nic.type === 'Shared') {
|
|
||||||
this.fetchPublicIps(record.nic.networkid)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onAcquireSecondaryIPAddress (record) {
|
|
||||||
if (record.nic.type === 'Shared') {
|
|
||||||
this.fetchPublicIps(record.nic.networkid)
|
|
||||||
} else {
|
|
||||||
this.listIps.opts = []
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editNicResource = record.nic
|
|
||||||
this.editNetworkId = record.nic.networkid
|
|
||||||
this.fetchSecondaryIPs(record.nic.id)
|
|
||||||
},
|
|
||||||
submitAddNetwork () {
|
|
||||||
if (this.loadingNic) return
|
|
||||||
const params = {}
|
|
||||||
params.virtualmachineid = this.vm.id
|
|
||||||
params.networkid = this.addNetworkData.network
|
|
||||||
if (this.addNetworkData.ip) {
|
|
||||||
params.ipaddress = this.addNetworkData.ip
|
|
||||||
}
|
|
||||||
this.showAddNetworkModal = false
|
|
||||||
this.loadingNic = true
|
|
||||||
api('addNicToVirtualMachine', params).then(response => {
|
|
||||||
this.$pollJob({
|
|
||||||
jobId: response.addnictovirtualmachineresponse.jobid,
|
|
||||||
successMessage: this.$t('message.success.add.network'),
|
|
||||||
successMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.closeModals()
|
|
||||||
},
|
|
||||||
errorMessage: this.$t('message.add.network.failed'),
|
|
||||||
errorMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.closeModals()
|
|
||||||
},
|
|
||||||
loadingMessage: this.$t('message.add.network.processing'),
|
|
||||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
|
||||||
catchMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.closeModals()
|
|
||||||
this.parentFetchData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
this.loadingNic = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setAsDefault (item) {
|
|
||||||
this.loadingNic = true
|
|
||||||
api('updateDefaultNicForVirtualMachine', {
|
|
||||||
virtualmachineid: this.vm.id,
|
|
||||||
nicid: item.id
|
|
||||||
}).then(response => {
|
|
||||||
this.$pollJob({
|
|
||||||
jobId: response.updatedefaultnicforvirtualmachineresponse.jobid,
|
|
||||||
successMessage: `${this.$t('label.success.set')} ${item.networkname} ${this.$t('label.as.default')}. ${this.$t('message.set.default.nic.manual')}.`,
|
|
||||||
successMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
},
|
|
||||||
errorMessage: `${this.$t('label.error.setting')} ${item.networkname} ${this.$t('label.as.default')}`,
|
|
||||||
errorMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
},
|
|
||||||
loadingMessage: `${this.$t('label.setting')} ${item.networkname} ${this.$t('label.as.default')}...`,
|
|
||||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
|
||||||
catchMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.parentFetchData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
this.loadingNic = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
submitUpdateIP () {
|
|
||||||
if (this.loadingNic) return
|
|
||||||
this.loadingNic = true
|
|
||||||
this.showUpdateIpModal = false
|
|
||||||
const params = {
|
|
||||||
nicId: this.editIpAddressNic
|
|
||||||
}
|
|
||||||
if (this.editIpAddressValue) {
|
|
||||||
params.ipaddress = this.editIpAddressValue
|
|
||||||
}
|
|
||||||
api('updateVmNicIp', params).then(response => {
|
|
||||||
this.$pollJob({
|
|
||||||
jobId: response.updatevmnicipresponse.jobid,
|
|
||||||
successMessage: this.$t('message.success.update.ipaddress'),
|
|
||||||
successMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.closeModals()
|
|
||||||
},
|
|
||||||
errorMessage: this.$t('label.error'),
|
|
||||||
errorMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.closeModals()
|
|
||||||
},
|
|
||||||
loadingMessage: this.$t('message.update.ipaddress.processing'),
|
|
||||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
|
||||||
catchMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.closeModals()
|
|
||||||
this.parentFetchData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
this.loadingNic = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
removeNIC (item) {
|
|
||||||
this.loadingNic = true
|
|
||||||
|
|
||||||
api('removeNicFromVirtualMachine', {
|
|
||||||
nicid: item.id,
|
|
||||||
virtualmachineid: this.vm.id
|
|
||||||
}).then(response => {
|
|
||||||
this.$pollJob({
|
|
||||||
jobId: response.removenicfromvirtualmachineresponse.jobid,
|
|
||||||
successMessage: this.$t('message.success.remove.nic'),
|
|
||||||
successMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
},
|
|
||||||
errorMessage: this.$t('message.error.remove.nic'),
|
|
||||||
errorMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
},
|
|
||||||
loadingMessage: this.$t('message.remove.nic.processing'),
|
|
||||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
|
||||||
catchMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.parentFetchData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
this.loadingNic = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
submitSecondaryIP () {
|
|
||||||
if (this.loadingNic) return
|
|
||||||
this.loadingNic = true
|
|
||||||
|
|
||||||
const params = {}
|
|
||||||
params.nicid = this.selectedNicId
|
|
||||||
if (this.newSecondaryIp) {
|
|
||||||
params.ipaddress = this.newSecondaryIp
|
|
||||||
}
|
|
||||||
|
|
||||||
api('addIpToNic', params).then(response => {
|
|
||||||
this.$pollJob({
|
|
||||||
jobId: response.addiptovmnicresponse.jobid,
|
|
||||||
successMessage: this.$t('message.success.add.secondary.ipaddress'),
|
|
||||||
successMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
},
|
|
||||||
errorMessage: this.$t('message.error.add.secondary.ipaddress'),
|
|
||||||
errorMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
},
|
|
||||||
loadingMessage: this.$t('message.add.secondary.ipaddress.processing'),
|
|
||||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
|
||||||
catchMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
this.parentFetchData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
this.loadingNic = false
|
|
||||||
}).finally(() => {
|
|
||||||
this.newSecondaryIp = null
|
|
||||||
this.fetchPublicIps(this.editNetworkId)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
removeSecondaryIP (id) {
|
|
||||||
this.loadingNic = true
|
|
||||||
|
|
||||||
api('removeIpFromNic', { id }).then(response => {
|
|
||||||
this.$pollJob({
|
|
||||||
jobId: response.removeipfromnicresponse.jobid,
|
|
||||||
successMessage: this.$t('message.success.remove.secondary.ipaddress'),
|
|
||||||
successMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
this.fetchPublicIps(this.editNetworkId)
|
|
||||||
},
|
|
||||||
errorMessage: this.$t('message.error.remove.secondary.ipaddress'),
|
|
||||||
errorMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
},
|
|
||||||
loadingMessage: this.$t('message.remove.secondary.ipaddress.processing'),
|
|
||||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
|
||||||
catchMethod: () => {
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
this.parentFetchData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}).catch(error => {
|
|
||||||
this.$notifyError(error)
|
|
||||||
this.loadingNic = false
|
|
||||||
this.fetchSecondaryIPs(this.selectedNicId)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
updateSecurityGroupsSelection (securitygroupids) {
|
updateSecurityGroupsSelection (securitygroupids) {
|
||||||
this.securitygroupids = securitygroupids || []
|
this.securitygroupids = securitygroupids || []
|
||||||
|
|||||||
602
ui/src/views/network/NicsTab.vue
Normal file
602
ui/src/views/network/NicsTab.vue
Normal file
@ -0,0 +1,602 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loadingNic">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
style="width: 100%; margin-bottom: 10px"
|
||||||
|
@click="showAddNicModal"
|
||||||
|
:loading="loadingNic"
|
||||||
|
:disabled="!('addNicToVirtualMachine' in $store.getters.apis)">
|
||||||
|
<template #icon><plus-outlined /></template> {{ $t('label.network.addvm') }}
|
||||||
|
</a-button>
|
||||||
|
<NicsTable :resource="resource" :loading="loading">
|
||||||
|
<template #actions="record">
|
||||||
|
<a-popconfirm
|
||||||
|
:title="$t('label.set.default.nic')"
|
||||||
|
@confirm="setAsDefault(record.nic)"
|
||||||
|
:okText="$t('label.yes')"
|
||||||
|
:cancelText="$t('label.no')"
|
||||||
|
v-if="!record.nic.isdefault"
|
||||||
|
>
|
||||||
|
<tooltip-button
|
||||||
|
tooltipPlacement="bottom"
|
||||||
|
:tooltip="$t('label.set.default.nic')"
|
||||||
|
:disabled="!('updateDefaultNicForVirtualMachine' in $store.getters.apis)"
|
||||||
|
icon="check-square-outlined" />
|
||||||
|
</a-popconfirm>
|
||||||
|
<tooltip-button
|
||||||
|
v-if="record.nic.type !== 'L2'"
|
||||||
|
tooltipPlacement="bottom"
|
||||||
|
:tooltip="$t('label.change.ip.address')"
|
||||||
|
icon="swap-outlined"
|
||||||
|
:disabled="!('updateVmNicIp' in $store.getters.apis)"
|
||||||
|
@onClick="onChangeIPAddress(record)" />
|
||||||
|
<tooltip-button
|
||||||
|
v-if="record.nic.type !== 'L2'"
|
||||||
|
tooltipPlacement="bottom"
|
||||||
|
:tooltip="$t('label.edit.secondary.ips')"
|
||||||
|
icon="environment-outlined"
|
||||||
|
:disabled="(!('addIpToNic' in $store.getters.apis) && !('addIpToNic' in $store.getters.apis))"
|
||||||
|
@onClick="onAcquireSecondaryIPAddress(record)" />
|
||||||
|
<a-popconfirm
|
||||||
|
:title="$t('message.network.removenic')"
|
||||||
|
@confirm="removeNIC(record.nic)"
|
||||||
|
:okText="$t('label.yes')"
|
||||||
|
:cancelText="$t('label.no')"
|
||||||
|
v-if="!record.nic.isdefault"
|
||||||
|
>
|
||||||
|
<tooltip-button
|
||||||
|
tooltipPlacement="bottom"
|
||||||
|
:tooltip="$t('label.action.remove.nic')"
|
||||||
|
:disabled="!('removeNicFromVirtualMachine' in $store.getters.apis)"
|
||||||
|
type="primary"
|
||||||
|
:danger="true"
|
||||||
|
icon="delete-outlined" />
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
</NicsTable>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
:visible="showAddNetworkModal"
|
||||||
|
:title="$t('label.network.addvm')"
|
||||||
|
:maskClosable="false"
|
||||||
|
:closable="true"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="closeModals">
|
||||||
|
{{ $t('message.network.addvm.desc') }}
|
||||||
|
<a-form @finish="submitAddNetwork" v-ctrl-enter="submitAddNetwork">
|
||||||
|
<div class="modal-form">
|
||||||
|
<p class="modal-form__label">{{ $t('label.network') }}:</p>
|
||||||
|
<a-select
|
||||||
|
:value="addNetworkData.network"
|
||||||
|
@change="e => addNetworkData.network = e"
|
||||||
|
v-focus="true"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="network in addNetworkData.allNetworks"
|
||||||
|
:key="network.id"
|
||||||
|
:value="network.id"
|
||||||
|
:label="network.name">
|
||||||
|
<span>
|
||||||
|
<resource-icon v-if="network.icon" :image="network.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||||
|
<apartment-outlined v-else style="margin-right: 5px" />
|
||||||
|
{{ network.name }}
|
||||||
|
</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<p class="modal-form__label">{{ $t('label.publicip') }}:</p>
|
||||||
|
<a-input v-model:value="addNetworkData.ip"></a-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeModals">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" ref="submit" @click="submitAddNetwork">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
:visible="showUpdateIpModal"
|
||||||
|
:title="$t('label.change.ipaddress')"
|
||||||
|
:maskClosable="false"
|
||||||
|
:closable="true"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="closeModals"
|
||||||
|
>
|
||||||
|
{{ $t('message.network.updateip') }}
|
||||||
|
|
||||||
|
<a-form @finish="submitUpdateIP" v-ctrl-enter="submitUpdateIP">
|
||||||
|
<div class="modal-form">
|
||||||
|
<p class="modal-form__label">{{ $t('label.publicip') }}:</p>
|
||||||
|
<a-select
|
||||||
|
v-if="editNicResource.type==='Shared'"
|
||||||
|
v-model:value="editIpAddressValue"
|
||||||
|
:loading="listIps.loading"
|
||||||
|
v-focus="editNicResource.type==='Shared'"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="value"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}">
|
||||||
|
<a-select-option v-for="ip in listIps.opts" :key="ip.ipaddress">
|
||||||
|
{{ ip.ipaddress }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-input
|
||||||
|
v-else
|
||||||
|
v-model:value="editIpAddressValue"
|
||||||
|
v-focus="editNicResource.type!=='Shared'"></a-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeModals">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" ref="submit" @click="submitUpdateIP">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
:visible="showSecondaryIpModal"
|
||||||
|
:title="$t('label.acquire.new.secondary.ip')"
|
||||||
|
:maskClosable="false"
|
||||||
|
:footer="null"
|
||||||
|
:closable="false"
|
||||||
|
class="wide-modal"
|
||||||
|
@cancel="closeModals"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
{{ $t('message.network.secondaryip') }}
|
||||||
|
</p>
|
||||||
|
<a-divider />
|
||||||
|
<div v-ctrl-enter="submitSecondaryIP">
|
||||||
|
<div class="modal-form">
|
||||||
|
<p class="modal-form__label">{{ $t('label.publicip') }}:</p>
|
||||||
|
<a-select
|
||||||
|
v-if="editNicResource.type==='Shared'"
|
||||||
|
v-model:value="newSecondaryIp"
|
||||||
|
:loading="listIps.loading"
|
||||||
|
v-focus="editNicResource.type==='Shared'"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="value"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}">
|
||||||
|
<a-select-option v-for="ip in listIps.opts" :key="ip.ipaddress">
|
||||||
|
{{ ip.ipaddress }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-input
|
||||||
|
v-else
|
||||||
|
:placeholder="$t('label.new.secondaryip.description')"
|
||||||
|
v-model:value="newSecondaryIp"
|
||||||
|
v-focus="editNicResource.type!=='Shared'"></a-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 10px; display: flex; justify-content:flex-end;">
|
||||||
|
<a-button @click="submitSecondaryIP" ref="submit" type="primary" style="margin-right: 10px;">{{ $t('label.add.secondary.ip') }}</a-button>
|
||||||
|
<a-button @click="closeModals">{{ $t('label.close') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
<a-list itemLayout="vertical">
|
||||||
|
<a-list-item v-for="(ip, index) in secondaryIPs" :key="index">
|
||||||
|
<a-popconfirm
|
||||||
|
:title="`${$t('label.action.release.ip')}?`"
|
||||||
|
@confirm="removeSecondaryIP(ip.id)"
|
||||||
|
:okText="$t('label.yes')"
|
||||||
|
:cancelText="$t('label.no')"
|
||||||
|
>
|
||||||
|
<tooltip-button
|
||||||
|
tooltipPlacement="top"
|
||||||
|
:tooltip="$t('label.action.release.ip')"
|
||||||
|
type="primary"
|
||||||
|
:danger="true"
|
||||||
|
icon="delete-outlined" />
|
||||||
|
{{ ip.ipaddress }}
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
|
</a-modal>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
import NicsTable from '@/views/network/NicsTable'
|
||||||
|
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||||
|
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NicsTab',
|
||||||
|
components: {
|
||||||
|
NicsTable,
|
||||||
|
TooltipButton,
|
||||||
|
ResourceIcon
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
vm: {},
|
||||||
|
showAddNetworkModal: false,
|
||||||
|
showUpdateIpModal: false,
|
||||||
|
showSecondaryIpModal: false,
|
||||||
|
addNetworkData: {
|
||||||
|
allNetworks: [],
|
||||||
|
network: '',
|
||||||
|
ip: ''
|
||||||
|
},
|
||||||
|
loadingNic: false,
|
||||||
|
editIpAddressNic: '',
|
||||||
|
editIpAddressValue: '',
|
||||||
|
editNetworkId: '',
|
||||||
|
secondaryIPs: [],
|
||||||
|
selectedNicId: '',
|
||||||
|
newSecondaryIp: '',
|
||||||
|
editNicResource: {},
|
||||||
|
listIps: {
|
||||||
|
loading: false,
|
||||||
|
opts: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.vm = this.resource
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
listNetworks () {
|
||||||
|
api('listNetworks', {
|
||||||
|
listAll: 'true',
|
||||||
|
showicon: true,
|
||||||
|
zoneid: this.vm.zoneid
|
||||||
|
}).then(response => {
|
||||||
|
this.addNetworkData.allNetworks = response.listnetworksresponse.network.filter(network => !this.vm.nic.map(nic => nic.networkid).includes(network.id))
|
||||||
|
this.addNetworkData.network = this.addNetworkData.allNetworks[0].id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchSecondaryIPs (nicId) {
|
||||||
|
this.showSecondaryIpModal = true
|
||||||
|
this.selectedNicId = nicId
|
||||||
|
api('listNics', {
|
||||||
|
nicId: nicId,
|
||||||
|
keyword: '',
|
||||||
|
virtualmachineid: this.vm.id
|
||||||
|
}).then(response => {
|
||||||
|
this.secondaryIPs = response.listnicsresponse.nic[0].secondaryip
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchPublicIps (networkid) {
|
||||||
|
this.listIps.loading = true
|
||||||
|
this.listIps.opts = []
|
||||||
|
api('listPublicIpAddresses', {
|
||||||
|
networkid: networkid,
|
||||||
|
allocatedonly: false,
|
||||||
|
forvirtualnetwork: false
|
||||||
|
}).then(json => {
|
||||||
|
const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || []
|
||||||
|
listPublicIps.forEach(item => {
|
||||||
|
if (item.state === 'Free') {
|
||||||
|
this.listIps.opts.push({
|
||||||
|
ipaddress: item.ipaddress
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.listIps.opts.sort(function (a, b) {
|
||||||
|
const currentIp = a.ipaddress.replaceAll('.', '')
|
||||||
|
const nextIp = b.ipaddress.replaceAll('.', '')
|
||||||
|
if (parseInt(currentIp) < parseInt(nextIp)) { return -1 }
|
||||||
|
if (parseInt(currentIp) > parseInt(nextIp)) { return 1 }
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
}).finally(() => {
|
||||||
|
this.listIps.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showAddNicModal () {
|
||||||
|
this.showAddNetworkModal = true
|
||||||
|
this.listNetworks()
|
||||||
|
},
|
||||||
|
closeModals () {
|
||||||
|
this.showAddNetworkModal = false
|
||||||
|
this.showUpdateIpModal = false
|
||||||
|
this.showSecondaryIpModal = false
|
||||||
|
this.addNetworkData.network = ''
|
||||||
|
this.addNetworkData.ip = ''
|
||||||
|
this.editIpAddressValue = ''
|
||||||
|
this.newSecondaryIp = ''
|
||||||
|
},
|
||||||
|
onChangeIPAddress (record) {
|
||||||
|
this.editNicResource = record.nic
|
||||||
|
this.editIpAddressNic = record.nic.id
|
||||||
|
this.showUpdateIpModal = true
|
||||||
|
if (record.nic.type === 'Shared') {
|
||||||
|
this.fetchPublicIps(record.nic.networkid)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onAcquireSecondaryIPAddress (record) {
|
||||||
|
if (record.nic.type === 'Shared') {
|
||||||
|
this.fetchPublicIps(record.nic.networkid)
|
||||||
|
} else {
|
||||||
|
this.listIps.opts = []
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editNicResource = record.nic
|
||||||
|
this.editNetworkId = record.nic.networkid
|
||||||
|
this.fetchSecondaryIPs(record.nic.id)
|
||||||
|
},
|
||||||
|
submitAddNetwork () {
|
||||||
|
if (this.loadingNic) return
|
||||||
|
const params = {}
|
||||||
|
params.virtualmachineid = this.vm.id
|
||||||
|
params.networkid = this.addNetworkData.network
|
||||||
|
if (this.addNetworkData.ip) {
|
||||||
|
params.ipaddress = this.addNetworkData.ip
|
||||||
|
}
|
||||||
|
this.showAddNetworkModal = false
|
||||||
|
this.loadingNic = true
|
||||||
|
api('addNicToVirtualMachine', params).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.addnictovirtualmachineresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.add.network'),
|
||||||
|
successMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.closeModals()
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('message.add.network.failed'),
|
||||||
|
errorMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.closeModals()
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.add.network.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.closeModals()
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
this.loadingNic = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setAsDefault (item) {
|
||||||
|
this.loadingNic = true
|
||||||
|
api('updateDefaultNicForVirtualMachine', {
|
||||||
|
virtualmachineid: this.vm.id,
|
||||||
|
nicid: item.id
|
||||||
|
}).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.updatedefaultnicforvirtualmachineresponse.jobid,
|
||||||
|
successMessage: `${this.$t('label.success.set')} ${item.networkname} ${this.$t('label.as.default')}. ${this.$t('message.set.default.nic.manual')}.`,
|
||||||
|
successMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
},
|
||||||
|
errorMessage: `${this.$t('label.error.setting')} ${item.networkname} ${this.$t('label.as.default')}`,
|
||||||
|
errorMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
},
|
||||||
|
loadingMessage: `${this.$t('label.setting')} ${item.networkname} ${this.$t('label.as.default')}...`,
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
this.loadingNic = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitUpdateIP () {
|
||||||
|
if (this.loadingNic) return
|
||||||
|
this.loadingNic = true
|
||||||
|
this.showUpdateIpModal = false
|
||||||
|
const params = {
|
||||||
|
nicId: this.editIpAddressNic
|
||||||
|
}
|
||||||
|
if (this.editIpAddressValue) {
|
||||||
|
params.ipaddress = this.editIpAddressValue
|
||||||
|
}
|
||||||
|
api('updateVmNicIp', params).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.updatevmnicipresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.update.ipaddress'),
|
||||||
|
successMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.closeModals()
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('label.error'),
|
||||||
|
errorMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.closeModals()
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.update.ipaddress.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.closeModals()
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
this.loadingNic = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeNIC (item) {
|
||||||
|
this.loadingNic = true
|
||||||
|
|
||||||
|
api('removeNicFromVirtualMachine', {
|
||||||
|
nicid: item.id,
|
||||||
|
virtualmachineid: this.vm.id
|
||||||
|
}).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.removenicfromvirtualmachineresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.remove.nic'),
|
||||||
|
successMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('message.error.remove.nic'),
|
||||||
|
errorMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.remove.nic.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
this.loadingNic = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitSecondaryIP () {
|
||||||
|
if (this.loadingNic) return
|
||||||
|
this.loadingNic = true
|
||||||
|
|
||||||
|
const params = {}
|
||||||
|
params.nicid = this.selectedNicId
|
||||||
|
if (this.newSecondaryIp) {
|
||||||
|
params.ipaddress = this.newSecondaryIp
|
||||||
|
}
|
||||||
|
|
||||||
|
api('addIpToNic', params).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.addiptovmnicresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.add.secondary.ipaddress'),
|
||||||
|
successMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('message.error.add.secondary.ipaddress'),
|
||||||
|
errorMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.add.secondary.ipaddress.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
this.loadingNic = false
|
||||||
|
}).finally(() => {
|
||||||
|
this.newSecondaryIp = null
|
||||||
|
this.fetchPublicIps(this.editNetworkId)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeSecondaryIP (id) {
|
||||||
|
this.loadingNic = true
|
||||||
|
|
||||||
|
api('removeIpFromNic', { id }).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.removeipfromnicresponse.jobid,
|
||||||
|
successMessage: this.$t('message.success.remove.secondary.ipaddress'),
|
||||||
|
successMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
this.fetchPublicIps(this.editNetworkId)
|
||||||
|
},
|
||||||
|
errorMessage: this.$t('message.error.remove.secondary.ipaddress'),
|
||||||
|
errorMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
},
|
||||||
|
loadingMessage: this.$t('message.remove.secondary.ipaddress.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||||
|
catchMethod: () => {
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
this.loadingNic = false
|
||||||
|
this.fetchSecondaryIPs(this.selectedNicId)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&--no-margin {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 5px;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
align-self: flex-start;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wide-modal {
|
||||||
|
min-width: 50vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-list-item) {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
236
ui/src/views/storage/ChangeSharedFSDiskOffering.vue
Normal file
236
ui/src/views/storage/ChangeSharedFSDiskOffering.vue
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
@finish="handleSubmit"
|
||||||
|
>
|
||||||
|
<a-form-item ref="diskofferingid" name="diskofferingid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.diskofferingid')" :tooltip="apiParams.diskofferingid.description || 'Disk Offering'"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.diskofferingid"
|
||||||
|
:loading="diskofferingLoading"
|
||||||
|
@change="id => handleDiskOfferingChange(id)"
|
||||||
|
:placeholder="apiParams.diskofferingid.description || $t('label.diskofferingid')"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="(diskoffering, index) in diskofferings"
|
||||||
|
:value="diskoffering.id"
|
||||||
|
:key="index"
|
||||||
|
:label="diskoffering.displaytext || diskoffering.name">
|
||||||
|
{{ diskoffering.displaytext || diskoffering.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<span v-if="customDiskOffering">
|
||||||
|
<a-form-item ref="size" name="size">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.sizegb')" :tooltip="apiParams.size.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.size"
|
||||||
|
:placeholder="apiParams.size.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
</span>
|
||||||
|
<span v-if="isCustomizedDiskIOps">
|
||||||
|
<a-form-item ref="miniops" name="miniops">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.miniops')" :tooltip="apiParams.miniops.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.miniops"
|
||||||
|
:placeholder="apiParams.miniops.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="maxiops" name="maxiops">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.maxiops')" :tooltip="apiParams.maxiops.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.maxiops"
|
||||||
|
:placeholder="apiParams.maxiops.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
</span>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" ref="submit" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import { mixinForm } from '@/utils/mixin'
|
||||||
|
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateSharedFS',
|
||||||
|
mixins: [mixinForm],
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ResourceIcon,
|
||||||
|
TooltipLabel
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
owner: {
|
||||||
|
projectid: store.getters.project?.id,
|
||||||
|
domainid: store.getters.project?.id ? null : store.getters.userInfo.domainid,
|
||||||
|
account: store.getters.project?.id ? null : store.getters.userInfo.account
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
diskofferings: [],
|
||||||
|
diskofferingLoading: false,
|
||||||
|
customDiskOffering: false,
|
||||||
|
isCustomizedDiskIOps: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('changeSharedFileSystemDiskOffering')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({
|
||||||
|
})
|
||||||
|
this.rules = reactive({
|
||||||
|
diskofferingid: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
size: [{ required: true, message: this.$t('message.error.custom.disk.size') }],
|
||||||
|
miniops: [{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (value && (isNaN(value) || value <= 0)) {
|
||||||
|
return Promise.reject(this.$t('message.error.number'))
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
maxiops: [{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (value && (isNaN(value) || value <= 0)) {
|
||||||
|
return Promise.reject(this.$t('message.error.number'))
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchData () {
|
||||||
|
this.fetchDiskOfferings()
|
||||||
|
},
|
||||||
|
fetchDiskOfferings () {
|
||||||
|
this.diskOfferingLoading = true
|
||||||
|
var params = {
|
||||||
|
zoneid: this.resource.zoneid,
|
||||||
|
listall: true,
|
||||||
|
domainid: this.owner.domainid
|
||||||
|
}
|
||||||
|
if (this.owner.projectid) {
|
||||||
|
params.projectid = this.owner.projectid
|
||||||
|
} else {
|
||||||
|
params.account = this.owner.account
|
||||||
|
}
|
||||||
|
api('listDiskOfferings', params).then(json => {
|
||||||
|
this.diskofferings = json.listdiskofferingsresponse.diskoffering || []
|
||||||
|
this.form.diskofferingid = this.diskofferings[0].id || ''
|
||||||
|
this.customDiskOffering = this.diskofferings[0].iscustomized || false
|
||||||
|
this.isCustomizedDiskIOps = this.diskofferings[0]?.iscustomizediops || false
|
||||||
|
}).finally(() => {
|
||||||
|
this.diskOfferingLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
handleDiskOfferingChange (id) {
|
||||||
|
const diskoffering = this.diskofferings.filter(x => x.id === id)
|
||||||
|
this.customDiskOffering = diskoffering[0]?.iscustomized || false
|
||||||
|
this.isCustomizedDiskIOps = diskoffering[0]?.iscustomizediops || false
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
this.formRef.value.validate().then(async () => {
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
id: this.resource.id,
|
||||||
|
diskofferingid: values.diskofferingid,
|
||||||
|
size: values.size,
|
||||||
|
minops: values.miniops,
|
||||||
|
maxops: values.maxiops
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
api('changeSharedFileSystemDiskOffering', data).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.changesharedfilesystemdiskofferingresponse.jobid,
|
||||||
|
title: this.$t('label.change.disk.offering'),
|
||||||
|
description: values.name,
|
||||||
|
successMessage: this.$t('message.success.change.offering'),
|
||||||
|
errorMessage: this.$t('message.change.disk.offering.sharedfs.failed'),
|
||||||
|
loadingMessage: this.$t('message.change.disk.offering.sharedfs.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result')
|
||||||
|
})
|
||||||
|
this.closeModal()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-layout {
|
||||||
|
width: 85vw;
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
width: 35vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
204
ui/src/views/storage/ChangeSharedFSServiceOffering.vue
Normal file
204
ui/src/views/storage/ChangeSharedFSServiceOffering.vue
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
@finish="handleSubmit"
|
||||||
|
>
|
||||||
|
<a-form-item>
|
||||||
|
<a-alert type="warning">
|
||||||
|
<template #message>
|
||||||
|
<span v-html="$t('message.confirm.change.service.offering.for.sharedfs')" />
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="serviceofferingid" name="serviceofferingid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description || 'Service Offering'"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.serviceofferingid"
|
||||||
|
:loading="serviceofferingLoading"
|
||||||
|
:placeholder="apiParams.serviceofferingid.description || $t('label.serviceofferingid')"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="(serviceoffering, index) in serviceofferings"
|
||||||
|
:value="serviceoffering.id"
|
||||||
|
:key="index"
|
||||||
|
:label="serviceoffering.name || serviceoffering.displaytext">
|
||||||
|
{{ serviceoffering.name || serviceoffering.displaytext }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" ref="submit" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import { mixinForm } from '@/utils/mixin'
|
||||||
|
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateSharedFS',
|
||||||
|
mixins: [mixinForm],
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ResourceIcon,
|
||||||
|
TooltipLabel
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
owner: {
|
||||||
|
projectid: store.getters.project?.id,
|
||||||
|
domainid: store.getters.project?.id ? null : store.getters.userInfo.domainid,
|
||||||
|
account: store.getters.project?.id ? null : store.getters.userInfo.account
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
configLoading: false,
|
||||||
|
serviceofferings: [],
|
||||||
|
serviceofferingLoding: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('changeSharedFileSystemServiceOffering')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({
|
||||||
|
})
|
||||||
|
this.rules = reactive({
|
||||||
|
serviceofferingid: [{ required: true, message: this.$t('label.required') }]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
arrayHasItems (array) {
|
||||||
|
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||||
|
},
|
||||||
|
fetchData () {
|
||||||
|
this.fetchServiceOfferings()
|
||||||
|
},
|
||||||
|
fetchCapabilities (id) {
|
||||||
|
api('listCapabilities').then(json => {
|
||||||
|
this.capability = json.listcapabilitiesresponse.capability || []
|
||||||
|
this.minCpu = this.capability.sharedfsvmmincpucount
|
||||||
|
this.minMemory = this.capability.sharedfsvmminramsize
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchServiceOfferings () {
|
||||||
|
this.fetchCapabilities()
|
||||||
|
this.serviceofferingLoading = true
|
||||||
|
var params = {
|
||||||
|
zoneid: this.resource.zoneid,
|
||||||
|
listall: true,
|
||||||
|
domainid: this.owner.domainid
|
||||||
|
}
|
||||||
|
if (this.owner.projectid) {
|
||||||
|
params.projectid = this.owner.projectid
|
||||||
|
} else {
|
||||||
|
params.account = this.owner.account
|
||||||
|
}
|
||||||
|
api('listServiceOfferings', params).then(json => {
|
||||||
|
var items = json.listserviceofferingsresponse.serviceoffering || []
|
||||||
|
if (items != null) {
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].iscustomized === false && items[i].offerha === true &&
|
||||||
|
items[i].cpunumber >= this.minCpu && items[i].memory >= this.minMemory) {
|
||||||
|
this.serviceofferings.push(items[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.form.serviceofferingid = this.serviceofferings[0].id || ''
|
||||||
|
})
|
||||||
|
this.serviceofferingLoading = false
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
this.formRef.value.validate().then(async () => {
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
id: this.resource.id,
|
||||||
|
serviceofferingid: values.serviceofferingid
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
api('changeSharedFileSystemServiceOffering', data).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.changesharedfilesystemserviceofferingresponse.jobid,
|
||||||
|
title: this.$t('label.change.service.offering'),
|
||||||
|
description: values.name,
|
||||||
|
successMessage: this.$t('message.success.change.offering'),
|
||||||
|
errorMessage: this.$t('message.change.service.offering.sharedfs.failed'),
|
||||||
|
loadingMessage: this.$t('message.change.service.offering.sharedfs.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result')
|
||||||
|
})
|
||||||
|
this.closeModal()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-layout {
|
||||||
|
width: 85vw;
|
||||||
|
|
||||||
|
@media (min-width: 1000px) {
|
||||||
|
width: 35vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
459
ui/src/views/storage/CreateSharedFS.vue
Normal file
459
ui/src/views/storage/CreateSharedFS.vue
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div v-if="!isNormalUserOrProject">
|
||||||
|
<ownership-selection @fetch-owner="fetchOwnerOptions" />
|
||||||
|
</div>
|
||||||
|
<a-form
|
||||||
|
class="form"
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
@finish="handleSubmit"
|
||||||
|
v-ctrl-enter="handleSubmit"
|
||||||
|
>
|
||||||
|
<a-form-item name="name" ref="name" :label="$t('label.name')">
|
||||||
|
<a-input v-model:value="form.name" v-focus="true" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="description" ref="description" :label="$t('label.description')">
|
||||||
|
<a-input v-model:value="form.description" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="zoneid" name="zoneid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.zoneid"
|
||||||
|
:loading="zoneLoading"
|
||||||
|
@change="zone => handleZoneChange(id)"
|
||||||
|
:placeholder="apiParams.zoneid.description"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="(zone, index) in zones"
|
||||||
|
:value="zone.id"
|
||||||
|
:key="index"
|
||||||
|
:label="zone.name">
|
||||||
|
<span>
|
||||||
|
<resource-icon v-if="zone.icon" :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||||
|
<global-outlined v-else style="margin-right: 5px"/>
|
||||||
|
{{ zone.name }}
|
||||||
|
</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="filesystem" name="filesystem">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.filesystem')" :tooltip="apiParams.filesystem.description"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.filesystem"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option value="XFS" label="XFS">XFS</a-select-option>
|
||||||
|
<a-select-option value="EXT4" label="EXT4">EXT4</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="networkid" name="networkid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.networkid')" :tooltip="apiParams.networkid.description || 'Network'"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.networkid"
|
||||||
|
:loading="networkLoading"
|
||||||
|
:placeholder="apiParams.networkid.description || $t('label.networkid')"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="(network, index) in networks"
|
||||||
|
:value="network.id"
|
||||||
|
:key="index"
|
||||||
|
:label="network.name"> {{ network.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="diskofferingid" name="diskofferingid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.diskofferingid')" :tooltip="apiParams.diskofferingid.description || 'Disk Offering'"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.diskofferingid"
|
||||||
|
:loading="diskofferingLoading"
|
||||||
|
@change="id => handleDiskOfferingChange(id)"
|
||||||
|
:placeholder="apiParams.diskofferingid.description || $t('label.diskofferingid')"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="(diskoffering, index) in diskofferings"
|
||||||
|
:value="diskoffering.id"
|
||||||
|
:key="index"
|
||||||
|
:label="diskoffering.displaytext || diskoffering.name">
|
||||||
|
{{ diskoffering.displaytext || diskoffering.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<span v-if="customDiskOffering">
|
||||||
|
<a-form-item ref="size" name="size">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.sizegb')" :tooltip="apiParams.size.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.size"
|
||||||
|
:placeholder="apiParams.size.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
</span>
|
||||||
|
<span v-if="isCustomizedDiskIOps">
|
||||||
|
<a-form-item ref="miniops" name="miniops">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.miniops')" :tooltip="apiParams.miniops.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.miniops"
|
||||||
|
:placeholder="apiParams.miniops.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item ref="maxiops" name="maxiops">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.maxiops')" :tooltip="apiParams.maxiops.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.maxiops"
|
||||||
|
:placeholder="apiParams.maxiops.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
</span>
|
||||||
|
<a-form-item ref="serviceofferingid" name="serviceofferingid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.compute.offering.for.vm')" :tooltip="apiParams.serviceofferingid.description || 'Service Offering'"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.serviceofferingid"
|
||||||
|
:loading="serviceofferingLoading"
|
||||||
|
:placeholder="$t('label.serviceofferingid')"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}" >
|
||||||
|
<a-select-option
|
||||||
|
v-for="(serviceoffering, index) in serviceofferings"
|
||||||
|
:value="serviceoffering.id"
|
||||||
|
:key="index"
|
||||||
|
:label="serviceoffering.displaytext || serviceoffering.name">
|
||||||
|
{{ serviceoffering.displaytext || serviceoffering.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" ref="submit" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import { mixinForm } from '@/utils/mixin'
|
||||||
|
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||||
|
import OwnershipSelection from '@/views/compute/wizard/OwnershipSelection.vue'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateSharedFS',
|
||||||
|
mixins: [mixinForm],
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
OwnershipSelection,
|
||||||
|
ResourceIcon,
|
||||||
|
TooltipLabel
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
owner: {
|
||||||
|
projectid: store.getters.project?.id,
|
||||||
|
domainid: store.getters.project?.id ? null : store.getters.userInfo.domainid,
|
||||||
|
account: store.getters.project?.id ? null : store.getters.userInfo.account
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
zones: [],
|
||||||
|
zoneLoading: false,
|
||||||
|
configLoading: false,
|
||||||
|
networks: [],
|
||||||
|
networkLoading: false,
|
||||||
|
serviceofferings: [],
|
||||||
|
serviceofferingLoading: false,
|
||||||
|
diskofferings: [],
|
||||||
|
diskofferingLoading: false,
|
||||||
|
customDiskOffering: false,
|
||||||
|
isCustomizedDiskIOps: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNormalUserOrProject () {
|
||||||
|
return ['User'].includes(this.$store.getters.userInfo.roletype) || store.getters.project?.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('createSharedFileSystem')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
this.fetchData()
|
||||||
|
this.form.filesystem = 'XFS'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({
|
||||||
|
})
|
||||||
|
this.rules = reactive({
|
||||||
|
zoneid: [{ required: true, message: this.$t('message.error.zone') }],
|
||||||
|
name: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
networkid: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
serviceofferingid: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
diskofferingid: [{ required: true, message: this.$t('label.required') }],
|
||||||
|
size: [{ required: true, message: this.$t('message.error.custom.disk.size') }],
|
||||||
|
miniops: [{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (value && (isNaN(value) || value <= 0)) {
|
||||||
|
return Promise.reject(this.$t('message.error.number'))
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
maxiops: [{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (value && (isNaN(value) || value <= 0)) {
|
||||||
|
return Promise.reject(this.$t('message.error.number'))
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
arrayHasItems (array) {
|
||||||
|
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||||
|
},
|
||||||
|
fetchOwnerOptions (OwnerOptions) {
|
||||||
|
this.owner = {}
|
||||||
|
console.log('fetching owner')
|
||||||
|
if (OwnerOptions.selectedAccountType === this.$t('label.account')) {
|
||||||
|
if (!OwnerOptions.selectedAccount) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('fetched account')
|
||||||
|
this.owner.account = OwnerOptions.selectedAccount
|
||||||
|
this.owner.domainid = OwnerOptions.selectedDomain
|
||||||
|
} else if (OwnerOptions.selectedAccountType === this.$t('label.project')) {
|
||||||
|
if (!OwnerOptions.selectedProject) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('fetched project')
|
||||||
|
this.owner.projectid = OwnerOptions.selectedProject
|
||||||
|
}
|
||||||
|
console.log('fetched owner')
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
fetchData () {
|
||||||
|
this.minCpu = store.getters.features.sharedfsvmmincpucount
|
||||||
|
this.minMemory = store.getters.features.sharedfsvmminramsize
|
||||||
|
this.fetchZones()
|
||||||
|
},
|
||||||
|
fetchZones () {
|
||||||
|
this.zoneLoading = true
|
||||||
|
const params = { showicon: true }
|
||||||
|
api('listZones', params).then(json => {
|
||||||
|
var listZones = json.listzonesresponse.zone
|
||||||
|
if (listZones) {
|
||||||
|
this.zones = []
|
||||||
|
listZones = listZones.filter(x => (x.allocationstate === 'Enabled' && x.networktype === 'Advanced' && x.securitygroupsenabled === false))
|
||||||
|
this.zones = this.zones.concat(listZones)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.zoneLoading = false
|
||||||
|
if (this.arrayHasItems(this.zones)) {
|
||||||
|
this.form.zoneid = this.zones[0].id
|
||||||
|
this.handleZoneChange(this.zones[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleZoneChange (zone) {
|
||||||
|
this.selectedZone = zone
|
||||||
|
this.fetchServiceOfferings()
|
||||||
|
this.fetchDiskOfferings()
|
||||||
|
this.fetchNetworks()
|
||||||
|
},
|
||||||
|
fetchServiceOfferings () {
|
||||||
|
this.serviceofferingLoading = true
|
||||||
|
this.serviceofferings = []
|
||||||
|
var params = {
|
||||||
|
zoneid: this.selectedZone.id,
|
||||||
|
listall: true,
|
||||||
|
domainid: this.owner.domainid
|
||||||
|
}
|
||||||
|
if (this.owner.projectid) {
|
||||||
|
params.projectid = this.owner.projectid
|
||||||
|
} else {
|
||||||
|
params.account = this.owner.account
|
||||||
|
}
|
||||||
|
api('listServiceOfferings', params).then(json => {
|
||||||
|
var items = json.listserviceofferingsresponse.serviceoffering || []
|
||||||
|
if (items != null) {
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].iscustomized === false && items[i].offerha === true &&
|
||||||
|
items[i].cpunumber >= this.minCpu && items[i].memory >= this.minMemory) {
|
||||||
|
this.serviceofferings.push(items[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.form.serviceofferingid = this.serviceofferings[0].id || ''
|
||||||
|
}).finally(() => {
|
||||||
|
this.serviceofferingLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchDiskOfferings () {
|
||||||
|
this.diskofferingLoading = true
|
||||||
|
this.form.diskofferingid = null
|
||||||
|
var params = {
|
||||||
|
zoneid: this.selectedZone.id,
|
||||||
|
listall: true,
|
||||||
|
domainid: this.owner.domainid
|
||||||
|
}
|
||||||
|
if (this.owner.projectid) {
|
||||||
|
params.projectid = this.owner.projectid
|
||||||
|
} else {
|
||||||
|
params.account = this.owner.account
|
||||||
|
}
|
||||||
|
api('listDiskOfferings', params).then(json => {
|
||||||
|
this.diskofferings = json.listdiskofferingsresponse.diskoffering || []
|
||||||
|
this.form.diskofferingid = this.diskofferings[0].id || ''
|
||||||
|
this.customDiskOffering = this.diskofferings[0].iscustomized || false
|
||||||
|
this.isCustomizedDiskIOps = this.diskofferings[0]?.iscustomizediops || false
|
||||||
|
}).finally(() => {
|
||||||
|
this.diskofferingLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchNetworks () {
|
||||||
|
this.networkLoading = true
|
||||||
|
this.form.networkid = null
|
||||||
|
var params = {
|
||||||
|
zoneid: this.selectedZone.id,
|
||||||
|
canusefordeploy: true,
|
||||||
|
domainid: this.owner.domainid
|
||||||
|
}
|
||||||
|
if (this.owner.projectid) {
|
||||||
|
params.projectid = this.owner.projectid
|
||||||
|
} else {
|
||||||
|
params.account = this.owner.account
|
||||||
|
}
|
||||||
|
api('listNetworks', params).then(json => {
|
||||||
|
this.networks = json.listnetworksresponse.network || []
|
||||||
|
this.form.networkid = this.networks[0].id || ''
|
||||||
|
}).finally(() => {
|
||||||
|
this.networkLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
handleDiskOfferingChange (id) {
|
||||||
|
const diskoffering = this.diskofferings.filter(x => x.id === id)
|
||||||
|
this.customDiskOffering = diskoffering[0]?.iscustomized || false
|
||||||
|
this.isCustomizedDiskIOps = diskoffering[0]?.iscustomizediops || false
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
this.formRef.value.validate().then(async () => {
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
name: values.name,
|
||||||
|
description: values.description,
|
||||||
|
zoneid: values.zoneid,
|
||||||
|
serviceofferingid: values.serviceofferingid,
|
||||||
|
diskofferingid: values.diskofferingid,
|
||||||
|
networkid: values.networkid,
|
||||||
|
size: values.size,
|
||||||
|
filesystem: values.filesystem,
|
||||||
|
miniops: values.miniops,
|
||||||
|
maxiops: values.maxiops,
|
||||||
|
domainid: this.owner.domainid
|
||||||
|
}
|
||||||
|
if (this.owner.projectid) {
|
||||||
|
data.projectid = this.owner.projectid
|
||||||
|
} else {
|
||||||
|
data.account = this.owner.account
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
api('createSharedFileSystem', data).then(response => {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId: response.createsharedfilesystemresponse.jobid,
|
||||||
|
title: this.$t('label.create.sharedfs'),
|
||||||
|
description: values.name,
|
||||||
|
successMessage: this.$t('message.success.create.sharedfs'),
|
||||||
|
errorMessage: this.$t('message.create.sharedfs.failed'),
|
||||||
|
loadingMessage: this.$t('message.create.sharedfs.processing'),
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result')
|
||||||
|
})
|
||||||
|
this.closeModal()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form {
|
||||||
|
width: 80vw;
|
||||||
|
|
||||||
|
@media (min-width: 500px) {
|
||||||
|
min-width: 400px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
202
ui/src/views/storage/SharedFSTab.vue
Normal file
202
ui/src/views/storage/SharedFSTab.vue
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-tabs
|
||||||
|
:activeKey="currentTab"
|
||||||
|
:tabPosition="device === 'mobile' ? 'top' : 'left'"
|
||||||
|
:animated="false"
|
||||||
|
@change="handleChangeTab">
|
||||||
|
<a-tab-pane :tab="$t('label.details')" key="details">
|
||||||
|
<DetailsTab :resource="dataResource" :loading="loading" />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :tab="$t('label.access')" key="access">
|
||||||
|
<h3>{{ $t('label.mount.sharedfs') }}</h3>
|
||||||
|
<div v-for="(nic, index) in resource.nic" :key="index" class="content">
|
||||||
|
<a-card>
|
||||||
|
<template #title>
|
||||||
|
<router-link :to="{ path: '/guestnetwork/' + nic.networkid }">
|
||||||
|
{{ nic.networkname }}
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<timeline>
|
||||||
|
<a-timeline-item color="blue">
|
||||||
|
<h3>mount -t nfs {{ nic.ipaddress }}:{{ resource.path }} [local_mount_path]</h3>
|
||||||
|
<p class="info"><i>(Mount the NFS share. Additional mount options can be given as required.)</i></p>
|
||||||
|
</a-timeline-item>
|
||||||
|
<a-timeline-item color="blue">
|
||||||
|
<h3>showmount -e {{ nic.ipaddress }}</h3>
|
||||||
|
<p class="info"><i>(Check the status of the NFS server using the showmount command.)</i></p>
|
||||||
|
</a-timeline-item>
|
||||||
|
</timeline>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :tab="$t('label.networks')" key="nics" v-if="'listNics' in $store.getters.apis">
|
||||||
|
<NicsTab :resource="vm"/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane v-if="$store.getters.features.instancesdisksstatsretentionenabled" :tab="$t('label.volume.metrics')" key="volumestats">
|
||||||
|
<StatsTab :resource="volume" :resourceType="'Volume'"/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :tab="$t('label.metrics')" key="vmstats">
|
||||||
|
<StatsTab :resource="vm"/>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :tab="$t('label.events')" key="events" v-if="'listEvents' in $store.getters.apis">
|
||||||
|
<events-tab :resource="resource" resourceType="SharedFS" :loading="loading" />
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { api } from '@/api'
|
||||||
|
import { mixinDevice } from '@/utils/mixin.js'
|
||||||
|
import Status from '@/components/widgets/Status'
|
||||||
|
import DetailsTab from '@/components/view/DetailsTab'
|
||||||
|
import StatsTab from '@/components/view/StatsTab'
|
||||||
|
import EventsTab from '@/components/view/EventsTab'
|
||||||
|
import NicsTab from '@/views/network/NicsTab.vue'
|
||||||
|
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SharedFSTab',
|
||||||
|
components: {
|
||||||
|
DetailsTab,
|
||||||
|
StatsTab,
|
||||||
|
EventsTab,
|
||||||
|
NicsTab,
|
||||||
|
TooltipButton,
|
||||||
|
Status
|
||||||
|
},
|
||||||
|
mixins: [mixinDevice],
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
vm: {},
|
||||||
|
virtualmachines: [],
|
||||||
|
currentTab: 'details',
|
||||||
|
dataResource: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
const self = this
|
||||||
|
this.dataResource = this.resource
|
||||||
|
this.fetchData()
|
||||||
|
window.addEventListener('popstate', function () {
|
||||||
|
self.setCurrentTab()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resource: {
|
||||||
|
deep: true,
|
||||||
|
handler (newData, oldData) {
|
||||||
|
if (newData !== oldData) {
|
||||||
|
this.dataResource = newData
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'$route.fullPath': function () {
|
||||||
|
this.setCurrentTab()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.setCurrentTab()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setCurrentTab () {
|
||||||
|
this.currentTab = this.$route.query.tab ? this.$route.query.tab : 'details'
|
||||||
|
},
|
||||||
|
handleChangeTab (e) {
|
||||||
|
this.currentTab = e
|
||||||
|
const query = Object.assign({}, this.$route.query)
|
||||||
|
query.tab = e
|
||||||
|
history.pushState(
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
'#' + this.$route.path + '?' + Object.keys(query).map(key => {
|
||||||
|
return (
|
||||||
|
encodeURIComponent(key) + '=' + encodeURIComponent(query[key])
|
||||||
|
)
|
||||||
|
}).join('&')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
fetchInstances () {
|
||||||
|
if (!this.resource.virtualmachineid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.instanceLoading = true
|
||||||
|
var params = {
|
||||||
|
id: this.resource.virtualmachineid,
|
||||||
|
listall: true
|
||||||
|
}
|
||||||
|
if (this.$store.getters.listAllProjects) {
|
||||||
|
params.projectid = '-1'
|
||||||
|
}
|
||||||
|
api('listVirtualMachines', params).then(json => {
|
||||||
|
this.virtualmachines = json.listvirtualmachinesresponse.virtualmachine || []
|
||||||
|
this.vm = this.virtualmachines[0]
|
||||||
|
})
|
||||||
|
this.instanceLoading = false
|
||||||
|
},
|
||||||
|
fetchVolumes () {
|
||||||
|
if (!this.resource.volumeid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.volumeLoading = true
|
||||||
|
var params = {
|
||||||
|
id: this.resource.volumeid,
|
||||||
|
listsystemvms: 'true',
|
||||||
|
listall: true
|
||||||
|
}
|
||||||
|
api('listVolumes', params).then(json => {
|
||||||
|
this.volumes = json.listvolumesresponse.volume || []
|
||||||
|
this.volume = this.volumes[0]
|
||||||
|
})
|
||||||
|
this.volumeLoading = false
|
||||||
|
},
|
||||||
|
fetchData () {
|
||||||
|
this.fetchInstances()
|
||||||
|
this.fetchVolumes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-header-wrapper-grid-content-main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
147
ui/src/views/storage/UpdateSharedFS.vue
Normal file
147
ui/src/views/storage/UpdateSharedFS.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||||
|
<a-form
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
@finish="handleSubmit"
|
||||||
|
>
|
||||||
|
<a-form-item name="name" ref="name" :label="$t('label.name')">
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.name"
|
||||||
|
:placeholder="$t('label.name')"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="description" ref="description" :label="$t('label.description')">
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.description"
|
||||||
|
:placeholder="$t('label.description')"/>
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button :loading="loading" type="primary" ref="submit" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import { mixinForm } from '@/utils/mixin'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'updateSharedFS',
|
||||||
|
mixins: [mixinForm],
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
TooltipLabel
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('updateSharedFileSystem')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({})
|
||||||
|
this.rules = reactive({})
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
fetchData () {
|
||||||
|
this.loading = false
|
||||||
|
this.fillEditFormFieldValues()
|
||||||
|
},
|
||||||
|
fillEditFormFieldValues () {
|
||||||
|
const form = this.form
|
||||||
|
this.loading = true
|
||||||
|
Object.keys(this.apiParams).forEach(item => {
|
||||||
|
const field = this.apiParams[item]
|
||||||
|
let fieldValue = null
|
||||||
|
let fieldName = null
|
||||||
|
|
||||||
|
fieldName = field.name
|
||||||
|
fieldValue = this.resource[fieldName] ? this.resource[fieldName] : null
|
||||||
|
if (fieldValue) {
|
||||||
|
form[field.name] = fieldValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
if (this.loading) return
|
||||||
|
this.formRef.value.validate().then(() => {
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
id: this.resource.id,
|
||||||
|
name: values.name,
|
||||||
|
description: values.description
|
||||||
|
}
|
||||||
|
console.log(data)
|
||||||
|
console.log(this.form)
|
||||||
|
this.loading = true
|
||||||
|
api('updateSharedFileSystem', data).then(response => {
|
||||||
|
this.$emit('refresh-data')
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('label.update.sharedfs'),
|
||||||
|
description: `${this.$t('message.success.update.sharedfs')} ${data.name}`
|
||||||
|
})
|
||||||
|
this.closeModal()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-layout {
|
||||||
|
width: 85vw;
|
||||||
|
|
||||||
|
@media (min-width: 760px) {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -33,10 +33,12 @@ import java.nio.file.Paths;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
@ -154,4 +156,8 @@ public class FileUtil {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String readResourceFile(String resource) throws IOException {
|
||||||
|
return IOUtils.toString(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResourceAsStream(resource)), com.cloud.utils.StringUtils.getPreferredCharset());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user