mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
ui,api,server: template categorization based on os (#10773)
Adds new interface for image selection (template/iso) for an instance in UI. Old interface can still be used and it can be configured using UI configuration (config.json) OS categories/Guest OS categories have been improved with ability to create new categories, delete an existing category, and marking a category as featured to allow it to show up in the UI in the image selection interface. New APIs added: - addOsCategory - deleteOsCategory - updateOsCategory APIs updated: - updateOsType - listTemplates - listOsCategories Several improvements in UI especially related to forms - DeloyVM, ReinstallVM, CreateVnfAppliance, AddAutoscaleGroup. DeployVM form can now be opened from template/ISO details view with query params. Reorganized (removed and added some) OS categories to the following (in the same order): ``` 1. Ubuntu 2. Debian 3. Fedora 4. CentOS 5. Rocky Linux 6. Alma Linux 7. Oracle 8. RedHat 9. SUSE 10. Windows 11. Other ``` Documentation PR: https://github.com/apache/cloudstack-documentation/pull/500 Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
41b4f0afd5
commit
bce17b627d
@ -688,6 +688,9 @@ public class EventTypes {
|
||||
public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE";
|
||||
|
||||
//Guest OS related events
|
||||
public static final String EVENT_GUEST_OS_CATEGORY_ADD = "GUEST.OS.CATEGORY.ADD";
|
||||
public static final String EVENT_GUEST_OS_CATEGORY_DELETE = "GUEST.OS.CATEGORY.DELETE";
|
||||
public static final String EVENT_GUEST_OS_CATEGORY_UPDATE = "GUEST.OS.CATEGORY.UPDATE";
|
||||
public static final String EVENT_GUEST_OS_ADD = "GUEST.OS.ADD";
|
||||
public static final String EVENT_GUEST_OS_REMOVE = "GUEST.OS.REMOVE";
|
||||
public static final String EVENT_GUEST_OS_UPDATE = "GUEST.OS.UPDATE";
|
||||
|
||||
@ -25,12 +25,15 @@ import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
|
||||
import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd;
|
||||
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
|
||||
import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
|
||||
@ -168,6 +171,12 @@ public interface ManagementService {
|
||||
*/
|
||||
Pair<List<? extends GuestOsCategory>, Integer> listGuestOSCategoriesByCriteria(ListGuestOsCategoriesCmd cmd);
|
||||
|
||||
GuestOsCategory addGuestOsCategory(AddGuestOsCategoryCmd cmd);
|
||||
|
||||
GuestOsCategory updateGuestOsCategory(UpdateGuestOsCategoryCmd cmd);
|
||||
|
||||
boolean deleteGuestOsCategory(DeleteGuestOsCategoryCmd cmd);
|
||||
|
||||
/**
|
||||
* Obtains a list of all guest OS mappings
|
||||
*
|
||||
|
||||
@ -16,7 +16,9 @@
|
||||
// under the License.
|
||||
package com.cloud.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ResourceIconManager {
|
||||
|
||||
@ -25,4 +27,8 @@ public interface ResourceIconManager {
|
||||
boolean deleteResourceIcon(List<String> resourceIds, ResourceTag.ResourceObjectType resourceType);
|
||||
|
||||
ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId);
|
||||
|
||||
Map<Long, ResourceIcon> getByResourceTypeAndIds(ResourceTag.ResourceObjectType type, Collection<Long> resourceIds);
|
||||
|
||||
Map<String, ResourceIcon> getByResourceTypeAndUuids(ResourceTag.ResourceObjectType type, Collection<String> resourceUuids);
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit
|
||||
LBStickinessPolicy(false, true),
|
||||
LBHealthCheckPolicy(false, true),
|
||||
SnapshotPolicy(true, true),
|
||||
GuestOsCategory(false, false, true),
|
||||
GuestOs(false, true),
|
||||
NetworkOffering(false, true),
|
||||
VpcOffering(true, false),
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.storage;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
@ -27,4 +29,7 @@ public interface GuestOsCategory extends Identity, InternalIdentity {
|
||||
|
||||
void setName(String name);
|
||||
|
||||
boolean isFeatured();
|
||||
|
||||
Date getCreated();
|
||||
}
|
||||
|
||||
@ -61,6 +61,7 @@ public enum ApiCommandResourceType {
|
||||
AffinityGroup(org.apache.cloudstack.affinity.AffinityGroup.class),
|
||||
InternalLbVm(com.cloud.network.router.VirtualRouter.class),
|
||||
DedicatedGuestVlanRange(com.cloud.network.GuestVlan.class),
|
||||
GuestOsCategory(com.cloud.storage.GuestOsCategory.class),
|
||||
GuestOs(com.cloud.storage.GuestOS.class),
|
||||
GuestOsMapping(com.cloud.storage.GuestOSHypervisor.class),
|
||||
Network(com.cloud.network.Network.class),
|
||||
|
||||
@ -299,6 +299,7 @@ public class ApiConstants {
|
||||
public static final String IS_EXTRACTABLE = "isextractable";
|
||||
public static final String IS_FEATURED = "isfeatured";
|
||||
public static final String IS_IMPLICIT = "isimplicit";
|
||||
public static final String IS_ISO = "isiso";
|
||||
public static final String IS_PORTABLE = "isportable";
|
||||
public static final String IS_PUBLIC = "ispublic";
|
||||
public static final String IS_PERSISTENT = "ispersistent";
|
||||
|
||||
@ -22,20 +22,16 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.cloud.bgp.ASNumber;
|
||||
import com.cloud.bgp.ASNumberRange;
|
||||
|
||||
import org.apache.cloudstack.storage.object.Bucket;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.api.ApiConstants.HostDetails;
|
||||
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse;
|
||||
import org.apache.cloudstack.api.response.ASNRangeResponse;
|
||||
import org.apache.cloudstack.api.response.ASNumberResponse;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse;
|
||||
import org.apache.cloudstack.api.response.AsyncJobResponse;
|
||||
import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
|
||||
import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
|
||||
@ -60,10 +56,10 @@ import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||
import org.apache.cloudstack.api.response.EventResponse;
|
||||
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.FirewallRuleResponse;
|
||||
import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOSResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOsMappingResponse;
|
||||
import org.apache.cloudstack.api.response.GuestVlanRangeResponse;
|
||||
@ -73,11 +69,11 @@ import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse;
|
||||
import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse;
|
||||
import org.apache.cloudstack.api.response.IPAddressResponse;
|
||||
import org.apache.cloudstack.api.response.IpQuarantineResponse;
|
||||
import org.apache.cloudstack.api.response.ImageStoreResponse;
|
||||
import org.apache.cloudstack.api.response.InstanceGroupResponse;
|
||||
import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse;
|
||||
import org.apache.cloudstack.api.response.IpForwardingRuleResponse;
|
||||
import org.apache.cloudstack.api.response.IpQuarantineResponse;
|
||||
import org.apache.cloudstack.api.response.IsolationMethodResponse;
|
||||
import org.apache.cloudstack.api.response.LBHealthCheckResponse;
|
||||
import org.apache.cloudstack.api.response.LBStickinessResponse;
|
||||
@ -115,6 +111,7 @@ import org.apache.cloudstack.api.response.SecondaryStorageHeuristicsResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceResponse;
|
||||
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||
import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse;
|
||||
import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse;
|
||||
import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse;
|
||||
@ -159,10 +156,14 @@ import org.apache.cloudstack.region.PortableIp;
|
||||
import org.apache.cloudstack.region.PortableIpRange;
|
||||
import org.apache.cloudstack.region.Region;
|
||||
import org.apache.cloudstack.secstorage.heuristics.Heuristic;
|
||||
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||
import org.apache.cloudstack.storage.object.Bucket;
|
||||
import org.apache.cloudstack.storage.object.ObjectStore;
|
||||
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||
import org.apache.cloudstack.usage.Usage;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
import com.cloud.bgp.ASNumber;
|
||||
import com.cloud.bgp.ASNumberRange;
|
||||
import com.cloud.capacity.Capacity;
|
||||
import com.cloud.configuration.ResourceCount;
|
||||
import com.cloud.configuration.ResourceLimit;
|
||||
@ -223,10 +224,11 @@ import com.cloud.projects.ProjectAccount;
|
||||
import com.cloud.projects.ProjectInvitation;
|
||||
import com.cloud.region.ha.GlobalLoadBalancerRule;
|
||||
import com.cloud.resource.RollingMaintenanceManager;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.storage.GuestOSHypervisor;
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.storage.ImageStore;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.StoragePool;
|
||||
@ -240,14 +242,13 @@ import com.cloud.user.User;
|
||||
import com.cloud.user.UserAccount;
|
||||
import com.cloud.user.UserData;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.net.Ip;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.net.Ip;
|
||||
import com.cloud.vm.InstanceGroup;
|
||||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.NicSecondaryIp;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.snapshot.VMSnapshot;
|
||||
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
|
||||
public interface ResponseGenerator {
|
||||
UserResponse createUserResponse(UserAccount user);
|
||||
@ -485,6 +486,10 @@ public interface ResponseGenerator {
|
||||
|
||||
AutoScaleVmGroupResponse createAutoScaleVmGroupResponse(AutoScaleVmGroup vmGroup);
|
||||
|
||||
GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory);
|
||||
|
||||
GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory, boolean showIcon);
|
||||
|
||||
GuestOSResponse createGuestOSResponse(GuestOS os);
|
||||
|
||||
GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor);
|
||||
@ -572,4 +577,6 @@ public interface ResponseGenerator {
|
||||
BackupRepositoryResponse createBackupRepositoryResponse(BackupRepository repository);
|
||||
|
||||
SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS);
|
||||
|
||||
void updateTemplateIsoResponsesForIcons(List<TemplateResponse> responses, ResourceTag.ResourceObjectType type);
|
||||
}
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.api.command.admin.guest;
|
||||
|
||||
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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "addOsCategory",
|
||||
description = "Adds a new OS category",
|
||||
responseObject = GuestOSCategoryResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
since = "4.21.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class AddGuestOsCategoryCmd extends BaseCmd {
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the OS category",
|
||||
required = true)
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN,
|
||||
description = "Whether the category is featured or not")
|
||||
private Boolean featured;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isFeatured() {
|
||||
return Boolean.TRUE.equals(featured);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
GuestOsCategory guestOs = _mgr.addGuestOsCategory(this);
|
||||
if (guestOs != null) {
|
||||
GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add new OS category");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.GuestOsCategory;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.cloudstack.api.command.admin.guest;
|
||||
|
||||
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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
|
||||
|
||||
@APICommand(name = "deleteOsCategory",
|
||||
description = "Deletes an OS category",
|
||||
responseObject = SuccessResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
since = "4.21.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class DeleteGuestOsCategoryCmd extends BaseCmd {
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class,
|
||||
required = true, description = "ID of the OS category")
|
||||
private Long id;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
boolean result = _mgr.deleteGuestOsCategory(this);
|
||||
if (result) {
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove OS category");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.GuestOsCategory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
@ -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.admin.guest;
|
||||
|
||||
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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "updateOsCategory",
|
||||
description = "Updates an OS category",
|
||||
responseObject = GuestOSCategoryResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
since = "4.21.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class UpdateGuestOsCategoryCmd extends BaseCmd {
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class,
|
||||
required = true, description = "ID of the OS category")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name for the OS category")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN,
|
||||
description = "Whether the category is featured or not")
|
||||
private Boolean featured;
|
||||
|
||||
@Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER,
|
||||
description = "sort key of the OS category for listing")
|
||||
private Integer sortKey;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Boolean isFeatured() {
|
||||
return featured;
|
||||
}
|
||||
|
||||
public Integer getSortKey() {
|
||||
return sortKey;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
GuestOsCategory guestOs = _mgr.updateGuestOsCategory(this);
|
||||
if (guestOs != null) {
|
||||
GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update OS category");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.GuestOsCategory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
}
|
||||
@ -16,8 +16,12 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.guest;
|
||||
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -25,18 +29,14 @@ import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOSResponse;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@APICommand(name = "updateGuestOs", description = "Updates the information about Guest OS", responseObject = GuestOSResponse.class,
|
||||
since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
public class UpdateGuestOsCmd extends BaseAsyncCmd {
|
||||
@ -50,7 +50,7 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, description = "UUID of the Guest OS")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS")
|
||||
@Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, description = "Unique display name for Guest OS")
|
||||
private String osDisplayName;
|
||||
|
||||
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs)")
|
||||
@ -59,6 +59,12 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
|
||||
@Parameter(name="forDisplay", type=CommandType.BOOLEAN, description="whether this guest OS is available for end users", authorized = {RoleType.Admin})
|
||||
private Boolean display;
|
||||
|
||||
@Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class,
|
||||
description = "the ID of the OS category", since = "4.21.0")
|
||||
private Long osCategoryId;
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -71,9 +77,9 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
|
||||
return osDisplayName;
|
||||
}
|
||||
|
||||
public Map getDetails() {
|
||||
Map<String, String> detailsMap = new HashMap<>();;
|
||||
if (MapUtils.isNotEmpty(detailsMap)) {
|
||||
public Map<String, String> getDetails() {
|
||||
Map<String, String> detailsMap = new HashMap<>();
|
||||
if (MapUtils.isNotEmpty(details)) {
|
||||
Collection<?> servicesCollection = details.values();
|
||||
Iterator<?> iter = servicesCollection.iterator();
|
||||
while (iter.hasNext()) {
|
||||
@ -90,6 +96,10 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd {
|
||||
return display;
|
||||
}
|
||||
|
||||
public Long getOsCategoryId() {
|
||||
return osCategoryId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@ -26,7 +26,9 @@ import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.utils.Pair;
|
||||
|
||||
@ -39,12 +41,48 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd {
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "list Os category by id")
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "List OS category by id")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list os category by name", since = "3.0.1")
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List OS category by name", since = "3.0.1")
|
||||
private String name;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_FEATURED,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "List available OS categories by featured or not",
|
||||
since = "4.21.0")
|
||||
private Boolean featured;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_ISO,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "List OS categories for which an ISO is available",
|
||||
since = "4.21.0")
|
||||
private Boolean iso;
|
||||
|
||||
@Parameter(name = ApiConstants.IS_VNF, type = CommandType.BOOLEAN,
|
||||
description = "List OS categories for which a VNF template is available",
|
||||
since = "4.21.0")
|
||||
private Boolean vnf;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "List available OS categories types for the zone",
|
||||
since = "4.21.0")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.ARCH,
|
||||
type = CommandType.STRING,
|
||||
description = "List OS categories types available for given CPU architecture",
|
||||
since = "4.21.0")
|
||||
private String arch;
|
||||
|
||||
@Parameter(name = ApiConstants.SHOW_RESOURCE_ICON,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "flag to display the resource image for the OS category",
|
||||
since = "4.21.0")
|
||||
private Boolean showIcon;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -57,6 +95,30 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Boolean isFeatured() {
|
||||
return featured;
|
||||
}
|
||||
|
||||
public Boolean isIso() {
|
||||
return iso;
|
||||
}
|
||||
|
||||
public Boolean isVnf() {
|
||||
return vnf;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public CPU.CPUArch getArch() {
|
||||
return arch == null ? null : CPU.CPUArch.fromType(arch);
|
||||
}
|
||||
|
||||
public boolean isShowIcon() {
|
||||
return Boolean.TRUE.equals(showIcon);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -64,14 +126,11 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd {
|
||||
@Override
|
||||
public void execute() {
|
||||
Pair<List<? extends GuestOsCategory>, Integer> result = _mgr.listGuestOSCategoriesByCriteria(this);
|
||||
ListResponse<GuestOSCategoryResponse> response = new ListResponse<GuestOSCategoryResponse>();
|
||||
List<GuestOSCategoryResponse> osCatResponses = new ArrayList<GuestOSCategoryResponse>();
|
||||
ListResponse<GuestOSCategoryResponse> response = new ListResponse<>();
|
||||
List<GuestOSCategoryResponse> osCatResponses = new ArrayList<>();
|
||||
for (GuestOsCategory osCategory : result.first()) {
|
||||
GuestOSCategoryResponse categoryResponse = new GuestOSCategoryResponse();
|
||||
categoryResponse.setId(osCategory.getUuid());
|
||||
categoryResponse.setName(osCategory.getName());
|
||||
|
||||
categoryResponse.setObjectName("oscategory");
|
||||
GuestOSCategoryResponse categoryResponse = _responseGenerator.createGuestOSCategoryResponse(osCategory,
|
||||
isShowIcon());
|
||||
osCatResponses.add(categoryResponse);
|
||||
}
|
||||
|
||||
|
||||
@ -16,11 +16,6 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.iso;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -28,16 +23,17 @@ import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "listIsos", description = "Lists all available ISO files.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
@ -95,6 +91,11 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd {
|
||||
since = "4.20")
|
||||
private String arch;
|
||||
|
||||
@Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType= GuestOSCategoryResponse.class,
|
||||
description = "the ID of the OS category for the ISO",
|
||||
since = "4.21.0")
|
||||
private Long osCategoryId;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -173,6 +174,10 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd {
|
||||
return CPU.CPUArch.fromType(arch);
|
||||
}
|
||||
|
||||
public Long getOsCategoryId() {
|
||||
return osCategoryId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -190,24 +195,14 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd {
|
||||
@Override
|
||||
public void execute() {
|
||||
ListResponse<TemplateResponse> response = _queryService.listIsos(this);
|
||||
if (response != null && response.getCount() > 0 && getShowIcon()) {
|
||||
updateIsoResponse(response.getResponses());
|
||||
if (response != null && getShowIcon()) {
|
||||
_responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(),
|
||||
ResourceTag.ResourceObjectType.ISO);
|
||||
}
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
private void updateIsoResponse(List<TemplateResponse> response) {
|
||||
for (TemplateResponse templateResponse : response) {
|
||||
ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.ISO, templateResponse.getId());
|
||||
if (resourceIcon == null) {
|
||||
continue;
|
||||
}
|
||||
ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon);
|
||||
templateResponse.setResourceIconResponse(iconResponse);
|
||||
}
|
||||
}
|
||||
|
||||
public Long getStoragePoolId() {
|
||||
return null;
|
||||
};
|
||||
|
||||
@ -16,17 +16,11 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.template;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -34,15 +28,20 @@ import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@APICommand(name = "listTemplates", description = "List all public, private, and privileged templates.", responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
@ -111,6 +110,11 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User
|
||||
since = "4.20")
|
||||
private String arch;
|
||||
|
||||
@Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType= GuestOSCategoryResponse.class,
|
||||
description = "the ID of the OS category for the template",
|
||||
since = "4.21.0")
|
||||
private Long osCategoryId;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -205,6 +209,10 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User
|
||||
return CPU.CPUArch.fromType(arch);
|
||||
}
|
||||
|
||||
public Long getOsCategoryId() {
|
||||
return osCategoryId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return s_name;
|
||||
@ -218,24 +226,14 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User
|
||||
@Override
|
||||
public void execute() {
|
||||
ListResponse<TemplateResponse> response = _queryService.listTemplates(this);
|
||||
if (response != null && response.getCount() > 0 && getShowIcon()) {
|
||||
updateTemplateResponse(response.getResponses());
|
||||
if (response != null && getShowIcon()) {
|
||||
_responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(),
|
||||
ResourceTag.ResourceObjectType.Template);
|
||||
}
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
private void updateTemplateResponse(List<TemplateResponse> response) {
|
||||
for (TemplateResponse templateResponse : response) {
|
||||
ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Template, templateResponse.getId());
|
||||
if (resourceIcon == null) {
|
||||
continue;
|
||||
}
|
||||
ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon);
|
||||
templateResponse.setResourceIconResponse(iconResponse);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Long> getIds() {
|
||||
if (ids == null) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@ -16,10 +16,15 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.vm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
@ -54,6 +59,7 @@ import com.cloud.cpu.CPU;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
||||
|
||||
@ -331,22 +337,75 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
protected void updateVMResponse(List<UserVmResponse> response) {
|
||||
for (UserVmResponse vmResponse : response) {
|
||||
ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.UserVm, vmResponse.getId());
|
||||
if (resourceIcon == null) {
|
||||
ResourceTag.ResourceObjectType type = ResourceTag.ResourceObjectType.Template;
|
||||
String uuid = vmResponse.getTemplateId();
|
||||
if (vmResponse.getIsoId() != null) {
|
||||
uuid = vmResponse.getIsoId();
|
||||
type = ResourceTag.ResourceObjectType.ISO;
|
||||
protected Map<String, ResourceIcon> getResourceIconsUsingOsCategory(List<UserVmResponse> responses) {
|
||||
Set<String> guestOsIds = responses.stream().map(UserVmResponse::getGuestOsId).collect(Collectors.toSet());
|
||||
List<GuestOS> guestOSList = _entityMgr.listByUuids(GuestOS.class, guestOsIds);
|
||||
Map<String, GuestOS> guestOSMap = guestOSList.stream()
|
||||
.collect(Collectors.toMap(GuestOS::getUuid, Function.identity()));
|
||||
Set<Long> guestOsCategoryIds = guestOSMap.values().stream()
|
||||
.map(GuestOS::getCategoryId)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, ResourceIcon> guestOsCategoryIcons =
|
||||
resourceIconManager.getByResourceTypeAndIds(ResourceTag.ResourceObjectType.GuestOsCategory,
|
||||
guestOsCategoryIds);
|
||||
Map<String, ResourceIcon> vmIcons = new HashMap<>();
|
||||
for (UserVmResponse response : responses) {
|
||||
GuestOS guestOS = guestOSMap.get(response.getGuestOsId());
|
||||
if (guestOS != null) {
|
||||
vmIcons.put(response.getId(), guestOsCategoryIcons.get(guestOS.getCategoryId()));
|
||||
}
|
||||
resourceIcon = resourceIconManager.getByResourceTypeAndUuid(type, uuid);
|
||||
if (resourceIcon == null) {
|
||||
}
|
||||
return vmIcons;
|
||||
}
|
||||
|
||||
protected Map<String, ResourceIcon> getResourceIconsForUsingTemplateIso(List<UserVmResponse> responses) {
|
||||
Map<String, String> vmTemplateIsoIdMap = new HashMap<>();
|
||||
Set<String> templateUuids = new HashSet<>();
|
||||
Set<String> isoUuids = new HashSet<>();
|
||||
for (UserVmResponse vmResponse : responses) {
|
||||
if (vmResponse.getTemplateId() != null) {
|
||||
templateUuids.add(vmResponse.getTemplateId());
|
||||
vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getTemplateId());
|
||||
}
|
||||
if (vmResponse.getIsoId() != null) {
|
||||
isoUuids.add(vmResponse.getIsoId());
|
||||
vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getIsoId());
|
||||
}
|
||||
}
|
||||
Map<String, ResourceIcon> templateOrIsoIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, templateUuids);
|
||||
templateOrIsoIcons.putAll(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, isoUuids));
|
||||
Map<String, ResourceIcon> vmIcons = new HashMap<>();
|
||||
List<UserVmResponse> noTemplateIsoIconResponses = new ArrayList<>();
|
||||
for (UserVmResponse response : responses) {
|
||||
String uuid = vmTemplateIsoIdMap.get(response.getId());
|
||||
if (StringUtils.isNotBlank(uuid) && templateOrIsoIcons.containsKey(uuid)) {
|
||||
vmIcons.put(response.getId(),
|
||||
templateOrIsoIcons.get(vmTemplateIsoIdMap.get(response.getId())));
|
||||
continue;
|
||||
}
|
||||
noTemplateIsoIconResponses.add(response);
|
||||
}
|
||||
ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon);
|
||||
vmIcons.putAll(getResourceIconsUsingOsCategory(noTemplateIsoIconResponses));
|
||||
return vmIcons;
|
||||
}
|
||||
|
||||
protected void updateVMResponse(List<UserVmResponse> responses) {
|
||||
if (CollectionUtils.isEmpty(responses)) {
|
||||
return;
|
||||
}
|
||||
Set<String> vmUuids = responses.stream().map(UserVmResponse::getId).collect(Collectors.toSet());
|
||||
Map<String, ResourceIcon> vmIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, vmUuids);
|
||||
List<UserVmResponse> noVmIconResponses = responses
|
||||
.stream()
|
||||
.filter(r -> !vmIcons.containsKey(r.getId()))
|
||||
.collect(Collectors.toList());
|
||||
vmIcons.putAll(getResourceIconsForUsingTemplateIso(noVmIconResponses));
|
||||
for (UserVmResponse vmResponse : responses) {
|
||||
ResourceIcon icon = vmIcons.get(vmResponse.getId());
|
||||
if (icon == null) {
|
||||
continue;
|
||||
}
|
||||
ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(icon);
|
||||
vmResponse.setResourceIconResponse(iconResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -26,7 +28,7 @@ import com.cloud.serializer.Param;
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
|
||||
@EntityReference(value = GuestOsCategory.class)
|
||||
public class GuestOSCategoryResponse extends BaseResponse {
|
||||
public class GuestOSCategoryResponse extends BaseResponse implements SetResourceIconResponse {
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "the ID of the OS category")
|
||||
private String id;
|
||||
@ -35,6 +37,18 @@ public class GuestOSCategoryResponse extends BaseResponse {
|
||||
@Param(description = "the name of the OS category")
|
||||
private String name;
|
||||
|
||||
@SerializedName(ApiConstants.IS_FEATURED)
|
||||
@Param(description = "Whether the OS category is featured", since = "4.21.0")
|
||||
private Boolean featured;
|
||||
|
||||
@SerializedName(ApiConstants.RESOURCE_ICON)
|
||||
@Param(description = "Base64 string representation of the resource icon", since = "4.21.0")
|
||||
private ResourceIconResponse resourceIconResponse;
|
||||
|
||||
@SerializedName(ApiConstants.CREATED)
|
||||
@Param(description = "Date when the OS category was created." )
|
||||
private Date created;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@ -50,4 +64,17 @@ public class GuestOSCategoryResponse extends BaseResponse {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setFeatured(Boolean featured) {
|
||||
this.featured = featured;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) {
|
||||
this.resourceIconResponse = resourceIconResponse;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,6 +93,8 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements
|
||||
@Param(description = "the name of the OS type for this template.")
|
||||
private String osTypeName;
|
||||
|
||||
private transient Long osTypeCategoryId;
|
||||
|
||||
@SerializedName(ApiConstants.ACCOUNT_ID)
|
||||
@Param(description = "the account id to which the template belongs")
|
||||
private String accountId;
|
||||
@ -285,6 +287,14 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements
|
||||
this.osTypeName = osTypeName;
|
||||
}
|
||||
|
||||
public Long getOsTypeCategoryId() {
|
||||
return osTypeCategoryId;
|
||||
}
|
||||
|
||||
public void setOsTypeCategoryId(Long osTypeCategoryId) {
|
||||
this.osTypeCategoryId = osTypeCategoryId;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.guest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AddGuestOsCategoryCmdTest {
|
||||
|
||||
@Test
|
||||
public void testGetArch() {
|
||||
AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd();
|
||||
Assert.assertNull(cmd.getName());
|
||||
String name = "Name";
|
||||
ReflectionTestUtils.setField(cmd, "name", name);
|
||||
Assert.assertEquals(name, cmd.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFeatured() {
|
||||
AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd();
|
||||
Assert.assertFalse(cmd.isFeatured());
|
||||
ReflectionTestUtils.setField(cmd, "featured", false);
|
||||
Assert.assertFalse(cmd.isFeatured());
|
||||
ReflectionTestUtils.setField(cmd, "featured", true);
|
||||
Assert.assertTrue(cmd.isFeatured());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.guest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class UpdateGuestOsCategoryCmdTest {
|
||||
|
||||
@Test
|
||||
public void testGetArch() {
|
||||
UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd();
|
||||
Assert.assertNull(cmd.getName());
|
||||
String name = "Name";
|
||||
ReflectionTestUtils.setField(cmd, "name", name);
|
||||
Assert.assertEquals(name, cmd.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFeatured() {
|
||||
UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd();
|
||||
Assert.assertNull(cmd.isFeatured());
|
||||
ReflectionTestUtils.setField(cmd, "featured", false);
|
||||
Assert.assertFalse(cmd.isFeatured());
|
||||
ReflectionTestUtils.setField(cmd, "featured", true);
|
||||
Assert.assertTrue(cmd.isFeatured());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSortKey() {
|
||||
UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd();
|
||||
Assert.assertNull(cmd.getSortKey());
|
||||
Integer sortKey = 100;
|
||||
ReflectionTestUtils.setField(cmd, "sortKey", sortKey);
|
||||
Assert.assertEquals(sortKey, cmd.getSortKey());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.admin.guest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class UpdateGuestOsCmdTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetZoneId() {
|
||||
UpdateGuestOsCmd cmd = new UpdateGuestOsCmd();
|
||||
Assert.assertNull(cmd.getOsCategoryId());
|
||||
Long osCategoryId = 100L;
|
||||
ReflectionTestUtils.setField(cmd, "osCategoryId", osCategoryId);
|
||||
Assert.assertEquals(osCategoryId, cmd.getOsCategoryId());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
// 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.guest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ListGuestOsCategoriesCmdTest {
|
||||
|
||||
@Test
|
||||
public void testIsFeatured() {
|
||||
ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd();
|
||||
Assert.assertNull(cmd.isFeatured());
|
||||
ReflectionTestUtils.setField(cmd, "featured", false);
|
||||
Assert.assertFalse(cmd.isFeatured());
|
||||
ReflectionTestUtils.setField(cmd, "featured", true);
|
||||
Assert.assertTrue(cmd.isFeatured());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsIso() {
|
||||
ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd();
|
||||
Assert.assertNull(cmd.isIso());
|
||||
ReflectionTestUtils.setField(cmd, "iso", false);
|
||||
Assert.assertFalse(cmd.isIso());
|
||||
ReflectionTestUtils.setField(cmd, "iso", true);
|
||||
Assert.assertTrue(cmd.isIso());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsVnf() {
|
||||
ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd();
|
||||
Assert.assertNull(cmd.isVnf());
|
||||
ReflectionTestUtils.setField(cmd, "vnf", false);
|
||||
Assert.assertFalse(cmd.isVnf());
|
||||
ReflectionTestUtils.setField(cmd, "vnf", true);
|
||||
Assert.assertTrue(cmd.isVnf());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetZoneId() {
|
||||
ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd();
|
||||
Assert.assertNull(cmd.getZoneId());
|
||||
Long zoneId = 100L;
|
||||
ReflectionTestUtils.setField(cmd, "zoneId", zoneId);
|
||||
Assert.assertEquals(zoneId, cmd.getZoneId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetArch() {
|
||||
ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd();
|
||||
Assert.assertNull(cmd.getArch());
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
ReflectionTestUtils.setField(cmd, "arch", arch.getType());
|
||||
Assert.assertEquals(arch, cmd.getArch());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsShowIcon() {
|
||||
ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd();
|
||||
Assert.assertFalse(cmd.isShowIcon());
|
||||
ReflectionTestUtils.setField(cmd, "showIcon", false);
|
||||
Assert.assertFalse(cmd.isShowIcon());
|
||||
ReflectionTestUtils.setField(cmd, "showIcon", true);
|
||||
Assert.assertTrue(cmd.isShowIcon());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,223 @@
|
||||
// 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.vm;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.anyList;
|
||||
import static org.mockito.Mockito.anySet;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.cloudstack.api.ResponseGenerator;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceIconManager;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
|
||||
public class ListVMsCmdTest {
|
||||
|
||||
EntityManager _entityMgr;
|
||||
ResourceIconManager resourceIconManager;
|
||||
ResponseGenerator _responseGenerator;
|
||||
|
||||
ListVMsCmd cmd;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
_entityMgr = mock(EntityManager.class);
|
||||
resourceIconManager = mock(ResourceIconManager.class);
|
||||
_responseGenerator = mock(ResponseGenerator.class);
|
||||
cmd = spy(ListVMsCmd.class);
|
||||
cmd._entityMgr = _entityMgr;
|
||||
cmd.resourceIconManager = resourceIconManager;
|
||||
cmd._responseGenerator = _responseGenerator;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateVMResponse_withMixedIcons() {
|
||||
String vm1Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm1 = mock(UserVmResponse.class);
|
||||
when(vm1.getId()).thenReturn(vm1Uuid);
|
||||
String vm2Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm2 = mock(UserVmResponse.class);
|
||||
when(vm2.getId()).thenReturn(vm2Uuid);
|
||||
List<UserVmResponse> responses = Arrays.asList(vm1, vm2);
|
||||
ResourceIcon icon1 = mock(ResourceIcon.class);
|
||||
ResourceIcon icon2 = mock(ResourceIcon.class);
|
||||
Map<String, ResourceIcon> initialIcons = new HashMap<>();
|
||||
initialIcons.put(vm1Uuid, icon1);
|
||||
when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, Set.of(vm1Uuid, vm2Uuid)))
|
||||
.thenReturn(initialIcons);
|
||||
Map<String, ResourceIcon> fallbackIcons = Map.of(vm2Uuid, icon2);
|
||||
doReturn(fallbackIcons).when(cmd).getResourceIconsForUsingTemplateIso(anyList());
|
||||
ResourceIconResponse iconResponse1 = new ResourceIconResponse();
|
||||
ResourceIconResponse iconResponse2 = new ResourceIconResponse();
|
||||
when(_responseGenerator.createResourceIconResponse(icon1)).thenReturn(iconResponse1);
|
||||
when(_responseGenerator.createResourceIconResponse(icon2)).thenReturn(iconResponse2);
|
||||
cmd.updateVMResponse(responses);
|
||||
verify(vm1).setResourceIconResponse(iconResponse1);
|
||||
verify(vm2).setResourceIconResponse(iconResponse2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateVMResponse_withEmptyList() {
|
||||
cmd.updateVMResponse(Collections.emptyList());
|
||||
verify(resourceIconManager, never()).getByResourceTypeAndIds(Mockito.any(), Mockito.anyCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsForUsingTemplateIso_withValidData() {
|
||||
String vm1Uuid = UUID.randomUUID().toString();
|
||||
String template1Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm1 = mock(UserVmResponse.class);
|
||||
when(vm1.getId()).thenReturn(vm1Uuid);
|
||||
when(vm1.getTemplateId()).thenReturn(template1Uuid);
|
||||
when(vm1.getIsoId()).thenReturn(null);
|
||||
String vm2Uuid = UUID.randomUUID().toString();
|
||||
String iso2Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm2 = mock(UserVmResponse.class);
|
||||
when(vm2.getId()).thenReturn(vm2Uuid);
|
||||
when(vm2.getTemplateId()).thenReturn(null);
|
||||
when(vm2.getIsoId()).thenReturn(iso2Uuid);
|
||||
List<UserVmResponse> responses = Arrays.asList(vm1, vm2);
|
||||
Map<String, ResourceIcon> templateIcons = new HashMap<>();
|
||||
templateIcons.put(template1Uuid, mock(ResourceIcon.class));
|
||||
Map<String, ResourceIcon> isoIcons = new HashMap<>();
|
||||
isoIcons.put(iso2Uuid, mock(ResourceIcon.class));
|
||||
when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, Set.of(template1Uuid)))
|
||||
.thenReturn(templateIcons);
|
||||
when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, Set.of(iso2Uuid)))
|
||||
.thenReturn(isoIcons);
|
||||
doReturn(Collections.emptyMap()).when(cmd).getResourceIconsUsingOsCategory(anyList());
|
||||
Map<String, ResourceIcon> result = cmd.getResourceIconsForUsingTemplateIso(responses);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.containsKey(vm1Uuid));
|
||||
assertTrue(result.containsKey(vm2Uuid));
|
||||
assertEquals(templateIcons.get(template1Uuid), result.get(vm1Uuid));
|
||||
assertEquals(isoIcons.get(iso2Uuid), result.get(vm2Uuid));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsForUsingTemplateIso_withMissingIcons() {
|
||||
String vm1Uuid = UUID.randomUUID().toString();
|
||||
String template1Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm1 = mock(UserVmResponse.class);
|
||||
when(vm1.getId()).thenReturn(vm1Uuid);
|
||||
when(vm1.getTemplateId()).thenReturn(template1Uuid);
|
||||
when(vm1.getIsoId()).thenReturn(null);
|
||||
List<UserVmResponse> responses = List.of(vm1);
|
||||
when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.Template), anySet()))
|
||||
.thenReturn(Collections.emptyMap());
|
||||
when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.ISO), anySet()))
|
||||
.thenReturn(Collections.emptyMap());
|
||||
Map<String, ResourceIcon> fallbackIcons = Map.of(vm1Uuid, mock(ResourceIcon.class));
|
||||
doReturn(fallbackIcons).when(cmd).getResourceIconsUsingOsCategory(anyList());
|
||||
Map<String, ResourceIcon> result = cmd.getResourceIconsForUsingTemplateIso(responses);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(fallbackIcons.get("vm1"), result.get("vm1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsUsingOsCategory_withValidData() {
|
||||
String vm1Uuid = UUID.randomUUID().toString();
|
||||
String os1Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm1 = mock(UserVmResponse.class);
|
||||
when(vm1.getGuestOsId()).thenReturn(os1Uuid);
|
||||
when(vm1.getId()).thenReturn(vm1Uuid);
|
||||
String vm2Uuid = UUID.randomUUID().toString();
|
||||
String os2Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm2 = mock(UserVmResponse.class);
|
||||
when(vm2.getGuestOsId()).thenReturn(os2Uuid);
|
||||
when(vm2.getId()).thenReturn(vm2Uuid);
|
||||
List<UserVmResponse> responses = Arrays.asList(vm1, vm2);
|
||||
GuestOS guestOS1 = mock(GuestOS.class);
|
||||
when(guestOS1.getUuid()).thenReturn(os1Uuid);
|
||||
when(guestOS1.getCategoryId()).thenReturn(10L);
|
||||
GuestOS guestOS2 = mock(GuestOS.class);
|
||||
when(guestOS2.getUuid()).thenReturn(os2Uuid);
|
||||
when(guestOS2.getCategoryId()).thenReturn(20L);
|
||||
when(_entityMgr.listByUuids(eq(GuestOS.class), anySet()))
|
||||
.thenReturn(Arrays.asList(guestOS1, guestOS2));
|
||||
ResourceIcon icon1 = mock(ResourceIcon.class);
|
||||
ResourceIcon icon2 = mock(ResourceIcon.class);
|
||||
Map<Long, ResourceIcon> categoryIcons = new HashMap<>();
|
||||
categoryIcons.put(10L, icon1);
|
||||
categoryIcons.put(20L, icon2);
|
||||
when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet()))
|
||||
.thenReturn(categoryIcons);
|
||||
Map<String, ResourceIcon> result = cmd.getResourceIconsUsingOsCategory(responses);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(icon1, result.get(vm1Uuid));
|
||||
assertEquals(icon2, result.get(vm2Uuid));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsUsingOsCategory_missingGuestOS() {
|
||||
String vm1Uuid = UUID.randomUUID().toString();
|
||||
String os1Uuid = UUID.randomUUID().toString();
|
||||
UserVmResponse vm1 = mock(UserVmResponse.class);
|
||||
when(vm1.getGuestOsId()).thenReturn(vm1Uuid);
|
||||
when(vm1.getId()).thenReturn(os1Uuid);
|
||||
List<UserVmResponse> responses = Collections.singletonList(vm1);
|
||||
when(_entityMgr.listByUuids(eq(GuestOS.class), anySet()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Map<String, ResourceIcon> result = cmd.getResourceIconsUsingOsCategory(responses);
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsUsingOsCategory_missingIcon() {
|
||||
UserVmResponse vm1 = mock(UserVmResponse.class);
|
||||
String vmUuid = UUID.randomUUID().toString();
|
||||
String osUuid = UUID.randomUUID().toString();
|
||||
when(vm1.getGuestOsId()).thenReturn(osUuid);
|
||||
when(vm1.getId()).thenReturn(vmUuid);
|
||||
List<UserVmResponse> responses = Collections.singletonList(vm1);
|
||||
GuestOS guestOS1 = mock(GuestOS.class);
|
||||
when(guestOS1.getCategoryId()).thenReturn(10L);
|
||||
when(guestOS1.getUuid()).thenReturn(osUuid);
|
||||
when(_entityMgr.listByUuids(eq(GuestOS.class), anySet()))
|
||||
.thenReturn(Collections.singletonList(guestOS1));
|
||||
when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet()))
|
||||
.thenReturn(Collections.emptyMap());
|
||||
Map<String, ResourceIcon> result = cmd.getResourceIconsUsingOsCategory(responses);
|
||||
assertTrue(result.containsKey(vmUuid));
|
||||
assertNull(result.get(vmUuid));
|
||||
}
|
||||
}
|
||||
@ -22,10 +22,13 @@ import com.cloud.server.ResourceTag;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface ResourceIconDao extends GenericDao<ResourceIconVO, Long> {
|
||||
ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIconVO);
|
||||
ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.ResourceObjectType resourceType);
|
||||
List<ResourceIconVO> listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, Collection<Long> resourceIds);
|
||||
List<ResourceIconVO> listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, Collection<String> resourceUuids);
|
||||
List<ResourceIconResponse> listResourceIcons(List<String> resourceUuids, ResourceTag.ResourceObjectType resourceType);
|
||||
}
|
||||
|
||||
@ -24,8 +24,10 @@ import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ResourceIconDaoImpl extends GenericDaoBase<ResourceIconVO, Long> implements ResourceIconDao {
|
||||
@ -58,11 +60,36 @@ public class ResourceIconDaoImpl extends GenericDaoBase<ResourceIconVO, Long> im
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceIconResponse> listResourceIcons(List<String> resourceUuids, ResourceTag.ResourceObjectType resourceType) {
|
||||
public List<ResourceIconVO> listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType,
|
||||
Collection<Long> resourceIds) {
|
||||
if (CollectionUtils.isEmpty(resourceIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
SearchBuilder<ResourceIconVO> sb = createSearchBuilder();
|
||||
sb.and("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.IN);
|
||||
sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<ResourceIconVO> sc = sb.create();
|
||||
sc.setParameters("resourceId", resourceIds.toArray());
|
||||
sc.setParameters("resourceType", resourceType);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceIconVO> listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType,
|
||||
Collection<String> resourceUuids) {
|
||||
if (CollectionUtils.isEmpty(resourceUuids)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
SearchCriteria<ResourceIconVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("uuid", resourceUuids.toArray());
|
||||
sc.setParameters("resourceType", resourceType);
|
||||
List<ResourceIconVO> resourceIcons = listBy(sc);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceIconResponse> listResourceIcons(List<String> resourceUuids, ResourceTag.ResourceObjectType resourceType) {
|
||||
List<ResourceIconVO> resourceIcons = listByResourceTypeAndUuids(resourceType, resourceUuids);
|
||||
List<ResourceIconResponse> iconResponses = new ArrayList<>();
|
||||
for (ResourceIconVO resourceIcon : resourceIcons) {
|
||||
ResourceIconResponse response = new ResourceIconResponse();
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
// under the License.
|
||||
package com.cloud.storage;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
@ -25,6 +26,8 @@ import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
@Entity
|
||||
@Table(name = "guest_os_category")
|
||||
public class GuestOSCategoryVO implements GuestOsCategory {
|
||||
@ -39,6 +42,26 @@ public class GuestOSCategoryVO implements GuestOsCategory {
|
||||
@Column(name = "uuid")
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
|
||||
@Column(name = "featured")
|
||||
boolean featured;
|
||||
|
||||
@Column(name = "sort_key")
|
||||
private int sortKey;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN)
|
||||
private Date created;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
private Date removed;
|
||||
|
||||
public GuestOSCategoryVO() {
|
||||
}
|
||||
|
||||
public GuestOSCategoryVO(String name, boolean featured) {
|
||||
this.name = name;
|
||||
this.featured = featured;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
@ -59,7 +82,25 @@ public class GuestOSCategoryVO implements GuestOsCategory {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
@Override
|
||||
public boolean isFeatured() {
|
||||
return featured;
|
||||
}
|
||||
|
||||
public void setFeatured(Boolean featured) {
|
||||
this.featured = featured;
|
||||
}
|
||||
|
||||
public void setSortKey(int key) {
|
||||
sortKey = key;
|
||||
}
|
||||
|
||||
public int getSortKey() {
|
||||
return sortKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,6 @@ import com.cloud.utils.db.GenericDaoBase;
|
||||
public class GuestOSCategoryDaoImpl extends GenericDaoBase<GuestOSCategoryVO, Long> implements GuestOSCategoryDao {
|
||||
|
||||
protected GuestOSCategoryDaoImpl() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -16,14 +16,14 @@
|
||||
// under the License.
|
||||
package com.cloud.storage.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface GuestOSDao extends GenericDao<GuestOSVO, Long> {
|
||||
|
||||
GuestOSVO findOneByDisplayName(String displayName);
|
||||
@ -36,4 +36,6 @@ public interface GuestOSDao extends GenericDao<GuestOSVO, Long> {
|
||||
List<GuestOSVO> listByDisplayName(String displayName);
|
||||
|
||||
Pair<List<? extends GuestOS>, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, Long id, Long osCategoryId, String description, String keyword, Boolean forDisplay);
|
||||
|
||||
List<Long> listIdsByCategoryId(final long categoryId);
|
||||
}
|
||||
|
||||
@ -25,19 +25,20 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.GenericSearchBuilder;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
@Component
|
||||
public class GuestOSDaoImpl extends GenericDaoBase<GuestOSVO, Long> implements GuestOSDao {
|
||||
@ -152,4 +153,14 @@ public class GuestOSDaoImpl extends GenericDaoBase<GuestOSVO, Long> implements G
|
||||
return new Pair<>(result.first(), result.second());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listIdsByCategoryId(final long categoryId) {
|
||||
GenericSearchBuilder<GuestOSVO, Long> sb = createSearchBuilder(Long.class);
|
||||
sb.selectFields(sb.entity().getId());
|
||||
sb.and("categoryId", sb.entity().getCategoryId(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<Long> sc = sb.create();
|
||||
sc.setParameters("categoryId", categoryId);
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +58,8 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
|
||||
|
||||
public List<VMTemplateVO> listInZoneByState(long dataCenterId, VirtualMachineTemplate.State... states);
|
||||
|
||||
public List<Long> listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, Boolean isVnf);
|
||||
|
||||
public List<VMTemplateVO> listAllActive();
|
||||
|
||||
public List<VMTemplateVO> listByState(VirtualMachineTemplate.State... states);
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package com.cloud.storage.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
@ -520,6 +521,48 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso,
|
||||
Boolean isVnf) {
|
||||
GenericSearchBuilder<VMTemplateVO, Long> sb = createSearchBuilder(Long.class);
|
||||
sb.select(null, Func.DISTINCT, sb.entity().getGuestOSId());
|
||||
sb.and("state", sb.entity().getState(), SearchCriteria.Op.IN);
|
||||
sb.and("type", sb.entity().getTemplateType(), SearchCriteria.Op.IN);
|
||||
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
|
||||
if (isIso != null) {
|
||||
sb.and("isIso", sb.entity().getFormat(), isIso ? SearchCriteria.Op.EQ : SearchCriteria.Op.NEQ);
|
||||
}
|
||||
if (dataCenterId != null) {
|
||||
SearchBuilder<VMTemplateZoneVO> templateZoneSearch = _templateZoneDao.createSearchBuilder();
|
||||
templateZoneSearch.and("removed", templateZoneSearch.entity().getRemoved(), SearchCriteria.Op.NULL);
|
||||
templateZoneSearch.and("zoneId", templateZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
|
||||
sb.join("templateZoneSearch", templateZoneSearch, templateZoneSearch.entity().getTemplateId(),
|
||||
sb.entity().getId(), JoinBuilder.JoinType.INNER);
|
||||
templateZoneSearch.done();
|
||||
}
|
||||
sb.done();
|
||||
SearchCriteria<Long> sc = sb.create();
|
||||
List<TemplateType> types = new ArrayList<>(Arrays.asList(TemplateType.USER, TemplateType.BUILTIN,
|
||||
TemplateType.PERHOST));
|
||||
if (isVnf == null) {
|
||||
types.add(TemplateType.VNF);
|
||||
} else if (isVnf) {
|
||||
types = Collections.singletonList(TemplateType.VNF);
|
||||
}
|
||||
sc.setParameters("type", types.toArray());
|
||||
sc.setParameters("state", VirtualMachineTemplate.State.Active);
|
||||
if (dataCenterId != null) {
|
||||
sc.setJoinParameters("templateZoneSearch", "zoneId", dataCenterId);
|
||||
}
|
||||
if (arch != null) {
|
||||
sc.setParameters("arch", arch);
|
||||
}
|
||||
if (isIso != null) {
|
||||
sc.setParameters("isIso", ImageFormat.ISO);
|
||||
}
|
||||
return customSearch(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VMTemplateVO> listAllActive() {
|
||||
SearchCriteria<VMTemplateVO> sc = ActiveTmpltSearch.create();
|
||||
|
||||
@ -80,3 +80,93 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'storage_access_groups', 'var
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.cluster', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the cluster"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_pod_ref', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the pod"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.data_center', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the zone"');
|
||||
|
||||
-- Add featured column for guest_os_category
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'featured', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "whether the category is featured or not" AFTER `uuid`');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'sort_key', 'int NOT NULL DEFAULT 0 COMMENT "sort key used for customising sort method" AFTER `featured`');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'created', 'datetime COMMENT "date on which the category was created" AFTER `sort_key`');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'removed', 'datetime COMMENT "date removed if not null" AFTER `created`');
|
||||
UPDATE `cloud`.`guest_os_category` SET `featured` = 1 WHERE `name` NOT IN ('Novel', 'None');
|
||||
|
||||
-- Begin: Changes for Guest OS category cleanup
|
||||
-- Add new OS categories if not present
|
||||
DROP PROCEDURE IF EXISTS `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`;
|
||||
CREATE PROCEDURE `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`(IN os_name VARCHAR(255))
|
||||
BEGIN
|
||||
IF NOT EXISTS ((SELECT 1 FROM `cloud`.`guest_os_category` WHERE name = os_name))
|
||||
THEN
|
||||
INSERT INTO `cloud`.`guest_os_category` (name, uuid)
|
||||
VALUES (os_name, UUID())
|
||||
; END IF
|
||||
; END;
|
||||
|
||||
CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Fedora');
|
||||
CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Rocky Linux');
|
||||
CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Alma Linux');
|
||||
|
||||
-- Move existing guest OS to new categories
|
||||
DROP PROCEDURE IF EXISTS `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`;
|
||||
CREATE PROCEDURE `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`(IN category_name VARCHAR(255), IN os_name VARCHAR(255))
|
||||
BEGIN
|
||||
DECLARE category_id BIGINT
|
||||
; SELECT `id` INTO category_id
|
||||
FROM `cloud`.`guest_os_category`
|
||||
WHERE `name` = category_name
|
||||
LIMIT 1
|
||||
; IF category_id IS NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Category not found'
|
||||
; END IF
|
||||
; UPDATE `cloud`.`guest_os`
|
||||
SET `category_id` = category_id
|
||||
WHERE `display_name` LIKE CONCAT('%', os_name, '%')
|
||||
; END;
|
||||
CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Rocky Linux', 'Rocky Linux');
|
||||
CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Alma Linux', 'Alma Linux');
|
||||
CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Fedora', 'Fedora');
|
||||
|
||||
-- Move existing guest OS whose category will be deleted to Other category
|
||||
DROP PROCEDURE IF EXISTS `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`;
|
||||
CREATE PROCEDURE `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`(IN to_category_name VARCHAR(255), IN from_category_name VARCHAR(255))
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT 0
|
||||
; DECLARE to_category_id BIGINT
|
||||
; SELECT id INTO to_category_id
|
||||
FROM `cloud`.`guest_os_category`
|
||||
WHERE `name` = to_category_name
|
||||
LIMIT 1
|
||||
; IF to_category_id IS NULL THEN
|
||||
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'ToCategory not found'
|
||||
; END IF
|
||||
; UPDATE `cloud`.`guest_os`
|
||||
SET `category_id` = to_category_id
|
||||
WHERE `category_id` = (SELECT `id` FROM `cloud`.`guest_os_category` WHERE `name` = from_category_name)
|
||||
; UPDATE `cloud`.`guest_os_category` SET `removed`=now() WHERE `name` = from_category_name
|
||||
; END;
|
||||
CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Novel');
|
||||
CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'None');
|
||||
CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Unix');
|
||||
CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Mac');
|
||||
|
||||
-- Add featured column for cloud.guest_os_category
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'featured', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "whether the category is featured or not" AFTER `uuid`');
|
||||
UPDATE `cloud`.`guest_os_category` SET featured = 1;
|
||||
-- Add sort_key column for cloud.guest_os_category
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'sort_key', 'int NOT NULL DEFAULT 0 COMMENT "sort key used for customising sort method" AFTER `featured`');
|
||||
|
||||
-- Update sort order for all guest OS categories
|
||||
UPDATE `cloud`.`guest_os_category`
|
||||
SET `sort_key` = CASE
|
||||
WHEN `name` = 'Ubuntu' THEN 1
|
||||
WHEN `name` = 'Debian' THEN 2
|
||||
WHEN `name` = 'Fedora' THEN 3
|
||||
WHEN `name` = 'CentOS' THEN 4
|
||||
WHEN `name` = 'Rocky Linux' THEN 5
|
||||
WHEN `name` = 'Alma Linux' THEN 6
|
||||
WHEN `name` = 'Oracle' THEN 7
|
||||
WHEN `name` = 'RedHat' THEN 8
|
||||
WHEN `name` = 'SUSE' THEN 9
|
||||
WHEN `name` = 'Windows' THEN 10
|
||||
WHEN `name` = 'Other' THEN 11
|
||||
ELSE `sort_key`
|
||||
END;
|
||||
-- End: Changes for Guest OS category cleanup
|
||||
|
||||
@ -41,6 +41,7 @@ SELECT
|
||||
`vm_template`.`guest_os_id` AS `guest_os_id`,
|
||||
`guest_os`.`uuid` AS `guest_os_uuid`,
|
||||
`guest_os`.`display_name` AS `guest_os_name`,
|
||||
`guest_os`.`category_id` AS `guest_os_category_id`,
|
||||
`vm_template`.`bootable` AS `bootable`,
|
||||
`vm_template`.`prepopulate` AS `prepopulate`,
|
||||
`vm_template`.`cross_zones` AS `cross_zones`,
|
||||
|
||||
@ -21,7 +21,11 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -44,8 +48,11 @@ import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VMTemplateZoneVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericSearchBuilder;
|
||||
import com.cloud.utils.db.JoinBuilder;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
@ -55,6 +62,9 @@ public class VMTemplateDaoImplTest {
|
||||
@Mock
|
||||
HostDao hostDao;
|
||||
|
||||
@Mock
|
||||
VMTemplateZoneDao templateZoneDao;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
VMTemplateDaoImpl templateDao = new VMTemplateDaoImpl();
|
||||
@ -186,4 +196,107 @@ public class VMTemplateDaoImplTest {
|
||||
VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
private void mockTemplateZoneJoin() {
|
||||
VMTemplateZoneVO templateZoneVO = mock(VMTemplateZoneVO.class);
|
||||
SearchBuilder<VMTemplateZoneVO> templateZoneVOSearchBuilder = mock(SearchBuilder.class);
|
||||
when(templateZoneVOSearchBuilder.entity()).thenReturn(templateZoneVO);
|
||||
when(templateZoneDao.createSearchBuilder()).thenReturn(templateZoneVOSearchBuilder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTemplateIsoByArchAndZone_WithDataCenterId() {
|
||||
Long dataCenterId = 1L;
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
Boolean isIso = true;
|
||||
VMTemplateVO templateVO = mock(VMTemplateVO.class);
|
||||
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
|
||||
when(searchBuilder.entity()).thenReturn(templateVO);
|
||||
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
|
||||
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
|
||||
when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
mockTemplateZoneJoin();
|
||||
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
|
||||
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
|
||||
assertNotNull(result);
|
||||
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
|
||||
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
|
||||
verify(templateDao, times(1)).customSearch(searchCriteria, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTemplateIsoByArchAndZone_WithoutDataCenterId() {
|
||||
Long dataCenterId = null;
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
Boolean isIso = false;
|
||||
VMTemplateVO templateVO = mock(VMTemplateVO.class);
|
||||
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
|
||||
when(searchBuilder.entity()).thenReturn(templateVO);
|
||||
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
|
||||
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
|
||||
when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
|
||||
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
|
||||
assertNotNull(result);
|
||||
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
|
||||
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ));
|
||||
verify(searchBuilder, never()).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
|
||||
verify(templateDao, times(1)).customSearch(searchCriteria, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTemplateIsoByArchAndZone_WithoutArch() {
|
||||
Long dataCenterId = 1L;
|
||||
CPU.CPUArch arch = null;
|
||||
Boolean isIso = true;
|
||||
VMTemplateVO templateVO = mock(VMTemplateVO.class);
|
||||
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
|
||||
when(searchBuilder.entity()).thenReturn(templateVO);
|
||||
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
|
||||
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
|
||||
when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
mockTemplateZoneJoin();
|
||||
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
|
||||
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
|
||||
assertNotNull(result);
|
||||
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
|
||||
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
|
||||
verify(templateDao, times(1)).customSearch(searchCriteria, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListTemplateIsoByArchAndZone_WithoutIsIso() {
|
||||
Long dataCenterId = 1L;
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
Boolean isIso = null;
|
||||
VMTemplateVO templateVO = mock(VMTemplateVO.class);
|
||||
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
|
||||
when(searchBuilder.entity()).thenReturn(templateVO);
|
||||
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
|
||||
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
|
||||
when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
mockTemplateZoneJoin();
|
||||
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
|
||||
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
|
||||
assertNotNull(result);
|
||||
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
|
||||
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
|
||||
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ));
|
||||
verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ));
|
||||
verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
|
||||
verify(templateDao, times(1)).customSearch(searchCriteria, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package com.cloud.dao;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -56,6 +57,13 @@ public class EntityManagerImpl extends ManagerBase implements EntityManager {
|
||||
return dao.findByUuid(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> listByUuids(Class<T> entityType, Collection<String> uuids) {
|
||||
// Finds and returns a unique VO using uuid, null if entity not found in db
|
||||
GenericDao<? extends T, String> dao = (GenericDao<? extends T, String>)GenericDaoBase.getDao(entityType);
|
||||
return (List<T>)dao.listByUuids(uuids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findByUuidIncludingRemoved(Class<T> entityType, String uuid) {
|
||||
// Finds and returns a unique VO using uuid, null if entity not found in db
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
package com.cloud.utils.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -56,6 +58,10 @@ public interface GenericDao<T, ID extends Serializable> {
|
||||
// Finds one unique VO using uuid
|
||||
T findByUuid(String uuid);
|
||||
|
||||
default List<T> listByUuids(Collection<String> uuids) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// Finds one unique VO using uuid including removed entities
|
||||
T findByUuidIncludingRemoved(String uuid);
|
||||
|
||||
|
||||
@ -1006,6 +1006,17 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB()
|
||||
public List<T> listByUuids(final Collection<String> uuids) {
|
||||
if (org.apache.commons.collections.CollectionUtils.isEmpty(uuids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
SearchCriteria<T> sc = createSearchCriteria();
|
||||
sc.addAnd("uuid", SearchCriteria.Op.IN, uuids.toArray());
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB()
|
||||
public T findByUuidIncludingRemoved(final String uuid) {
|
||||
|
||||
@ -39,16 +39,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.bgp.ASNumber;
|
||||
import com.cloud.bgp.ASNumberRange;
|
||||
import com.cloud.dc.ASNumberRangeVO;
|
||||
import com.cloud.dc.ASNumberVO;
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.dao.ASNumberDao;
|
||||
import com.cloud.dc.dao.ASNumberRangeDao;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.BucketVO;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
@ -63,12 +53,12 @@ import org.apache.cloudstack.api.BaseResponseWithAssociatedNetwork;
|
||||
import org.apache.cloudstack.api.ResponseGenerator;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
|
||||
import org.apache.cloudstack.api.response.ASNRangeResponse;
|
||||
import org.apache.cloudstack.api.response.ASNumberResponse;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.ApplicationLoadBalancerInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse;
|
||||
import org.apache.cloudstack.api.response.ApplicationLoadBalancerRuleResponse;
|
||||
import org.apache.cloudstack.api.response.ASNRangeResponse;
|
||||
import org.apache.cloudstack.api.response.ASNumberResponse;
|
||||
import org.apache.cloudstack.api.response.AsyncJobResponse;
|
||||
import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
|
||||
import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
|
||||
@ -99,10 +89,10 @@ import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||
import org.apache.cloudstack.api.response.EventResponse;
|
||||
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.FirewallRuleResponse;
|
||||
import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOSResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOsMappingResponse;
|
||||
import org.apache.cloudstack.api.response.GuestVlanRangeResponse;
|
||||
@ -164,6 +154,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupRuleResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceResponse;
|
||||
import org.apache.cloudstack.api.response.SharedFSResponse;
|
||||
import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse;
|
||||
import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse;
|
||||
import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse;
|
||||
@ -230,10 +221,10 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
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.ObjectStore;
|
||||
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||
import org.apache.cloudstack.storage.sharedfs.query.vo.SharedFSJoinVO;
|
||||
import org.apache.cloudstack.usage.Usage;
|
||||
import org.apache.cloudstack.usage.UsageService;
|
||||
import org.apache.cloudstack.usage.UsageTypes;
|
||||
@ -241,8 +232,8 @@ import org.apache.cloudstack.vm.UnmanagedInstanceTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.VgpuTypesInfo;
|
||||
import com.cloud.api.query.ViewResponseHelper;
|
||||
@ -271,6 +262,8 @@ import com.cloud.api.query.vo.UserVmJoinVO;
|
||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||
import com.cloud.api.query.vo.VpcOfferingJoinVO;
|
||||
import com.cloud.api.response.ApiResponseSerializer;
|
||||
import com.cloud.bgp.ASNumber;
|
||||
import com.cloud.bgp.ASNumberRange;
|
||||
import com.cloud.capacity.Capacity;
|
||||
import com.cloud.capacity.CapacityVO;
|
||||
import com.cloud.capacity.dao.CapacityDaoImpl.SummedCapacity;
|
||||
@ -279,6 +272,8 @@ import com.cloud.configuration.Resource.ResourceOwnerType;
|
||||
import com.cloud.configuration.Resource.ResourceType;
|
||||
import com.cloud.configuration.ResourceCount;
|
||||
import com.cloud.configuration.ResourceLimit;
|
||||
import com.cloud.dc.ASNumberRangeVO;
|
||||
import com.cloud.dc.ASNumberVO;
|
||||
import com.cloud.dc.ClusterDetailsDao;
|
||||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.DataCenter;
|
||||
@ -289,7 +284,11 @@ import com.cloud.dc.Pod;
|
||||
import com.cloud.dc.StorageNetworkIpRange;
|
||||
import com.cloud.dc.Vlan;
|
||||
import com.cloud.dc.Vlan.VlanType;
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.VlanVO;
|
||||
import com.cloud.dc.dao.ASNumberDao;
|
||||
import com.cloud.dc.dao.ASNumberRangeDao;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.event.Event;
|
||||
@ -299,6 +298,7 @@ import com.cloud.gpu.GPU;
|
||||
import com.cloud.host.ControlState;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.HypervisorCapabilities;
|
||||
import com.cloud.network.GuestVlan;
|
||||
import com.cloud.network.GuestVlanRange;
|
||||
@ -377,10 +377,13 @@ import com.cloud.projects.ProjectAccount;
|
||||
import com.cloud.projects.ProjectInvitation;
|
||||
import com.cloud.region.ha.GlobalLoadBalancerRule;
|
||||
import com.cloud.resource.RollingMaintenanceManager;
|
||||
import com.cloud.resource.icon.ResourceIconVO;
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceIconManager;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.server.ResourceTag.ResourceObjectType;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.storage.BucketVO;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.GuestOS;
|
||||
@ -521,6 +524,8 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
BgpPeerDao bgpPeerDao;
|
||||
@Inject
|
||||
RoutedIpv4Manager routedIpv4Manager;
|
||||
@Inject
|
||||
ResourceIconManager resourceIconManager;
|
||||
|
||||
@Override
|
||||
public UserResponse createUserResponse(User user) {
|
||||
@ -3902,6 +3907,30 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory) {
|
||||
return createGuestOSCategoryResponse(guestOsCategory, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory, boolean showIcon) {
|
||||
GuestOSCategoryResponse categoryResponse = new GuestOSCategoryResponse();
|
||||
categoryResponse.setId(guestOsCategory.getUuid());
|
||||
categoryResponse.setName(guestOsCategory.getName());
|
||||
categoryResponse.setFeatured(guestOsCategory.isFeatured());
|
||||
categoryResponse.setCreated(guestOsCategory.getCreated());
|
||||
if (showIcon) {
|
||||
ResourceIconVO resourceIcon = ApiDBUtils.getResourceIconByResourceUUID(guestOsCategory.getUuid(),
|
||||
ResourceObjectType.GuestOsCategory);
|
||||
if (resourceIcon != null) {
|
||||
ResourceIconResponse iconResponse = ApiDBUtils.newResourceIconResponse(resourceIcon);
|
||||
categoryResponse.setResourceIconResponse(iconResponse);
|
||||
}
|
||||
}
|
||||
categoryResponse.setObjectName("oscategory");
|
||||
return categoryResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuestOSResponse createGuestOSResponse(GuestOS guestOS) {
|
||||
GuestOSResponse response = new GuestOSResponse();
|
||||
@ -5482,4 +5511,39 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
SharedFSJoinVO sharedFSView = ApiDBUtils.newSharedFSView(sharedFS);
|
||||
return ApiDBUtils.newSharedFSResponse(view, sharedFSView);
|
||||
}
|
||||
|
||||
protected Map<String, ResourceIcon> getResourceIconsUsingOsCategory(List<TemplateResponse> responses) {
|
||||
Set<Long> guestOsCategoryIds = responses.stream().map(TemplateResponse::getOsTypeCategoryId).collect(Collectors.toSet());
|
||||
Map<Long, ResourceIcon> guestOsCategoryIcons =
|
||||
resourceIconManager.getByResourceTypeAndIds(ResourceTag.ResourceObjectType.GuestOsCategory,
|
||||
guestOsCategoryIds);
|
||||
Map<String, ResourceIcon> vmIcons = new HashMap<>();
|
||||
for (TemplateResponse response : responses) {
|
||||
vmIcons.put(response.getId(), guestOsCategoryIcons.get(response.getOsTypeCategoryId()));
|
||||
}
|
||||
return vmIcons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTemplateIsoResponsesForIcons(List<TemplateResponse> responses,
|
||||
ResourceTag.ResourceObjectType type) {
|
||||
if (CollectionUtils.isEmpty(responses)) {
|
||||
return;
|
||||
}
|
||||
Set<String> uuids = responses.stream().map(TemplateResponse::getId).collect(Collectors.toSet());
|
||||
Map<String, ResourceIcon> templateIcons = resourceIconManager.getByResourceTypeAndUuids(type, uuids);
|
||||
List<TemplateResponse> noTemplateIconResponses = responses
|
||||
.stream()
|
||||
.filter(r -> !templateIcons.containsKey(r.getId()))
|
||||
.collect(Collectors.toList());
|
||||
templateIcons.putAll(getResourceIconsUsingOsCategory(noTemplateIconResponses));
|
||||
for (TemplateResponse response : responses) {
|
||||
ResourceIcon icon = templateIcons.get(response.getId());
|
||||
if (icon == null) {
|
||||
continue;
|
||||
}
|
||||
ResourceIconResponse iconResponse = createResourceIconResponse(icon);
|
||||
response.setResourceIconResponse(iconResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,6 +316,7 @@ import com.cloud.storage.VolumeApiServiceImpl;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.BucketDao;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.dao.GuestOSDao;
|
||||
import com.cloud.storage.dao.StoragePoolHostDao;
|
||||
import com.cloud.storage.dao.StoragePoolTagsDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
@ -643,6 +644,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
@Inject
|
||||
HostPodDao podDao;
|
||||
|
||||
@Inject
|
||||
GuestOSDao guestOSDao;
|
||||
|
||||
private SearchCriteria<ServiceOfferingJoinVO> getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) {
|
||||
SearchCriteria<ServiceOfferingJoinVO> sc = _srvOfferingJoinDao.createSearchCriteria();
|
||||
SearchCriteria<ServiceOfferingJoinVO> sc1 = _srvOfferingJoinDao.createSearchCriteria();
|
||||
@ -4791,7 +4795,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(),
|
||||
cmd.getImageStoreId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller,
|
||||
listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique(),
|
||||
templateType, isVnf, cmd.getArch());
|
||||
templateType, isVnf, cmd.getArch(), cmd.getOsCategoryId());
|
||||
}
|
||||
|
||||
private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword,
|
||||
@ -4800,7 +4804,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller,
|
||||
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags,
|
||||
boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, Boolean showUnique, String templateType,
|
||||
Boolean isVnf, CPU.CPUArch arch) {
|
||||
Boolean isVnf, CPU.CPUArch arch, Long osCategoryId) {
|
||||
|
||||
// check if zone is configured, if not, just return empty list
|
||||
List<HypervisorType> hypers = null;
|
||||
@ -4828,10 +4832,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
|
||||
if (storagePoolId != null) {
|
||||
SearchBuilder<VMTemplateStoragePoolVO> storagePoolSb = templatePoolDao.createSearchBuilder();
|
||||
storagePoolSb.and("pool_id", storagePoolSb.entity().getPoolId(), SearchCriteria.Op.EQ);
|
||||
sb.join("storagePool", storagePoolSb, storagePoolSb.entity().getTemplateId(), sb.entity().getId(), JoinBuilder.JoinType.INNER);
|
||||
}
|
||||
|
||||
if (osCategoryId != null) {
|
||||
sb.and("guestOsIdIN", sb.entity().getGuestOSId(), Op.IN);
|
||||
}
|
||||
|
||||
SearchCriteria<TemplateJoinVO> sc = sb.create();
|
||||
|
||||
if (imageStoreId != null) {
|
||||
@ -4846,6 +4853,15 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
sc.setJoinParameters("storagePool", "pool_id", storagePoolId);
|
||||
}
|
||||
|
||||
if (osCategoryId != null) {
|
||||
List<Long> guestOsIds = guestOSDao.listIdsByCategoryId(osCategoryId);
|
||||
if (CollectionUtils.isNotEmpty(guestOsIds)) {
|
||||
sc.setParameters("guestOsIdIN", guestOsIds.toArray());
|
||||
} else {
|
||||
return new Pair<>(new ArrayList<>(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// verify templateId parameter and specially handle it
|
||||
if (templateId != null) {
|
||||
template = _templateDao.findByIdIncludingRemoved(templateId); // Done for backward compatibility - Bug-5221
|
||||
@ -5231,7 +5247,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(),
|
||||
cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(),
|
||||
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria,
|
||||
tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null, cmd.getArch());
|
||||
tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null,
|
||||
cmd.getArch(), cmd.getOsCategoryId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -28,54 +28,53 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.deployasis.DeployAsIsConstants;
|
||||
import com.cloud.deployasis.TemplateDeployAsIsDetailVO;
|
||||
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.storage.VnfTemplateDetailVO;
|
||||
import com.cloud.storage.VnfTemplateNicVO;
|
||||
import com.cloud.storage.dao.VnfTemplateDetailsDao;
|
||||
import com.cloud.storage.dao.VnfTemplateNicDao;
|
||||
import com.cloud.user.dao.UserDataDao;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.response.VnfNicResponse;
|
||||
import org.apache.cloudstack.api.response.VnfTemplateResponse;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.utils.security.DigestHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.response.ChildTemplateResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.VnfNicResponse;
|
||||
import org.apache.cloudstack.api.response.VnfTemplateResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateState;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.utils.security.DigestHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.api.ApiResponseHelper;
|
||||
import com.cloud.api.query.vo.ResourceTagJoinVO;
|
||||
import com.cloud.api.query.vo.TemplateJoinVO;
|
||||
import com.cloud.deployasis.DeployAsIsConstants;
|
||||
import com.cloud.deployasis.TemplateDeployAsIsDetailVO;
|
||||
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Storage.TemplateType;
|
||||
import com.cloud.storage.VMTemplateStoragePoolVO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VnfTemplateDetailVO;
|
||||
import com.cloud.storage.VnfTemplateNicVO;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplateDetailsDao;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.storage.dao.VnfTemplateDetailsDao;
|
||||
import com.cloud.storage.dao.VnfTemplateNicDao;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.user.dao.UserDataDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
@ -262,6 +261,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
||||
|
||||
templateResponse.setOsTypeId(template.getGuestOSUuid());
|
||||
templateResponse.setOsTypeName(template.getGuestOSName());
|
||||
templateResponse.setOsTypeCategoryId(template.getGuestOSCategoryId());
|
||||
|
||||
// populate owner.
|
||||
ApiResponseHelper.populateOwner(templateResponse, template);
|
||||
@ -402,6 +402,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
||||
response.setFormat(result.getFormat());
|
||||
response.setOsTypeId(result.getGuestOSUuid());
|
||||
response.setOsTypeName(result.getGuestOSName());
|
||||
response.setOsTypeCategoryId(result.getGuestOSCategoryId());
|
||||
response.setBootable(result.isBootable());
|
||||
response.setHypervisor(result.getHypervisorType().getHypervisorDisplayName());
|
||||
response.setDynamicallyScalable(result.isDynamicallyScalable());
|
||||
@ -490,6 +491,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation<Templa
|
||||
|
||||
isoResponse.setOsTypeId(iso.getGuestOSUuid());
|
||||
isoResponse.setOsTypeName(iso.getGuestOSName());
|
||||
isoResponse.setOsTypeCategoryId(iso.getGuestOSCategoryId());
|
||||
isoResponse.setBits(iso.getBits());
|
||||
isoResponse.setPasswordEnabled(iso.isEnablePassword());
|
||||
|
||||
|
||||
@ -27,19 +27,19 @@ import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.user.Account;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.util.CPUArchConverter;
|
||||
import org.apache.cloudstack.util.HypervisorTypeConverter;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.storage.ScopeType;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.template.VirtualMachineTemplate.State;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.util.CPUArchConverter;
|
||||
import org.apache.cloudstack.util.HypervisorTypeConverter;
|
||||
|
||||
@Entity
|
||||
@Table(name = "template_view")
|
||||
@ -112,6 +112,9 @@ public class TemplateJoinVO extends BaseViewWithTagInformationVO implements Cont
|
||||
@Column(name = "guest_os_name")
|
||||
private String guestOSName;
|
||||
|
||||
@Column(name = "guest_os_category_id")
|
||||
private Long guestOSCategoryId;
|
||||
|
||||
@Column(name = "bootable")
|
||||
private boolean bootable = true;
|
||||
|
||||
@ -405,6 +408,10 @@ public class TemplateJoinVO extends BaseViewWithTagInformationVO implements Cont
|
||||
return guestOSName;
|
||||
}
|
||||
|
||||
public Long getGuestOSCategoryId() {
|
||||
return guestOSCategoryId;
|
||||
}
|
||||
|
||||
public boolean isBootable() {
|
||||
return bootable;
|
||||
}
|
||||
|
||||
@ -16,8 +16,11 @@
|
||||
// under the License.
|
||||
package com.cloud.resourceicon;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -247,4 +250,18 @@ public class ResourceIconManagerImpl extends ManagerBase implements ResourceIcon
|
||||
sc.setParameters("resourceType", resourceType);
|
||||
return resourceIconDao.search(sc, null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<Long, ResourceIcon> getByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, Collection<Long> resourceIds) {
|
||||
List<ResourceIconVO> icons = resourceIconDao.listByResourceTypeAndIds(resourceType, resourceIds);
|
||||
return icons.stream().collect(Collectors.toMap(ResourceIconVO::getResourceId, Function.identity()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, ResourceIcon> getByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, Collection<String> resourceUuids) {
|
||||
List<ResourceIconVO> icons = resourceIconDao.listByResourceTypeAndUuids(resourceType, resourceUuids);
|
||||
return icons.stream().collect(Collectors.toMap(ResourceIconVO::getResourceUuid, Function.identity()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,12 +89,15 @@ import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmdByAdmin;
|
||||
import org.apache.cloudstack.api.command.admin.domain.MoveDomainCmd;
|
||||
import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd;
|
||||
import org.apache.cloudstack.api.command.admin.host.AddHostCmd;
|
||||
@ -643,6 +646,8 @@ import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.cloudstack.vm.lease.VMLeaseManager;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
@ -2752,29 +2757,114 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
||||
|
||||
@Override
|
||||
public Pair<List<? extends GuestOsCategory>, Integer> listGuestOSCategoriesByCriteria(final ListGuestOsCategoriesCmd cmd) {
|
||||
final Filter searchFilter = new Filter(GuestOSCategoryVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
final Filter searchFilter = new Filter(GuestOSCategoryVO.class, "sortKey", true,
|
||||
cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
searchFilter.addOrderBy(GuestOSCategoryVO.class, "id", true);
|
||||
final Long id = cmd.getId();
|
||||
final String name = cmd.getName();
|
||||
final String keyword = cmd.getKeyword();
|
||||
final Boolean featured = cmd.isFeatured();
|
||||
final Boolean isIso = cmd.isIso();
|
||||
final Boolean isVnf = cmd.isVnf();
|
||||
final Long zoneId = cmd.getZoneId();
|
||||
final CPU.CPUArch arch = cmd.getArch();
|
||||
|
||||
final SearchCriteria<GuestOSCategoryVO> sc = _guestOSCategoryDao.createSearchCriteria();
|
||||
|
||||
final SearchBuilder<GuestOSCategoryVO> sb = _guestOSCategoryDao.createSearchBuilder();
|
||||
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
|
||||
sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE);
|
||||
sb.and("featured", sb.entity().isFeatured(), SearchCriteria.Op.EQ);
|
||||
if (ObjectUtils.anyNotNull(zoneId, arch, isIso, isVnf)) {
|
||||
final SearchBuilder<GuestOSVO> guestOsSearch = _guestOSDao.createSearchBuilder();
|
||||
guestOsSearch.and("ids", guestOsSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||
sb.join("guestOsSearch", guestOsSearch, guestOsSearch.entity().getCategoryId(), sb.entity().getId(),
|
||||
JoinType.INNER);
|
||||
guestOsSearch.done();
|
||||
sb.groupBy(sb.entity().getId());
|
||||
}
|
||||
sb.done();
|
||||
SearchCriteria<GuestOSCategoryVO> sc = sb.create();
|
||||
if (id != null) {
|
||||
sc.addAnd("id", SearchCriteria.Op.EQ, id);
|
||||
sc.setParameters("id", id);
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + name + "%");
|
||||
sc.setParameters("name", "%" + name + "%");
|
||||
}
|
||||
|
||||
if (keyword != null) {
|
||||
sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
||||
sc.setParameters("name", "%" + keyword + "%");
|
||||
}
|
||||
if (featured != null) {
|
||||
sc.setParameters("featured", featured);
|
||||
}
|
||||
if (ObjectUtils.anyNotNull(zoneId, arch, isIso, isVnf)) {
|
||||
List<Long> guestOsIds = templateDao.listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf);
|
||||
if (CollectionUtils.isEmpty(guestOsIds)) {
|
||||
return new Pair<>(Collections.emptyList(), 0);
|
||||
}
|
||||
sc.setJoinParameters("guestOsSearch", "ids", guestOsIds.toArray());
|
||||
}
|
||||
|
||||
final Pair<List<GuestOSCategoryVO>, Integer> result = _guestOSCategoryDao.searchAndCount(sc, searchFilter);
|
||||
return new Pair<>(result.first(), result.second());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_CATEGORY_ADD, eventDescription = "adding OS category")
|
||||
public GuestOsCategory addGuestOsCategory(AddGuestOsCategoryCmd cmd) {
|
||||
final String name = cmd.getName();
|
||||
final boolean featured = cmd.isFeatured();
|
||||
final GuestOSCategoryVO guestOSCategory = new GuestOSCategoryVO(name, featured);
|
||||
GuestOsCategory guestOsCategory = _guestOSCategoryDao.persist(guestOSCategory);
|
||||
CallContext.current().setEventResourceId(guestOsCategory.getId());
|
||||
CallContext.current().setEventResourceType(ApiCommandResourceType.GuestOsCategory);
|
||||
return guestOSCategory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_CATEGORY_UPDATE, eventDescription = "updating OS category")
|
||||
public GuestOsCategory updateGuestOsCategory(UpdateGuestOsCategoryCmd cmd) {
|
||||
final long id = cmd.getId();
|
||||
final String name = cmd.getName();
|
||||
final Boolean featured = cmd.isFeatured();
|
||||
Integer sortKey = cmd.getSortKey();
|
||||
final GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(id);
|
||||
if (guestOSCategory == null) {
|
||||
throw new InvalidParameterValueException("Invalid OS category ID specified");
|
||||
}
|
||||
if (ObjectUtils.allNull(name, featured, sortKey)) {
|
||||
return guestOSCategory;
|
||||
}
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
guestOSCategory.setName(name);
|
||||
}
|
||||
if (featured != null) {
|
||||
guestOSCategory.setFeatured(featured);
|
||||
}
|
||||
if (sortKey != null) {
|
||||
guestOSCategory.setSortKey(sortKey);
|
||||
}
|
||||
if (!_guestOSCategoryDao.update(id, guestOSCategory)) {
|
||||
return null;
|
||||
}
|
||||
return guestOSCategory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_CATEGORY_DELETE, eventDescription = "deleting OS category")
|
||||
public boolean deleteGuestOsCategory(DeleteGuestOsCategoryCmd cmd) {
|
||||
final long id = cmd.getId();
|
||||
final GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(id);
|
||||
if (guestOSCategory == null) {
|
||||
throw new InvalidParameterValueException("Invalid OS category ID specified");
|
||||
}
|
||||
List<Long> guestOses = _guestOSDao.listIdsByCategoryId(id);
|
||||
if (!guestOses.isEmpty()) {
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Unable to delete the OS category. %d guest OS exist for it.", guestOses.size()));
|
||||
}
|
||||
return _guestOSCategoryDao.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<? extends GuestOSHypervisor>, Integer> listGuestOSMappingByCriteria(final ListGuestOsMappingCmd cmd) {
|
||||
final String guestOsId = "guestOsId";
|
||||
@ -2992,6 +3082,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
||||
public GuestOS updateGuestOs(final UpdateGuestOsCmd cmd) {
|
||||
final Long id = cmd.getId();
|
||||
final String displayName = cmd.getOsDisplayName();
|
||||
final Long osCategoryId = cmd.getOsCategoryId();
|
||||
final Boolean display = cmd.getForDisplay();
|
||||
final Map<String, String> details = cmd.getDetails();
|
||||
boolean updateNeeded = false;
|
||||
|
||||
//check if guest OS exists
|
||||
final GuestOS guestOsHandle = ApiDBUtils.findGuestOSById(id);
|
||||
@ -2999,27 +3093,46 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
||||
throw new InvalidParameterValueException("Guest OS not found. Please specify a valid ID for the Guest OS");
|
||||
}
|
||||
|
||||
if (!guestOsHandle.getIsUserDefined()) {
|
||||
throw new InvalidParameterValueException("Unable to modify system defined guest OS");
|
||||
}
|
||||
|
||||
persistGuestOsDetails(cmd.getDetails(), id);
|
||||
|
||||
//Check if update is needed
|
||||
if (displayName.equals(guestOsHandle.getDisplayName())) {
|
||||
return guestOsHandle;
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(displayName) && !displayName.equals(guestOsHandle.getDisplayName())) {
|
||||
//Check if another Guest OS by same name exists
|
||||
final GuestOS duplicate = ApiDBUtils.findGuestOSByDisplayName(displayName);
|
||||
if (duplicate != null) {
|
||||
throw new InvalidParameterValueException("The specified Guest OS name : " + displayName + " already exists. Please specify a unique guest OS name");
|
||||
}
|
||||
updateNeeded = true;
|
||||
}
|
||||
|
||||
if (osCategoryId != null) {
|
||||
if (_guestOSCategoryDao.findById(osCategoryId) == null) {
|
||||
throw new InvalidParameterValueException("Invalid OS category ID specified");
|
||||
}
|
||||
updateNeeded = true;
|
||||
}
|
||||
|
||||
if (!guestOsHandle.getIsUserDefined() && (StringUtils.isNotBlank(displayName) || MapUtils.isNotEmpty(details)
|
||||
|| display != null)) {
|
||||
throw new InvalidParameterValueException("Unable to modify system defined guest OS");
|
||||
}
|
||||
|
||||
if (MapUtils.isNotEmpty(details)) {
|
||||
persistGuestOsDetails(details, id);
|
||||
}
|
||||
|
||||
if (!updateNeeded) {
|
||||
return guestOsHandle;
|
||||
}
|
||||
|
||||
final GuestOSVO guestOs = _guestOSDao.createForUpdate(id);
|
||||
if (StringUtils.isNotBlank(displayName)) {
|
||||
guestOs.setDisplayName(displayName);
|
||||
}
|
||||
if (cmd.getForDisplay() != null) {
|
||||
guestOs.setDisplay(cmd.getForDisplay());
|
||||
}
|
||||
if (osCategoryId != null) {
|
||||
guestOs.setCategoryId(osCategoryId);
|
||||
}
|
||||
if (_guestOSDao.update(id, guestOs)) {
|
||||
return _guestOSDao.findById(id);
|
||||
} else {
|
||||
@ -3722,6 +3835,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
||||
cmdList.add(ListPortForwardingRulesCmd.class);
|
||||
cmdList.add(UpdatePortForwardingRuleCmd.class);
|
||||
cmdList.add(ListGuestOsCategoriesCmd.class);
|
||||
cmdList.add(AddGuestOsCategoryCmd.class);
|
||||
cmdList.add(UpdateGuestOsCategoryCmd.class);
|
||||
cmdList.add(DeleteGuestOsCategoryCmd.class);
|
||||
cmdList.add(ListGuestOsCmd.class);
|
||||
cmdList.add(ListGuestOsMappingCmd.class);
|
||||
cmdList.add(AddGuestOsCmd.class);
|
||||
|
||||
@ -58,6 +58,7 @@ import com.cloud.server.ResourceManagerUtil;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
@ -117,6 +118,7 @@ public class ResourceManagerUtilImpl implements ResourceManagerUtil {
|
||||
s_typeMap.put(ResourceTag.ResourceObjectType.NetworkOffering, NetworkOfferingVO.class);
|
||||
s_typeMap.put(ResourceTag.ResourceObjectType.VpcOffering, VpcOfferingVO.class);
|
||||
s_typeMap.put(ResourceTag.ResourceObjectType.Domain, DomainVO.class);
|
||||
s_typeMap.put(ResourceTag.ResourceObjectType.GuestOsCategory, GuestOsCategory.class);
|
||||
}
|
||||
|
||||
@Inject
|
||||
|
||||
@ -16,37 +16,36 @@
|
||||
// under the License.
|
||||
package com.cloud.api;
|
||||
|
||||
import com.cloud.capacity.Capacity;
|
||||
import com.cloud.configuration.Resource;
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.network.PublicIpQuarantine;
|
||||
import com.cloud.network.as.AutoScaleVmGroup;
|
||||
import com.cloud.network.as.AutoScaleVmGroupVO;
|
||||
import com.cloud.network.as.AutoScaleVmProfileVO;
|
||||
import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
|
||||
import com.cloud.network.dao.IPAddressDao;
|
||||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.LoadBalancerVO;
|
||||
import com.cloud.network.dao.NetworkServiceMapDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.usage.UsageVO;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserData;
|
||||
import com.cloud.user.UserDataVO;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.user.dao.UserDataDao;
|
||||
import com.cloud.utils.net.Ip;
|
||||
import com.cloud.vm.NicSecondaryIp;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
|
||||
import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
|
||||
import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse;
|
||||
import org.apache.cloudstack.api.response.GuestOSCategoryResponse;
|
||||
import org.apache.cloudstack.api.response.IpQuarantineResponse;
|
||||
import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UnmanagedInstanceResponse;
|
||||
import org.apache.cloudstack.api.response.UsageRecordResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
@ -63,21 +62,38 @@ import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.when;
|
||||
import com.cloud.capacity.Capacity;
|
||||
import com.cloud.configuration.Resource;
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.network.PublicIpQuarantine;
|
||||
import com.cloud.network.as.AutoScaleVmGroup;
|
||||
import com.cloud.network.as.AutoScaleVmGroupVO;
|
||||
import com.cloud.network.as.AutoScaleVmProfileVO;
|
||||
import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
|
||||
import com.cloud.network.dao.IPAddressDao;
|
||||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.LoadBalancerVO;
|
||||
import com.cloud.network.dao.NetworkServiceMapDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.resource.icon.ResourceIconVO;
|
||||
import com.cloud.server.ResourceIcon;
|
||||
import com.cloud.server.ResourceIconManager;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.usage.UsageVO;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.UserData;
|
||||
import com.cloud.user.UserDataVO;
|
||||
import com.cloud.user.UserVO;
|
||||
import com.cloud.user.dao.UserDataDao;
|
||||
import com.cloud.utils.net.Ip;
|
||||
import com.cloud.vm.NicSecondaryIp;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ApiResponseHelperTest {
|
||||
@ -105,6 +121,9 @@ public class ApiResponseHelperTest {
|
||||
@Mock
|
||||
IPAddressDao ipAddressDaoMock;
|
||||
|
||||
@Mock
|
||||
ResourceIconManager resourceIconManager;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
ApiResponseHelper apiResponseHelper = new ApiResponseHelper();
|
||||
@ -481,4 +500,135 @@ public class ApiResponseHelperTest {
|
||||
Assert.assertTrue(apiResponseHelper.capacityListingForSingleNonGpuType(List.of(c1, c2)));
|
||||
Assert.assertFalse(apiResponseHelper.capacityListingForSingleNonGpuType(List.of(c1, c2, c3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateGuestOSCategoryResponse_WithResourceIcon() {
|
||||
GuestOsCategory guestOsCategory = Mockito.mock(GuestOsCategory.class);
|
||||
ResourceIconVO resourceIconVO = Mockito.mock(ResourceIconVO.class);
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String name = "Ubuntu";
|
||||
boolean featured = true;
|
||||
Mockito.when(guestOsCategory.getUuid()).thenReturn(uuid);
|
||||
Mockito.when(guestOsCategory.getName()).thenReturn(name);
|
||||
Mockito.when(guestOsCategory.isFeatured()).thenReturn(featured);
|
||||
ResourceIconResponse mockIconResponse = Mockito.mock(ResourceIconResponse.class);
|
||||
try (MockedStatic<ApiDBUtils> ignored = Mockito.mockStatic(ApiDBUtils.class)) {
|
||||
Mockito.when(ApiDBUtils.getResourceIconByResourceUUID(uuid, ResourceTag.ResourceObjectType.GuestOsCategory)).thenReturn(resourceIconVO);
|
||||
Mockito.when(ApiDBUtils.newResourceIconResponse(resourceIconVO)).thenReturn(mockIconResponse);
|
||||
GuestOSCategoryResponse response = apiResponseHelper.createGuestOSCategoryResponse(guestOsCategory);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(uuid, response.getId());
|
||||
Assert.assertEquals(name, response.getName());
|
||||
Object obj = ReflectionTestUtils.getField(response, "featured");
|
||||
if (obj == null) {
|
||||
Assert.fail("Invalid featured value");
|
||||
}
|
||||
Assert.assertTrue((Boolean)obj);
|
||||
obj = ReflectionTestUtils.getField(response, "resourceIconResponse");
|
||||
Assert.assertNotNull(obj);
|
||||
Assert.assertEquals("oscategory", response.getObjectName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateGuestOSCategoryResponse_WithoutResourceIcon() {
|
||||
GuestOsCategory guestOsCategory = Mockito.mock(GuestOsCategory.class);
|
||||
String uuid = "1234";
|
||||
String name = "Ubuntu";
|
||||
boolean featured = false;
|
||||
Mockito.when(guestOsCategory.getUuid()).thenReturn(uuid);
|
||||
Mockito.when(guestOsCategory.getName()).thenReturn(name);
|
||||
Mockito.when(guestOsCategory.isFeatured()).thenReturn(featured);
|
||||
try (MockedStatic<ApiDBUtils> ignored = Mockito.mockStatic(ApiDBUtils.class)) {
|
||||
when(ApiDBUtils.getResourceIconByResourceUUID(uuid, ResourceTag.ResourceObjectType.GuestOsCategory)).thenReturn(null);
|
||||
GuestOSCategoryResponse response = apiResponseHelper.createGuestOSCategoryResponse(guestOsCategory);
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals(uuid, response.getId());
|
||||
Assert.assertEquals(name, response.getName());
|
||||
Object obj = ReflectionTestUtils.getField(response, "featured");
|
||||
if (obj == null) {
|
||||
Assert.fail("Invalid featured value");
|
||||
}
|
||||
Assert.assertFalse((Boolean)obj);
|
||||
obj = ReflectionTestUtils.getField(response, "resourceIconResponse");
|
||||
Assert.assertNull(obj);
|
||||
Assert.assertEquals("oscategory", response.getObjectName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateGuestOSCategoryResponse_WithShowIconFalse() {
|
||||
GuestOsCategory guestOsCategory = Mockito.mock(GuestOsCategory.class);
|
||||
Mockito.when(guestOsCategory.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
try (MockedStatic<ApiDBUtils> mockedStatic = Mockito.mockStatic(ApiDBUtils.class)) {
|
||||
GuestOSCategoryResponse response = apiResponseHelper.createGuestOSCategoryResponse(guestOsCategory, false);
|
||||
Assert.assertNotNull(response);
|
||||
mockedStatic.verify(() -> ApiDBUtils.getResourceIconByResourceUUID(Mockito.any(), Mockito.any()),
|
||||
Mockito.never());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsUsingOsCategory_withValidData() {
|
||||
TemplateResponse template1 = Mockito.mock(TemplateResponse.class);
|
||||
when(template1.getId()).thenReturn("t1");
|
||||
when(template1.getOsTypeCategoryId()).thenReturn(100L);
|
||||
TemplateResponse template2 = Mockito.mock(TemplateResponse.class);
|
||||
when(template2.getId()).thenReturn("t2");
|
||||
when(template2.getOsTypeCategoryId()).thenReturn(200L);
|
||||
List<TemplateResponse> responses = Arrays.asList(template1, template2);
|
||||
Map<Long, ResourceIcon> icons = new HashMap<>();
|
||||
ResourceIcon icon1 = Mockito.mock(ResourceIcon.class);
|
||||
ResourceIcon icon2 = Mockito.mock(ResourceIcon.class);
|
||||
icons.put(100L, icon1);
|
||||
icons.put(200L, icon2);
|
||||
when(resourceIconManager.getByResourceTypeAndIds(Mockito.eq(ResourceTag.ResourceObjectType.GuestOsCategory), Mockito.anySet()))
|
||||
.thenReturn(icons);
|
||||
Map<String, ResourceIcon> result = apiResponseHelper.getResourceIconsUsingOsCategory(responses);
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(icon1, result.get("t1"));
|
||||
assertEquals(icon2, result.get("t2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceIconsUsingOsCategory_missingIcons() {
|
||||
TemplateResponse template1 = Mockito.mock(TemplateResponse.class);
|
||||
when(template1.getId()).thenReturn("t1");
|
||||
when(template1.getOsTypeCategoryId()).thenReturn(100L);
|
||||
List<TemplateResponse> responses = List.of(template1);
|
||||
when(resourceIconManager.getByResourceTypeAndIds(Mockito.eq(ResourceTag.ResourceObjectType.GuestOsCategory), Mockito.anySet())).thenReturn(Collections.emptyMap());
|
||||
Map<String, ResourceIcon> result = apiResponseHelper.getResourceIconsUsingOsCategory(responses);
|
||||
assertTrue(result.containsKey("t1"));
|
||||
assertNull(result.get("t1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTemplateIsoResponsesForIcons_withMixedIcons() {
|
||||
TemplateResponse template1 = Mockito.mock(TemplateResponse.class);
|
||||
when(template1.getId()).thenReturn("t1");
|
||||
TemplateResponse template2 = Mockito.mock(TemplateResponse.class);
|
||||
when(template2.getId()).thenReturn("t2");
|
||||
List<TemplateResponse> responses = Arrays.asList(template1, template2);
|
||||
Map<String, ResourceIcon> isoIcons = new HashMap<>();
|
||||
isoIcons.put("t1", Mockito.mock(ResourceIcon.class));
|
||||
when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, Set.of("t1", "t2")))
|
||||
.thenReturn(isoIcons);
|
||||
Map<String, ResourceIcon> fallbackIcons = Map.of("t2", Mockito.mock(ResourceIcon.class));
|
||||
Mockito.doReturn(fallbackIcons).when(apiResponseHelper).getResourceIconsUsingOsCategory(Mockito.anyList());
|
||||
ResourceIconResponse iconResponse1 = new ResourceIconResponse();
|
||||
ResourceIconResponse iconResponse2 = new ResourceIconResponse();
|
||||
Mockito.doReturn(iconResponse1).when(apiResponseHelper).createResourceIconResponse(isoIcons.get("t1"));
|
||||
Mockito.doReturn(iconResponse2).when(apiResponseHelper).createResourceIconResponse(fallbackIcons.get("t2"));
|
||||
apiResponseHelper.updateTemplateIsoResponsesForIcons(responses, ResourceTag.ResourceObjectType.ISO);
|
||||
verify(template1).setResourceIconResponse(iconResponse1);
|
||||
verify(template2).setResourceIconResponse(iconResponse2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTemplateIsoResponsesForIcons_emptyInput() {
|
||||
apiResponseHelper.updateTemplateIsoResponsesForIcons(Collections.emptyList(),
|
||||
ResourceTag.ResourceObjectType.Template);
|
||||
Mockito.verify(resourceIconManager, Mockito.never()).getByResourceTypeAndUuids(Mockito.any(),
|
||||
Mockito.anyCollection());
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,56 @@
|
||||
// under the License.
|
||||
package com.cloud.server;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd;
|
||||
import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd;
|
||||
import org.apache.cloudstack.api.command.user.guest.ListGuestOsCategoriesCmd;
|
||||
import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.framework.config.ConfigDepot;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
|
||||
import org.apache.cloudstack.userdata.UserDataManager;
|
||||
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.mockito.stubbing.Answer;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.cloud.cpu.CPU;
|
||||
import com.cloud.dc.Vlan.VlanType;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
@ -26,7 +76,12 @@ import com.cloud.host.dao.HostDetailsDao;
|
||||
import com.cloud.network.IpAddress;
|
||||
import com.cloud.network.IpAddressManagerImpl;
|
||||
import com.cloud.network.dao.IPAddressVO;
|
||||
import com.cloud.storage.GuestOSCategoryVO;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import com.cloud.storage.GuestOsCategory;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.dao.GuestOSCategoryDao;
|
||||
import com.cloud.storage.dao.GuestOSDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
@ -46,46 +101,6 @@ import com.cloud.vm.UserVmDetailVO;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
|
||||
import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd;
|
||||
import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.framework.config.ConfigDepot;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
|
||||
import org.apache.cloudstack.userdata.UserDataManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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 java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ManagementServerImplTest {
|
||||
@ -118,7 +133,7 @@ public class ManagementServerImplTest {
|
||||
UserDataDao _userDataDao;
|
||||
|
||||
@Mock
|
||||
VMTemplateDao _templateDao;
|
||||
VMTemplateDao templateDao;
|
||||
|
||||
@Mock
|
||||
AnnotationDao annotationDao;
|
||||
@ -129,9 +144,6 @@ public class ManagementServerImplTest {
|
||||
@Mock
|
||||
UserDataManager userDataManager;
|
||||
|
||||
@Spy
|
||||
ManagementServerImpl spy = new ManagementServerImpl();
|
||||
|
||||
@Mock
|
||||
UserVmDetailsDao userVmDetailsDao;
|
||||
|
||||
@ -147,23 +159,22 @@ public class ManagementServerImplTest {
|
||||
@Mock
|
||||
DomainDao domainDao;
|
||||
|
||||
@Mock
|
||||
GuestOSCategoryDao _guestOSCategoryDao;
|
||||
|
||||
@Mock
|
||||
GuestOSDao _guestOSDao;
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
ManagementServerImpl spy = new ManagementServerImpl();
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
@Before
|
||||
public void setup() throws IllegalAccessException, NoSuchFieldException {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
|
||||
spy._accountMgr = _accountMgr;
|
||||
spy.userDataDao = _userDataDao;
|
||||
spy.templateDao = _templateDao;
|
||||
spy._userVmDao = _userVmDao;
|
||||
spy.annotationDao = annotationDao;
|
||||
spy._UserVmDetailsDao = userVmDetailsDao;
|
||||
spy._detailsDao = hostDetailsDao;
|
||||
spy.userDataManager = userDataManager;
|
||||
spy._configDao = configDao;
|
||||
spy._configDepot = configDepot;
|
||||
spy._domainDao = domainDao;
|
||||
}
|
||||
|
||||
@After
|
||||
@ -407,7 +418,7 @@ public class ManagementServerImplTest {
|
||||
|
||||
Mockito.when(userData.getId()).thenReturn(1L);
|
||||
when(_userDataDao.findById(1L)).thenReturn(userData);
|
||||
when(_templateDao.findTemplatesLinkedToUserdata(1L)).thenReturn(new ArrayList<VMTemplateVO>());
|
||||
when(templateDao.findTemplatesLinkedToUserdata(1L)).thenReturn(new ArrayList<VMTemplateVO>());
|
||||
when(_userVmDao.findByUserDataId(1L)).thenReturn(new ArrayList<UserVmVO>());
|
||||
when(_userDataDao.remove(1L)).thenReturn(true);
|
||||
|
||||
@ -437,7 +448,7 @@ public class ManagementServerImplTest {
|
||||
VMTemplateVO vmTemplateVO = Mockito.mock(VMTemplateVO.class);
|
||||
List<VMTemplateVO> linkedTemplates = new ArrayList<>();
|
||||
linkedTemplates.add(vmTemplateVO);
|
||||
when(_templateDao.findTemplatesLinkedToUserdata(1L)).thenReturn(linkedTemplates);
|
||||
when(templateDao.findTemplatesLinkedToUserdata(1L)).thenReturn(linkedTemplates);
|
||||
|
||||
spy.deleteUserData(cmd);
|
||||
}
|
||||
@ -461,7 +472,7 @@ public class ManagementServerImplTest {
|
||||
Mockito.when(userData.getId()).thenReturn(1L);
|
||||
when(_userDataDao.findById(1L)).thenReturn(userData);
|
||||
|
||||
when(_templateDao.findTemplatesLinkedToUserdata(1L)).thenReturn(new ArrayList<VMTemplateVO>());
|
||||
when(templateDao.findTemplatesLinkedToUserdata(1L)).thenReturn(new ArrayList<VMTemplateVO>());
|
||||
|
||||
UserVmVO userVmVO = Mockito.mock(UserVmVO.class);
|
||||
List<UserVmVO> vms = new ArrayList<>();
|
||||
@ -740,4 +751,272 @@ public class ManagementServerImplTest {
|
||||
|
||||
Assert.assertEquals("0.85", result.first().get(0).getValue());
|
||||
}
|
||||
@Test
|
||||
public void testAddGuestOsCategory() {
|
||||
AddGuestOsCategoryCmd addCmd = Mockito.mock(AddGuestOsCategoryCmd.class);
|
||||
String name = "Ubuntu";
|
||||
boolean featured = true;
|
||||
Mockito.when(addCmd.getName()).thenReturn(name);
|
||||
Mockito.when(addCmd.isFeatured()).thenReturn(featured);
|
||||
Mockito.doAnswer((Answer<GuestOSCategoryVO>) invocation -> (GuestOSCategoryVO)invocation.getArguments()[0]).when(_guestOSCategoryDao).persist(Mockito.any(GuestOSCategoryVO.class));
|
||||
GuestOsCategory result = spy.addGuestOsCategory(addCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(name, result.getName());
|
||||
Assert.assertEquals(featured, result.isFeatured());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).persist(any(GuestOSCategoryVO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGuestOsCategory() {
|
||||
UpdateGuestOsCategoryCmd updateCmd = Mockito.mock(UpdateGuestOsCategoryCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = new GuestOSCategoryVO("Old name", false);
|
||||
long id = 1L;
|
||||
String name = "Updated Name";
|
||||
Boolean featured = true;
|
||||
Integer sortKey = 10;
|
||||
Mockito.when(updateCmd.getId()).thenReturn(id);
|
||||
Mockito.when(updateCmd.getName()).thenReturn(name);
|
||||
Mockito.when(updateCmd.isFeatured()).thenReturn(featured);
|
||||
Mockito.when(updateCmd.getSortKey()).thenReturn(sortKey);
|
||||
Mockito.when(_guestOSCategoryDao.findById(id)).thenReturn(guestOSCategory);
|
||||
Mockito.when(_guestOSCategoryDao.update(Mockito.eq(id), any(GuestOSCategoryVO.class))).thenReturn(true);
|
||||
GuestOsCategory result = spy.updateGuestOsCategory(updateCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(name, result.getName());
|
||||
Assert.assertEquals(featured, result.isFeatured());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).findById(id);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).update(Mockito.eq(id), any(GuestOSCategoryVO.class));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testUpdateGuestOsCategory_ThrowsExceptionWhenCategoryNotFound() {
|
||||
UpdateGuestOsCategoryCmd updateCmd = Mockito.mock(UpdateGuestOsCategoryCmd.class);
|
||||
long id = 1L;
|
||||
when(updateCmd.getId()).thenReturn(id);
|
||||
when(_guestOSCategoryDao.findById(id)).thenReturn(null);
|
||||
spy.updateGuestOsCategory(updateCmd);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGuestOsCategory_NoChanges() {
|
||||
UpdateGuestOsCategoryCmd updateCmd = Mockito.mock(UpdateGuestOsCategoryCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = new GuestOSCategoryVO("Old name", false);
|
||||
long id = 1L;
|
||||
when(updateCmd.getId()).thenReturn(id);
|
||||
when(updateCmd.getName()).thenReturn(null);
|
||||
when(updateCmd.isFeatured()).thenReturn(null);
|
||||
when(updateCmd.getSortKey()).thenReturn(null);
|
||||
when(_guestOSCategoryDao.findById(id)).thenReturn(guestOSCategory);
|
||||
GuestOsCategory result = spy.updateGuestOsCategory(updateCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertNotNull(result.getName());
|
||||
Assert.assertFalse(result.isFeatured());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).findById(id);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.never()).update(Mockito.eq(id), any(GuestOSCategoryVO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGuestOsCategory_UpdateNameOnly() {
|
||||
UpdateGuestOsCategoryCmd updateCmd = Mockito.mock(UpdateGuestOsCategoryCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = new GuestOSCategoryVO("Old name", false);
|
||||
long id = 1L;
|
||||
String name = "Updated Name";
|
||||
Mockito.when(updateCmd.getId()).thenReturn(id);
|
||||
Mockito.when(updateCmd.getName()).thenReturn(name);
|
||||
Mockito.when(updateCmd.isFeatured()).thenReturn(null);
|
||||
Mockito.when(updateCmd.getSortKey()).thenReturn(null);
|
||||
Mockito.when(_guestOSCategoryDao.findById(id)).thenReturn(guestOSCategory);
|
||||
Mockito.when(_guestOSCategoryDao.update(Mockito.eq(id), any(GuestOSCategoryVO.class))).thenReturn(true);
|
||||
GuestOsCategory result = spy.updateGuestOsCategory(updateCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(name, result.getName());
|
||||
Assert.assertFalse(result.isFeatured());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).findById(id);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).update(Mockito.eq(id), any(GuestOSCategoryVO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteGuestOsCategory_Successful() {
|
||||
DeleteGuestOsCategoryCmd deleteCmd = Mockito.mock(DeleteGuestOsCategoryCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = Mockito.mock(GuestOSCategoryVO.class);
|
||||
long id = 1L;
|
||||
Mockito.when(deleteCmd.getId()).thenReturn(id);
|
||||
Mockito.when(_guestOSCategoryDao.findById(id)).thenReturn(guestOSCategory);
|
||||
Mockito.when(_guestOSDao.listIdsByCategoryId(id)).thenReturn(Arrays.asList());
|
||||
Mockito.when(_guestOSCategoryDao.remove(id)).thenReturn(true);
|
||||
boolean result = spy.deleteGuestOsCategory(deleteCmd);
|
||||
Assert.assertTrue(result);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).findById(id);
|
||||
Mockito.verify(_guestOSDao, Mockito.times(1)).listIdsByCategoryId(id);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).remove(id);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDeleteGuestOsCategory_ThrowsExceptionWhenCategoryNotFound() {
|
||||
DeleteGuestOsCategoryCmd deleteCmd = Mockito.mock(DeleteGuestOsCategoryCmd.class);
|
||||
long id = 1L;
|
||||
Mockito.when(deleteCmd.getId()).thenReturn(id);
|
||||
Mockito.when(_guestOSCategoryDao.findById(id)).thenReturn(null);
|
||||
spy.deleteGuestOsCategory(deleteCmd);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testDeleteGuestOsCategory_ThrowsExceptionWhenGuestOsExists() {
|
||||
DeleteGuestOsCategoryCmd deleteCmd = Mockito.mock(DeleteGuestOsCategoryCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = Mockito.mock(GuestOSCategoryVO.class);
|
||||
long id = 1L;
|
||||
Mockito.when(deleteCmd.getId()).thenReturn(id);
|
||||
Mockito.when(_guestOSCategoryDao.findById(id)).thenReturn(guestOSCategory);
|
||||
Mockito.when(_guestOSDao.listIdsByCategoryId(id)).thenReturn(Arrays.asList(1L));
|
||||
spy.deleteGuestOsCategory(deleteCmd);
|
||||
}
|
||||
|
||||
private void mockGuestOsJoin() {
|
||||
GuestOSVO vo = mock(GuestOSVO.class);
|
||||
SearchBuilder<GuestOSVO> sb = mock(SearchBuilder.class);
|
||||
when(sb.entity()).thenReturn(vo);
|
||||
when(_guestOSDao.createSearchBuilder()).thenReturn(sb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListGuestOSCategoriesByCriteria_Success() {
|
||||
ListGuestOsCategoriesCmd listCmd = Mockito.mock(ListGuestOsCategoriesCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = Mockito.mock(GuestOSCategoryVO.class);
|
||||
Filter filter = Mockito.mock(Filter.class);
|
||||
Long id = 1L;
|
||||
String name = "Ubuntu";
|
||||
String keyword = "Linux";
|
||||
Boolean featured = true;
|
||||
Long zoneId = 1L;
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
Boolean isIso = true;
|
||||
Boolean isVnf = false;
|
||||
Mockito.when(listCmd.getId()).thenReturn(id);
|
||||
Mockito.when(listCmd.getName()).thenReturn(name);
|
||||
Mockito.when(listCmd.getKeyword()).thenReturn(keyword);
|
||||
Mockito.when(listCmd.isFeatured()).thenReturn(featured);
|
||||
Mockito.when(listCmd.getZoneId()).thenReturn(zoneId);
|
||||
Mockito.when(listCmd.getArch()).thenReturn(arch);
|
||||
Mockito.when(listCmd.isIso()).thenReturn(isIso);
|
||||
Mockito.when(listCmd.isVnf()).thenReturn(isVnf);
|
||||
SearchBuilder<GuestOSCategoryVO> searchBuilder = Mockito.mock(SearchBuilder.class);
|
||||
Mockito.when(searchBuilder.entity()).thenReturn(guestOSCategory);
|
||||
SearchCriteria<GuestOSCategoryVO> searchCriteria = Mockito.mock(SearchCriteria.class);
|
||||
Mockito.when(_guestOSCategoryDao.createSearchBuilder()).thenReturn(searchBuilder);
|
||||
Mockito.when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
Mockito.when(templateDao.listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf)).thenReturn(Arrays.asList(1L, 2L));
|
||||
Pair<List<GuestOSCategoryVO>, Integer> mockResult = new Pair<>(Arrays.asList(guestOSCategory), 1);
|
||||
mockGuestOsJoin();
|
||||
Mockito.when(_guestOSCategoryDao.searchAndCount(Mockito.eq(searchCriteria), Mockito.any())).thenReturn(mockResult);
|
||||
Pair<List<? extends GuestOsCategory>, Integer> result = spy.listGuestOSCategoriesByCriteria(listCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(1, result.second().intValue());
|
||||
Assert.assertEquals(1, result.first().size());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).createSearchBuilder();
|
||||
Mockito.verify(templateDao, Mockito.times(1)).listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).searchAndCount(Mockito.eq(searchCriteria), Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListGuestOSCategoriesByCriteria_NoResults() {
|
||||
ListGuestOsCategoriesCmd listCmd = Mockito.mock(ListGuestOsCategoriesCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = Mockito.mock(GuestOSCategoryVO.class);
|
||||
Long id = 1L;
|
||||
String name = "CentOS";
|
||||
String keyword = "Linux";
|
||||
Boolean featured = false;
|
||||
Long zoneId = 1L;
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
Boolean isIso = false;
|
||||
Boolean isVnf = false;
|
||||
Mockito.when(listCmd.getId()).thenReturn(id);
|
||||
Mockito.when(listCmd.getName()).thenReturn(name);
|
||||
Mockito.when(listCmd.getKeyword()).thenReturn(keyword);
|
||||
Mockito.when(listCmd.isFeatured()).thenReturn(featured);
|
||||
Mockito.when(listCmd.getZoneId()).thenReturn(zoneId);
|
||||
Mockito.when(listCmd.getArch()).thenReturn(arch);
|
||||
Mockito.when(listCmd.isIso()).thenReturn(isIso);
|
||||
Mockito.when(listCmd.isVnf()).thenReturn(isVnf);
|
||||
SearchBuilder<GuestOSCategoryVO> searchBuilder = Mockito.mock(SearchBuilder.class);
|
||||
Mockito.when(searchBuilder.entity()).thenReturn(guestOSCategory);
|
||||
SearchCriteria<GuestOSCategoryVO> searchCriteria = Mockito.mock(SearchCriteria.class);
|
||||
Mockito.when(_guestOSCategoryDao.createSearchBuilder()).thenReturn(searchBuilder);
|
||||
Mockito.when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
Mockito.when(templateDao.listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf)).thenReturn(Arrays.asList(1L, 2L));
|
||||
Pair<List<GuestOSCategoryVO>, Integer> mockResult = new Pair<>(Arrays.asList(), 0);
|
||||
Mockito.when(_guestOSCategoryDao.searchAndCount(Mockito.eq(searchCriteria), Mockito.any())).thenReturn(mockResult);
|
||||
mockGuestOsJoin();
|
||||
Pair<List<? extends GuestOsCategory>, Integer> result = spy.listGuestOSCategoriesByCriteria(listCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(0, result.second().intValue());
|
||||
Assert.assertEquals(0, result.first().size());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).createSearchBuilder();
|
||||
Mockito.verify(templateDao, Mockito.times(1)).listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).searchAndCount(Mockito.eq(searchCriteria), Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListGuestOSCategoriesByCriteria_NoGuestOsIdsFound() {
|
||||
ListGuestOsCategoriesCmd listCmd = Mockito.mock(ListGuestOsCategoriesCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = Mockito.mock(GuestOSCategoryVO.class);
|
||||
Long id = 1L;
|
||||
String name = "Ubuntu";
|
||||
String keyword = "Linux";
|
||||
Boolean featured = true;
|
||||
Long zoneId = 1L;
|
||||
CPU.CPUArch arch = CPU.CPUArch.getDefault();
|
||||
Boolean isIso = true;
|
||||
Boolean isVnf = false;
|
||||
Mockito.when(listCmd.getId()).thenReturn(id);
|
||||
Mockito.when(listCmd.getName()).thenReturn(name);
|
||||
Mockito.when(listCmd.getKeyword()).thenReturn(keyword);
|
||||
Mockito.when(listCmd.isFeatured()).thenReturn(featured);
|
||||
Mockito.when(listCmd.getZoneId()).thenReturn(zoneId);
|
||||
Mockito.when(listCmd.getArch()).thenReturn(arch);
|
||||
Mockito.when(listCmd.isIso()).thenReturn(isIso);
|
||||
Mockito.when(listCmd.isVnf()).thenReturn(isVnf);
|
||||
SearchBuilder<GuestOSCategoryVO> searchBuilder = Mockito.mock(SearchBuilder.class);
|
||||
Mockito.when(searchBuilder.entity()).thenReturn(guestOSCategory);
|
||||
SearchCriteria<GuestOSCategoryVO> searchCriteria = Mockito.mock(SearchCriteria.class);
|
||||
Mockito.when(_guestOSCategoryDao.createSearchBuilder()).thenReturn(searchBuilder);
|
||||
Mockito.when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
Mockito.when(templateDao.listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf)).thenReturn(Arrays.asList(1L, 2L));
|
||||
Pair<List<GuestOSCategoryVO>, Integer> mockResult = new Pair<>(Arrays.asList(), 0);
|
||||
when(_guestOSCategoryDao.searchAndCount(Mockito.eq(searchCriteria), Mockito.any())).thenReturn(mockResult);
|
||||
mockGuestOsJoin();
|
||||
Pair<List<? extends GuestOsCategory>, Integer> result = spy.listGuestOSCategoriesByCriteria(listCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(0, result.second().intValue());
|
||||
Assert.assertEquals(0, result.first().size());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).createSearchBuilder();
|
||||
Mockito.verify(templateDao, Mockito.times(1)).listTemplateIsoByArchVnfAndZone(zoneId, arch, isIso, isVnf);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).searchAndCount(Mockito.eq(searchCriteria), Mockito.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListGuestOSCategoriesByCriteria_FilterById() {
|
||||
ListGuestOsCategoriesCmd listCmd = Mockito.mock(ListGuestOsCategoriesCmd.class);
|
||||
GuestOSCategoryVO guestOSCategory = Mockito.mock(GuestOSCategoryVO.class);
|
||||
Long id = 1L;
|
||||
Mockito.when(listCmd.getId()).thenReturn(id);
|
||||
Mockito.when(listCmd.getZoneId()).thenReturn(null);
|
||||
Mockito.when(listCmd.isIso()).thenReturn(null);
|
||||
Mockito.when(listCmd.isVnf()).thenReturn(null);
|
||||
SearchBuilder<GuestOSCategoryVO> searchBuilder = Mockito.mock(SearchBuilder.class);
|
||||
Mockito.when(searchBuilder.entity()).thenReturn(guestOSCategory);
|
||||
SearchCriteria<GuestOSCategoryVO> searchCriteria = Mockito.mock(SearchCriteria.class);
|
||||
Mockito.when(_guestOSCategoryDao.createSearchBuilder()).thenReturn(searchBuilder);
|
||||
Mockito.when(searchBuilder.create()).thenReturn(searchCriteria);
|
||||
Pair<List<GuestOSCategoryVO>, Integer> mockResult = new Pair<>(Arrays.asList(guestOSCategory), 1);
|
||||
Mockito.when(_guestOSCategoryDao.searchAndCount(Mockito.eq(searchCriteria), Mockito.any())).thenReturn(mockResult);
|
||||
mockGuestOsJoin();
|
||||
Pair<List<? extends GuestOsCategory>, Integer> result = spy.listGuestOSCategoriesByCriteria(listCmd);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(1, result.second().intValue());
|
||||
Assert.assertEquals(1, result.first().size());
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).createSearchBuilder();
|
||||
Mockito.verify(searchCriteria, Mockito.times(1)).setParameters("id", id);
|
||||
Mockito.verify(_guestOSCategoryDao, Mockito.times(1)).searchAndCount(Mockito.eq(searchCriteria), Mockito.any());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
3
ui/public/config.json
vendored
3
ui/public/config.json
vendored
@ -97,5 +97,8 @@
|
||||
"basicZoneEnabled": true,
|
||||
"multipleServer": false,
|
||||
"allowSettingTheme": true,
|
||||
"imageSelectionInterface": "modern",
|
||||
"showUserCategoryForModernImageSelection": true,
|
||||
"showAllCategoryForModernImageSelection": false,
|
||||
"docHelpMappings": {}
|
||||
}
|
||||
|
||||
@ -87,8 +87,9 @@
|
||||
"label.action.delete.egress.firewall": "Delete egress firewall rule",
|
||||
"label.action.delete.firewall": "Delete firewall rule",
|
||||
"label.action.delete.interface.static.route": "Remove Tungsten Fabric interface static route",
|
||||
"label.action.delete.guest.os": "Delete guest os",
|
||||
"label.action.delete.guest.os.hypervisor.mapping": "Delete guest os hypervisor mapping",
|
||||
"label.action.delete.guest.os": "Delete guest OS",
|
||||
"label.action.delete.guest.os.category": "Delete guest OS category",
|
||||
"label.action.delete.guest.os.hypervisor.mapping": "Delete guest OS hypervisor mapping",
|
||||
"label.action.delete.ip.range": "Delete IP range",
|
||||
"label.action.delete.iso": "Delete ISO",
|
||||
"label.action.delete.load.balancer": "Delete load balancer rule",
|
||||
@ -263,8 +264,9 @@
|
||||
"label.add.firewall": "Add firewall rule",
|
||||
"label.add.firewallrule": "Add Firewall Rule",
|
||||
"label.add.guest.network": "Add guest Network",
|
||||
"label.add.guest.os": "Add guest os",
|
||||
"label.add.guest.os.hypervisor.mapping": "Add guest os hypervisor mapping",
|
||||
"label.add.guest.os": "Add guest OS",
|
||||
"label.add.guest.os.category": "Add guest OS category",
|
||||
"label.add.guest.os.hypervisor.mapping": "Add guest OS hypervisor mapping",
|
||||
"label.add.host": "Add host",
|
||||
"label.add.ingress.rule": "Add ingress rule",
|
||||
"label.add.intermediate.certificate": "Add intermediate certificate",
|
||||
@ -1069,6 +1071,8 @@
|
||||
"label.guest.netmask": "Guest netmask",
|
||||
"label.guest.networks": "Guest Networks",
|
||||
"label.guest.os": "Guest OS",
|
||||
"label.guest.os.category": "Guest OS Category",
|
||||
"label.guest.os.categories": "Guest OS Categories",
|
||||
"label.guest.os.hypervisor.mappings": "Guest OS mappings",
|
||||
"label.guest.start.ip": "Guest start IP",
|
||||
"label.guest.traffic": "Guest traffic",
|
||||
@ -1143,6 +1147,8 @@
|
||||
"label.ikelifetime": "IKE lifetime (second)",
|
||||
"label.ikepolicy": "IKE policy",
|
||||
"label.ikeversion": "IKE version",
|
||||
"label.image": "Image",
|
||||
"label.image.type": "Image type",
|
||||
"label.images": "Images",
|
||||
"label.imagestoreid": "Secondary Storage",
|
||||
"label.import.backup.offering": "Import backup offering",
|
||||
@ -1651,6 +1657,7 @@
|
||||
"label.operator.equal": "Equals to",
|
||||
"label.optional": "Optional",
|
||||
"label.order": "Order",
|
||||
"label.os": "Operating System",
|
||||
"label.oscategoryid": "OS category",
|
||||
"label.oscategoryname": "OS category name",
|
||||
"label.osname": "OS name",
|
||||
@ -1806,6 +1813,7 @@
|
||||
"label.provisioningtype.fat": "Fat provisioning",
|
||||
"label.provisioningtype.sparse": "Sparse provisioning",
|
||||
"label.provisioningtype.thin": "Thin provisioning",
|
||||
"label.public": "Public",
|
||||
"label.publicmtu": "Public Interface MTU",
|
||||
"label.public.interface": "Public interface",
|
||||
"label.public.ip": "Public IP address",
|
||||
@ -2305,7 +2313,8 @@
|
||||
"label.tariffvalue": "Tariff value",
|
||||
"label.tcp": "TCP",
|
||||
"label.tcp.proxy": "TCP proxy",
|
||||
"label.template": "Select a template",
|
||||
"label.template": "Template",
|
||||
"label.template.select": "Select a template",
|
||||
"label.templatetag": "Tag",
|
||||
"label.template.select.existing": "Select an existing template",
|
||||
"label.template.temporary.import": "Use a temporary template for import",
|
||||
@ -2698,6 +2707,7 @@
|
||||
"message.action.delete.ingress.rule": "Please confirm that you want to delete this ingress rule.",
|
||||
"message.action.delete.ipv4.subnet": "Please confirm that you want to delete this IPv4 subnet.",
|
||||
"message.action.delete.guest.os": "Please confirm that you want to delete this guest os. System defined entry cannot be deleted.",
|
||||
"message.action.delete.guest.os.category": "Please confirm that you want to delete this guest os category.",
|
||||
"message.action.delete.guest.os.hypervisor.mapping": "Please confirm that you want to delete this guest os hypervisor mapping. System defined entry cannot be deleted.",
|
||||
"message.action.delete.instance.group": "Please confirm that you want to delete the Instance group.",
|
||||
"message.action.delete.interface.static.route": "Please confirm that you want to remove this interface Static Route?",
|
||||
@ -3306,6 +3316,7 @@
|
||||
"message.installwizard.tooltip.tungsten.provider.name": "Tungsten provider name is required",
|
||||
"message.installwizard.tooltip.tungsten.provider.port": "Tungsten provider port is required",
|
||||
"message.installwizard.tooltip.tungsten.provider.vrouterport": "Tungsten provider vrouter port is required",
|
||||
"message.instance.architecture": "Please select Instance architecture",
|
||||
"message.instances.managed": "Instances controlled by CloudStack.",
|
||||
"message.instances.unmanaged": "Instances not controlled by CloudStack.",
|
||||
"message.instances.migrate.vmware": "Instances that can be migrated from VMware.",
|
||||
|
||||
@ -139,6 +139,10 @@ export default {
|
||||
},
|
||||
fetchResourceIcon (id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.$store.getters.avatar) {
|
||||
this.image = this.$store.getters.avatar
|
||||
resolve(this.image)
|
||||
}
|
||||
api('listUsers', {
|
||||
id: id,
|
||||
showicon: true
|
||||
@ -146,6 +150,7 @@ export default {
|
||||
const response = json.listusersresponse.user || []
|
||||
if (response?.[0]) {
|
||||
this.image = response[0]?.icon?.base64image || ''
|
||||
this.$store.commit('SET_AVATAR', this.image)
|
||||
resolve(this.image)
|
||||
}
|
||||
}).catch(error => {
|
||||
|
||||
150
ui/src/components/view/ImageDeployInstanceButton.vue
Normal file
150
ui/src/components/view/ImageDeployInstanceButton.vue
Normal file
@ -0,0 +1,150 @@
|
||||
// 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>
|
||||
<span>
|
||||
<a-dropdown-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
v-if="allowed"
|
||||
:disabled="!zones || zones.length === 0">
|
||||
<rocket-outlined />
|
||||
{{ $t('label.create.vm') }}
|
||||
<template #icon><down-outlined /></template>
|
||||
<template #overlay>
|
||||
<a-menu type="primary" @click="handleDeployInstanceMenu">
|
||||
<a-menu-item v-for="zone in zones" :key="zone.id">
|
||||
<span v-if="zone.icon && zone.icon.base64image">
|
||||
<resource-icon :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zone.name }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
|
||||
export default {
|
||||
name: 'ImageDeployInstanceButton',
|
||||
components: {
|
||||
ResourceIcon
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
osCategoryId: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
emits: ['update-zones'],
|
||||
data () {
|
||||
return {
|
||||
imageApi: 'listTemplates',
|
||||
loading: false,
|
||||
zones: []
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.$route.meta.name === 'iso') {
|
||||
this.imageApi = 'listIsos'
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.fetchData()
|
||||
}, 100)
|
||||
},
|
||||
computed: {
|
||||
allowed () {
|
||||
return (this.$route.meta.name === 'template' ||
|
||||
(this.$route.meta.name === 'iso' && this.resource.bootable))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
fetchData () {
|
||||
this.fetchResourceData()
|
||||
},
|
||||
fetchResourceData () {
|
||||
const params = {}
|
||||
params.id = this.resource.id
|
||||
params.templatefilter = 'executable'
|
||||
params.listall = true
|
||||
params.page = this.page
|
||||
params.pagesize = this.pageSize
|
||||
|
||||
this.dataSource = []
|
||||
this.itemCount = 0
|
||||
this.fetchLoading = true
|
||||
this.zones = []
|
||||
api(this.imageApi, params).then(json => {
|
||||
const imageResponse = json?.[this.imageApi.toLowerCase() + 'response']?.[this.$route.meta.name] || []
|
||||
this.zones = imageResponse.map(i => ({
|
||||
id: i.zoneid,
|
||||
name: i.zonename
|
||||
}))
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
this.loading = false
|
||||
}).finally(() => {
|
||||
if (this.zones.length !== 0) {
|
||||
this.$emit('update-zones', this.zones)
|
||||
}
|
||||
this.fetchZonesForIcon()
|
||||
})
|
||||
},
|
||||
fetchZonesForIcon () {
|
||||
if (!this.zones) {
|
||||
return
|
||||
}
|
||||
const zoneids = this.zones.map(z => z.id)
|
||||
this.loading = true
|
||||
api('listZones', { showicon: true, ids: zoneids.join(',') }).then(json => {
|
||||
this.zones = json.listzonesresponse.zone || []
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
if (this.zones.length !== 0) {
|
||||
this.$emit('update-zones', this.zones)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDeployInstanceMenu (e) {
|
||||
const query = { zoneid: e.key }
|
||||
query[this.$route.meta.name + 'id'] = this.resource.id
|
||||
if (this.resource.arch) {
|
||||
query.arch = this.resource.arch
|
||||
}
|
||||
if (this.osCategoryId) {
|
||||
query.oscategoryid = this.osCategoryId
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/action/deployVirtualMachine',
|
||||
query: query
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -30,11 +30,11 @@
|
||||
<edit-outlined class="upload-icon"/>
|
||||
</div>
|
||||
<slot name="avatar">
|
||||
<span v-if="(resource.icon && resource.icon.base64image || images.template || images.iso || resourceIcon) && !['router', 'systemvm', 'volume'].includes($route.path.split('/')[1])">
|
||||
<resource-icon :image="getImage(resource.icon && resource.icon.base64image || images.template || images.iso || resourceIcon)" size="4x" style="margin-right: 5px"/>
|
||||
<span v-if="resourceIcon && !['router', 'systemvm', 'volume'].includes($route.path.split('/')[1])">
|
||||
<resource-icon :image="resourceIcon" size="4x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<span v-else>
|
||||
<os-logo v-if="resource.ostypeid || resource.ostypename" :osId="resource.ostypeid" :osName="resource.ostypename" size="3x" @update-osname="setResourceOsType"/>
|
||||
<os-logo v-if="resource.ostypeid || resource.ostypename || ['guestoscategory'].includes($route.path.split('/')[1])" :osId="resource.ostypeid" :osName="resource.ostypename || resource.name" size="3x" @update-osname="setResourceOsType"/>
|
||||
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 36px" :icon="$route.meta.icon" />
|
||||
<font-awesome-icon
|
||||
v-else-if="$route.meta.icon && Array.isArray($route.meta.icon)"
|
||||
@ -158,11 +158,16 @@
|
||||
<div class="resource-detail-item" v-if="resource.ostypename && resource.ostypeid">
|
||||
<div class="resource-detail-item__label">{{ $t('label.ostypename') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="resource.icon && resource.icon.base64image || images.template || images.iso">
|
||||
<resource-icon :image="getImage(images.template || images.iso)" size="1x" style="margin-right: 5px"/>
|
||||
<span v-if="images.guestoscategory">
|
||||
<resource-icon :image="images.guestoscategory" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<os-logo v-else :osId="resource.ostypeid" :osName="resource.ostypename" size="lg" style="margin-left: -1px" />
|
||||
<span style="margin-left: 8px">{{ resource.ostypename }}</span>
|
||||
<span style="margin-left: 8px">
|
||||
<router-link v-if="$router.resolve('/guestos/' + resource.ostypeid).matched[0].redirect !== '/exception/404'" :to="{ path: '/guestos/' + resource.ostypeid }">
|
||||
{{ resource.ostypename }}
|
||||
</router-link>
|
||||
<span v-else>{{ resource.ostypename }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.ipaddress">
|
||||
@ -449,7 +454,7 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.project') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="images.project">
|
||||
<resource-icon :image="getImage(images.project)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="images.project" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<project-outlined v-else />
|
||||
<router-link v-if="!isStatic && resource.projectid" :to="{ path: '/project/' + resource.projectid }">{{ resource.project || resource.projectname || resource.projectid }}</router-link>
|
||||
@ -546,7 +551,7 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.vpcname') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="images.vpc">
|
||||
<resource-icon :image="getImage(images.vpc)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="images.vpc" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<deployment-unit-outlined v-else />
|
||||
<router-link :to="{ path: '/vpc/' + resource.vpcid }">{{ resource.vpcname || resource.vpcid }}</router-link>
|
||||
@ -557,7 +562,7 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.aclid') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="images.acl">
|
||||
<resource-icon :image="getImage(images.acl)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="images.acl" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<deployment-unit-outlined v-else />
|
||||
<router-link :to="{ path: '/acllist/' + resource.aclid }">{{ resource.aclname || resource.aclid }}</router-link>
|
||||
@ -578,7 +583,7 @@
|
||||
<div class="resource-detail-item" v-if="resource.templateid">
|
||||
<div class="resource-detail-item__label">{{ resource.templateformat === 'ISO'? $t('label.iso') : $t('label.templatename') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<resource-icon v-if="resource.icon" :image="getImage(resource.icon.base64image)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon v-if="images.template || images.guestoscategory" :image="images.template || images.guestoscategory" size="1x" style="margin-right: 5px"/>
|
||||
<SaveOutlined v-else />
|
||||
<router-link :to="{ path: (resource.templateformat === 'ISO' ? '/iso/' : '/template/') + resource.templateid }">{{ resource.templatedisplaytext || resource.templatename || resource.templateid }} </router-link>
|
||||
</div>
|
||||
@ -586,7 +591,7 @@
|
||||
<div class="resource-detail-item" v-if="resource.isoid">
|
||||
<div class="resource-detail-item__label">{{ $t('label.isoname') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<resource-icon v-if="resource.icon" :image="getImage(resource.icon.base64image)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon v-if="images.iso || (resource.isoid === resource.templateid && images.guestoscategory)" :image="images.iso || images.guestoscategory" size="1x" style="margin-right: 5px"/>
|
||||
<UsbOutlined v-else />
|
||||
<router-link :to="{ path: '/iso/' + resource.isoid }">{{ resource.isodisplaytext || resource.isoname || resource.isoid }} </router-link>
|
||||
</div>
|
||||
@ -677,7 +682,7 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.zone') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="images.zone">
|
||||
<resource-icon :image="getImage(images.zone)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="images.zone" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<global-outlined v-else />
|
||||
<router-link v-if="!isStatic && $router.resolve('/zone/' + resource.zoneid).matched[0].redirect !== '/exception/404'" :to="{ path: '/zone/' + resource.zoneid }">{{ resource.zone || resource.zonename || resource.zoneid }}</router-link>
|
||||
@ -712,7 +717,7 @@
|
||||
<div class="resource-detail-item__label">{{ $t('label.account') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<span v-if="images.account">
|
||||
<resource-icon :image="getImage(images.account)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon :image="images.account" size="1x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<user-outlined v-else />
|
||||
<router-link v-if="!isStatic && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/account', query: { name: resource.account, domainid: resource.domainid } }">{{ resource.account }}</router-link>
|
||||
@ -730,7 +735,7 @@
|
||||
<div class="resource-detail-item" v-if="resource.domainid">
|
||||
<div class="resource-detail-item__label">{{ $t('label.domain') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<resource-icon v-if="images.domain" :image="getImage(images.domain)" size="1x" style="margin-right: 5px"/>
|
||||
<resource-icon v-if="images.domain" :image="images.domain" size="1x" style="margin-right: 5px"/>
|
||||
<block-outlined v-else />
|
||||
<router-link v-if="!isStatic && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + resource.domainid, query: { tab: 'details'} }">{{ resource.domain || resource.domainid }}</router-link>
|
||||
<span v-else>{{ resource.domain || resource.domainid }}</span>
|
||||
@ -788,6 +793,10 @@
|
||||
</a-button>
|
||||
</router-link>
|
||||
</div>
|
||||
<image-deploy-instance-button
|
||||
v-if="'deployVirtualMachine' in $store.getters.apis && ['template', 'iso'].includes($route.meta.name)"
|
||||
:resource="resource"
|
||||
:osCategoryId="osCategoryId" />
|
||||
</div>
|
||||
|
||||
<div class="account-center-tags" v-if="showKeys || resource.apikeyaccess">
|
||||
@ -894,6 +903,7 @@ import UploadResourceIcon from '@/components/view/UploadResourceIcon'
|
||||
import eventBus from '@/config/eventBus'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import ResourceLabel from '@/components/widgets/ResourceLabel'
|
||||
import ImageDeployInstanceButton from '@/components/view/ImageDeployInstanceButton.vue'
|
||||
|
||||
export default {
|
||||
name: 'InfoCard',
|
||||
@ -905,7 +915,8 @@ export default {
|
||||
TooltipButton,
|
||||
UploadResourceIcon,
|
||||
ResourceIcon,
|
||||
ResourceLabel
|
||||
ResourceLabel,
|
||||
ImageDeployInstanceButton
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
@ -951,7 +962,8 @@ export default {
|
||||
network: ''
|
||||
},
|
||||
newResource: {},
|
||||
validLinks: {}
|
||||
validLinks: {},
|
||||
osCategoryId: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -975,9 +987,6 @@ export default {
|
||||
}
|
||||
this.updateResourceAdditionalData()
|
||||
}
|
||||
},
|
||||
async templateIcon () {
|
||||
this.getIcons()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -1007,22 +1016,31 @@ export default {
|
||||
}
|
||||
return [this.resource.keypairs.toString()]
|
||||
},
|
||||
templateIcon () {
|
||||
return this.resource.templateid
|
||||
isResourceShowingParentResourceIcon () {
|
||||
const resourcesShowParentResourceIcon = ['guestos']
|
||||
const routeName = this.$route.path.split('/')?.[1] || null
|
||||
if (!routeName) {
|
||||
return false
|
||||
}
|
||||
return resourcesShowParentResourceIcon.includes(routeName)
|
||||
},
|
||||
resourceIcon () {
|
||||
if (this.$showIcon()) {
|
||||
if (!this.$showIcon() && !this.isResourceShowingParentResourceIcon) {
|
||||
return null
|
||||
}
|
||||
if (this.resource?.icon?.base64image) {
|
||||
return this.resource.icon.base64image
|
||||
}
|
||||
if (this.resource?.resourceIcon?.base64image) {
|
||||
return this.resource.resourceIcon.base64image
|
||||
}
|
||||
}
|
||||
return null
|
||||
return this.images.template || this.images.iso || this.images.guestoscategory || null
|
||||
},
|
||||
routeFromResourceType () {
|
||||
return this.$getRouteFromResourceType(this.resource.resourcetype)
|
||||
},
|
||||
isModernImageSelection () {
|
||||
return this.$config.imageSelectionInterface === undefined || this.$config.imageSelectionInterface === 'modern'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -1037,6 +1055,15 @@ export default {
|
||||
this.getTags()
|
||||
}
|
||||
}
|
||||
const osId = this.resource.guestosid || this.resource.ostypeid
|
||||
if (osId && 'listOsTypes' in this.$store.getters.apis) {
|
||||
api('listOsTypes', { id: osId }).then(json => {
|
||||
this.osCategoryId = json?.listostypesresponse?.ostype?.[0]?.oscategoryid || null
|
||||
if (this.osCategoryId) {
|
||||
this.fetchResourceIcon(this.osCategoryId, 'guestoscategory')
|
||||
}
|
||||
})
|
||||
}
|
||||
this.getIcons()
|
||||
},
|
||||
showUploadModal (show) {
|
||||
@ -1048,43 +1075,34 @@ export default {
|
||||
this.showUpload = false
|
||||
}
|
||||
},
|
||||
getImage (image) {
|
||||
return (image || this.resource?.icon?.base64image)
|
||||
},
|
||||
async getIcons () {
|
||||
this.images = {
|
||||
zone: '',
|
||||
template: '',
|
||||
iso: '',
|
||||
domain: '',
|
||||
account: '',
|
||||
project: '',
|
||||
vpc: '',
|
||||
network: ''
|
||||
}
|
||||
getIcons () {
|
||||
this.images = {}
|
||||
if (this.resource.templateid) {
|
||||
await this.fetchResourceIcon(this.resource.templateid, 'template')
|
||||
this.fetchResourceIcon(this.resource.templateid, 'template')
|
||||
}
|
||||
if (this.resource.isoid) {
|
||||
await this.fetchResourceIcon(this.resource.isoid, 'iso')
|
||||
this.fetchResourceIcon(this.resource.isoid, 'iso')
|
||||
}
|
||||
if (this.resource.zoneid) {
|
||||
await this.fetchResourceIcon(this.resource.zoneid, 'zone')
|
||||
this.fetchResourceIcon(this.resource.zoneid, 'zone')
|
||||
}
|
||||
if (this.resource.domainid) {
|
||||
await this.fetchResourceIcon(this.resource.domainid, 'domain')
|
||||
this.fetchResourceIcon(this.resource.domainid, 'domain')
|
||||
}
|
||||
if (this.resource.account) {
|
||||
await this.fetchAccount()
|
||||
this.fetchAccount()
|
||||
}
|
||||
if (this.resource.projectid) {
|
||||
await this.fetchResourceIcon(this.resource.projectid, 'project')
|
||||
this.fetchResourceIcon(this.resource.projectid, 'project')
|
||||
}
|
||||
if (this.resource.vpcid) {
|
||||
await this.fetchResourceIcon(this.resource.vpcid, 'vpc')
|
||||
this.fetchResourceIcon(this.resource.vpcid, 'vpc')
|
||||
}
|
||||
if (this.resource.networkid) {
|
||||
await this.fetchResourceIcon(this.resource.networkid, 'network')
|
||||
this.fetchResourceIcon(this.resource.networkid, 'network')
|
||||
}
|
||||
if (this.resource.oscategoryid) {
|
||||
this.fetchResourceIcon(this.resource.oscategoryid, 'guestoscategory')
|
||||
}
|
||||
},
|
||||
fetchAccount () {
|
||||
@ -1109,19 +1127,14 @@ export default {
|
||||
resourcetype: type
|
||||
}).then(json => {
|
||||
const response = json.listresourceiconresponse.icon || []
|
||||
if (response?.[0]) {
|
||||
this.images[type] = response[0].base64image
|
||||
this.images[type] = response?.[0]?.base64image || null
|
||||
resolve(this.images)
|
||||
} else {
|
||||
this.images[type] = ''
|
||||
resolve(this.images)
|
||||
}
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.images.type = ''
|
||||
this.images.type = null
|
||||
}
|
||||
},
|
||||
setData () {
|
||||
@ -1252,7 +1265,6 @@ export default {
|
||||
query[item.param] = this.resource.id
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
</span>
|
||||
<span v-if="$showIcon() && !['vm', 'vnfapp'].includes($route.path.split('/')[1])" style="margin-right: 5px">
|
||||
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/>
|
||||
<os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="xl" />
|
||||
<os-logo v-else-if="record.ostypename || ['guestoscategory'].includes($route.path.split('/')[1])" :osName="record.ostypename || record.name" size="xl" />
|
||||
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/>
|
||||
<render-icon v-else style="font-size: 16px;" :svgIcon="$route.meta.icon" />
|
||||
</span>
|
||||
@ -241,6 +241,15 @@
|
||||
</span>
|
||||
<span v-else>{{ text }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'oscategoryname'">
|
||||
<span v-if="('listOsCategories' in $store.getters.apis) && record.oscategoryid">
|
||||
<router-link :to="{ path: '/guestoscategory/' + record.oscategoryid }">{{ text }}</router-link>
|
||||
</span>
|
||||
<span v-else>{{ text }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'isuserdefined'">
|
||||
<span>{{ text ? $t('label.yes') : $t('label.no') }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'state'">
|
||||
<status v-if="$route.path.startsWith('/host')" :text="getHostState(record)" displayText />
|
||||
<status v-else :text="text ? text : ''" displayText :styles="{ 'min-width': '80px' }" />
|
||||
@ -485,6 +494,9 @@
|
||||
<template v-if="['startdate', 'enddate'].includes(column.key) && ['usage'].includes($route.path.split('/')[1])">
|
||||
{{ $toLocaleDate(text.replace('\'T\'', ' ')) }}
|
||||
</template>
|
||||
<template v-if="['isfeatured'].includes(column.key) && ['guestoscategory'].includes($route.path.split('/')[1])">
|
||||
{{ record.isfeatured ? $t('label.yes') : $t('label.no') }}
|
||||
</template>
|
||||
<template v-if="column.key === 'order'">
|
||||
<div class="shift-btns">
|
||||
<a-tooltip :name="text" placement="top">
|
||||
@ -756,7 +768,7 @@ export default {
|
||||
'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
|
||||
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
|
||||
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment', 'buckets',
|
||||
'webhook', 'webhookdeliveries', 'sharedfs', 'ipv4subnets', 'asnumbers'
|
||||
'webhook', 'webhookdeliveries', 'sharedfs', 'ipv4subnets', 'asnumbers', 'guestos'
|
||||
].includes(this.$route.name)
|
||||
},
|
||||
getDateAtTimeZone (date, timezone) {
|
||||
@ -859,8 +871,9 @@ export default {
|
||||
case 'vpcoffering':
|
||||
apiString = 'updateVPCOffering'
|
||||
break
|
||||
default:
|
||||
apiString = 'updateTemplate'
|
||||
case 'guestoscategory':
|
||||
apiString = 'updateOsCategory'
|
||||
break
|
||||
}
|
||||
return apiString
|
||||
},
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<img :src="getImg()" :height="getDimensions()" :width="getDimensions()" :style="{ marginTop: (getDimensions() === 56 || ['deployVirtualMachine'].includes($route.path.split('/')[2])) ? '' : '-5px' }"/>
|
||||
<img :src="computedImage" :height="dimensions" :width="dimensions" :style="{ marginTop: (dimensions === 56 || ['deployVirtualMachine'].includes($route.path.split('/')[2])) ? '' : '-5px' }"/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
@ -34,15 +34,17 @@ export default {
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
getImg () {
|
||||
computed: {
|
||||
computedImage () {
|
||||
if (!this.image) {
|
||||
return null
|
||||
}
|
||||
if (this.image.startsWith('data:image/png')) {
|
||||
return this.image
|
||||
} else {
|
||||
return 'data:image/png;charset=utf-8;base64, ' + this.image
|
||||
}
|
||||
return 'data:image/png;charset=utf-8;base64, ' + this.image
|
||||
},
|
||||
getDimensions () {
|
||||
dimensions () {
|
||||
const num = Number(this.size)
|
||||
if (Number.isInteger(num) && num > 0) {
|
||||
return num
|
||||
@ -54,6 +56,8 @@ export default {
|
||||
return 24
|
||||
case '1x':
|
||||
return 16
|
||||
case 'os':
|
||||
return 28
|
||||
default:
|
||||
return 16
|
||||
}
|
||||
|
||||
@ -309,7 +309,7 @@ export default {
|
||||
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
|
||||
'type', 'scope', 'managementserverid', 'serviceofferingid',
|
||||
'diskofferingid', 'networkid', 'usagetype', 'restartrequired',
|
||||
'displaynetwork', 'guestiptype', 'usersource', 'arch'].includes(item)
|
||||
'displaynetwork', 'guestiptype', 'usersource', 'arch', 'oscategoryid', 'templatetype'].includes(item)
|
||||
) {
|
||||
type = 'list'
|
||||
} else if (item === 'tags') {
|
||||
@ -458,6 +458,13 @@ export default {
|
||||
this.fields[typeIndex].opts = this.$fetchCpuArchitectureTypes()
|
||||
this.fields[typeIndex].loading = false
|
||||
}
|
||||
|
||||
if (arrayField.includes('templatetype')) {
|
||||
const typeIndex = this.fields.findIndex(item => item.name === 'templatetype')
|
||||
this.fields[typeIndex].loading = true
|
||||
this.fields[typeIndex].opts = this.$fetchTemplateTypes()
|
||||
this.fields[typeIndex].loading = false
|
||||
}
|
||||
},
|
||||
async fetchDynamicFieldData (arrayField, searchKeyword) {
|
||||
const promises = []
|
||||
@ -477,6 +484,7 @@ export default {
|
||||
let networkIndex = -1
|
||||
let usageTypeIndex = -1
|
||||
let volumeIndex = -1
|
||||
let osCategoryIndex = -1
|
||||
|
||||
if (arrayField.includes('type')) {
|
||||
if (this.$route.path === '/alert') {
|
||||
@ -580,6 +588,12 @@ export default {
|
||||
promises.push(await this.fetchVolumes(searchKeyword))
|
||||
}
|
||||
|
||||
if (arrayField.includes('oscategoryid')) {
|
||||
osCategoryIndex = this.fields.findIndex(item => item.name === 'oscategoryid')
|
||||
this.fields[osCategoryIndex].loading = true
|
||||
promises.push(await this.fetchOsCategories(searchKeyword))
|
||||
}
|
||||
|
||||
Promise.all(promises).then(response => {
|
||||
if (typeIndex > -1) {
|
||||
const types = response.filter(item => item.type === 'type')
|
||||
@ -676,6 +690,13 @@ export default {
|
||||
this.fields[usageTypeIndex].opts = this.sortArray(usageTypes[0].data)
|
||||
}
|
||||
}
|
||||
|
||||
if (osCategoryIndex > -1) {
|
||||
const osCategories = response.filter(item => item.type === 'oscategoryid')
|
||||
if (osCategories && osCategories.length > 0) {
|
||||
this.fields[osCategoryIndex].opts = this.sortArray(osCategories[0].data)
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
if (typeIndex > -1) {
|
||||
this.fields[typeIndex].loading = false
|
||||
@ -722,6 +743,9 @@ export default {
|
||||
if (usageTypeIndex > -1) {
|
||||
this.fields[usageTypeIndex].loading = false
|
||||
}
|
||||
if (osCategoryIndex > -1) {
|
||||
this.fields[osCategoryIndex].loading = false
|
||||
}
|
||||
if (Array.isArray(arrayField)) {
|
||||
this.fillFormFieldValues()
|
||||
}
|
||||
@ -1006,6 +1030,19 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchOsCategories (searchKeyword) {
|
||||
return new Promise((resolve, reject) => {
|
||||
api('listOsCategories', { showicon: true, keyword: searchKeyword }).then(json => {
|
||||
const osCategories = json.listoscategoriesresponse.oscategory
|
||||
resolve({
|
||||
type: 'oscategoryid',
|
||||
data: osCategories
|
||||
})
|
||||
}).catch(error => {
|
||||
reject(error.response.headers['x-description'])
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchGuestNetworkTypes () {
|
||||
const types = []
|
||||
if (this.apiName.indexOf('listNetworks') > -1) {
|
||||
|
||||
154
ui/src/components/widgets/BlockRadioGroupSelect.vue
Normal file
154
ui/src/components/widgets/BlockRadioGroupSelect.vue
Normal file
@ -0,0 +1,154 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-radio-group
|
||||
v-if="items.length <= maxBlocks"
|
||||
v-model:value="localValue"
|
||||
@change="handleChange()">
|
||||
<a-row type="flex" :gutter="[horizontalGutter, verticalGutter]" justify="start">
|
||||
<div v-for="item in items" :key="item.id">
|
||||
<a-col :span="6">
|
||||
<a-radio-button
|
||||
:value="item.id"
|
||||
style="border-width: 2px"
|
||||
:class="blockRadioButtonClass">
|
||||
<slot name="radio-option" :item="item"></slot>
|
||||
</a-radio-button>
|
||||
</a-col>
|
||||
</div>
|
||||
</a-row>
|
||||
</a-radio-group>
|
||||
<a-select
|
||||
v-else
|
||||
v-model:value="localValue"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="handleChange()"
|
||||
:loading="loading"
|
||||
v-focus="true">
|
||||
<a-select-option v-for="item in items" :key="item.id" :label="item.name">
|
||||
<slot name="select-option" :item="item"></slot>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'BlockRadioGroupSelect',
|
||||
props: {
|
||||
blockSize: {
|
||||
type: String,
|
||||
default: 'large'
|
||||
},
|
||||
selectedValue: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
horizontalGutter: {
|
||||
type: Number,
|
||||
default: 16
|
||||
},
|
||||
verticalGutter: {
|
||||
type: Number,
|
||||
default: 18
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
maxBlocks: {
|
||||
type: Number,
|
||||
default: 8
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
localValue: this.selectedValue
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedValue (newValue) {
|
||||
if (this.localValue === newValue) {
|
||||
return
|
||||
}
|
||||
this.localValue = newValue
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
blockRadioButtonClass () {
|
||||
if (['square', 'medium', 'small'].includes(this.blockSize)) {
|
||||
return this.blockSize + '-block-radio-button'
|
||||
}
|
||||
return 'large-block-radio-button'
|
||||
}
|
||||
},
|
||||
emits: ['change'],
|
||||
methods: {
|
||||
handleChange () {
|
||||
this.$emit('change', this.localValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.large-block-radio-button {
|
||||
width:100%;
|
||||
min-width: 345px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
padding-left: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
.medium-block-radio-button {
|
||||
width:100%;
|
||||
min-width: 160px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
padding-left: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
.small-block-radio-button {
|
||||
width:100%;
|
||||
min-width: 80px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
padding-left: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
.square-block-radio-button {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
@ -21,7 +21,7 @@
|
||||
{{ name }}
|
||||
</template>
|
||||
<font-awesome-icon
|
||||
:icon="['fab', logo]"
|
||||
:icon="logo"
|
||||
:size="size"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#666' }]"
|
||||
/>
|
||||
@ -50,7 +50,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
name: '',
|
||||
osLogo: 'linux'
|
||||
osLogo: ['fas', 'image']
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -87,35 +87,34 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
getFontAwesomeIcon (name) {
|
||||
return ['fab', name]
|
||||
},
|
||||
discoverOsLogo (name) {
|
||||
this.name = name
|
||||
const osname = name.toLowerCase()
|
||||
if (osname.includes('centos')) {
|
||||
this.osLogo = 'centos'
|
||||
} else if (osname.includes('debian')) {
|
||||
this.osLogo = 'debian'
|
||||
} else if (osname.includes('ubuntu')) {
|
||||
this.osLogo = 'ubuntu'
|
||||
} else if (osname.includes('suse')) {
|
||||
this.osLogo = 'suse'
|
||||
} else if (osname.includes('redhat')) {
|
||||
this.osLogo = 'redhat'
|
||||
} else if (osname.includes('fedora')) {
|
||||
this.osLogo = 'fedora'
|
||||
} else if (osname.includes('linux')) {
|
||||
this.osLogo = 'linux'
|
||||
} else if (osname.includes('bsd')) {
|
||||
this.osLogo = 'freebsd'
|
||||
} else if (osname.includes('apple')) {
|
||||
this.osLogo = 'apple'
|
||||
} else if (osname.includes('window') || osname.includes('dos')) {
|
||||
this.osLogo = 'windows'
|
||||
} else if (osname.includes('oracle')) {
|
||||
this.osLogo = 'java'
|
||||
} else {
|
||||
this.osLogo = 'linux'
|
||||
}
|
||||
this.$emit('update-osname', this.name)
|
||||
const osname = name.toLowerCase()
|
||||
const logos = [
|
||||
{ name: 'centos' },
|
||||
{ name: 'debian' },
|
||||
{ name: 'ubuntu' },
|
||||
{ name: 'suse' },
|
||||
{ name: 'redhat' },
|
||||
{ name: 'fedora' },
|
||||
{ name: 'linux' },
|
||||
{ name: 'bsd', alternate: 'freebsd' },
|
||||
{ name: 'apple' },
|
||||
{ name: 'macos', alternate: 'apple' },
|
||||
{ name: 'window', alternate: 'windows' },
|
||||
{ name: 'dos', alternate: 'windows' },
|
||||
{ name: 'oracle', alternate: 'java' }
|
||||
]
|
||||
const match = logos.find(entry => osname.includes(entry.name))
|
||||
if (match) {
|
||||
this.osLogo = ['fab', match.alternate ? match.alternate : match.name]
|
||||
return
|
||||
}
|
||||
this.osLogo = ['fas', 'image']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +187,56 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'guestoscategory',
|
||||
title: 'label.guest.os.categories',
|
||||
docHelp: 'adminguide/guest_os.html#guest-os-categories',
|
||||
icon: 'group-outlined',
|
||||
permission: ['listOsCategories', 'addOsCategory'],
|
||||
columns: ['name', 'isfeatured', 'created', 'order'],
|
||||
details: ['name', 'isfeatured', 'created'],
|
||||
related: [{
|
||||
name: 'guestos',
|
||||
title: 'label.guest.os',
|
||||
param: 'oscategoryid'
|
||||
},
|
||||
{
|
||||
name: 'template',
|
||||
title: 'label.templates',
|
||||
param: 'oscategoryid'
|
||||
},
|
||||
{
|
||||
name: 'iso',
|
||||
title: 'label.isos',
|
||||
param: 'oscategoryid'
|
||||
}],
|
||||
actions: [
|
||||
{
|
||||
api: 'addOsCategory',
|
||||
icon: 'plus-outlined',
|
||||
label: 'label.add.guest.os.category',
|
||||
listView: true,
|
||||
dataView: false,
|
||||
args: ['name', 'isfeatured']
|
||||
},
|
||||
{
|
||||
api: 'updateOsCategory',
|
||||
icon: 'edit-outlined',
|
||||
label: 'label.edit',
|
||||
dataView: true,
|
||||
popup: true,
|
||||
args: ['name', 'isfeatured']
|
||||
},
|
||||
{
|
||||
api: 'deleteOsCategory',
|
||||
icon: 'delete-outlined',
|
||||
label: 'label.action.delete.guest.os.category',
|
||||
message: 'message.action.delete.guest.os.category',
|
||||
dataView: true,
|
||||
popup: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'guestos',
|
||||
title: 'label.guest.os',
|
||||
@ -195,6 +245,7 @@ export default {
|
||||
permission: ['listOsTypes', 'listOsCategories'],
|
||||
columns: ['name', 'oscategoryname', 'isuserdefined'],
|
||||
details: ['name', 'oscategoryname', 'isuserdefined'],
|
||||
searchFilters: ['oscategoryid'],
|
||||
related: [{
|
||||
name: 'guestoshypervisormapping',
|
||||
title: 'label.guest.os.hypervisor.mappings',
|
||||
@ -221,7 +272,14 @@ export default {
|
||||
label: 'label.edit',
|
||||
dataView: true,
|
||||
popup: true,
|
||||
args: ['osdisplayname']
|
||||
groupAction: true,
|
||||
groupMap: (selection, values) => { return selection.map(x => { return { id: x, oscategoryid: values.oscategoryid } }) },
|
||||
args: (record, store, isGroupAction) => {
|
||||
if (isGroupAction) {
|
||||
return ['oscategoryid']
|
||||
}
|
||||
return ['osdisplayname', 'oscategoryid']
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'addGuestOsMapping',
|
||||
|
||||
@ -67,7 +67,7 @@ export default {
|
||||
return fields
|
||||
},
|
||||
searchFilters: () => {
|
||||
var filters = ['name', 'zoneid', 'tags', 'arch']
|
||||
var filters = ['name', 'zoneid', 'tags', 'arch', 'oscategoryid', 'templatetype']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
filters.push('storageid')
|
||||
filters.push('imagestoreid')
|
||||
@ -235,7 +235,7 @@ export default {
|
||||
},
|
||||
details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'arch', 'bootable', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'crosszones', 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url'],
|
||||
searchFilters: () => {
|
||||
var filters = ['name', 'zoneid', 'tags', 'arch']
|
||||
var filters = ['name', 'zoneid', 'tags', 'arch', 'oscategoryid']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
filters.push('storageid')
|
||||
filters.push('imagestoreid')
|
||||
|
||||
@ -48,6 +48,7 @@ import {
|
||||
Divider,
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
Typography,
|
||||
Upload,
|
||||
Progress,
|
||||
Skeleton,
|
||||
@ -117,6 +118,7 @@ export default {
|
||||
app.use(Divider)
|
||||
app.use(DatePicker)
|
||||
app.use(TimePicker)
|
||||
app.use(Typography)
|
||||
app.use(Upload)
|
||||
app.use(Progress)
|
||||
app.use(Skeleton)
|
||||
|
||||
@ -83,6 +83,7 @@ import {
|
||||
FileDoneOutlined,
|
||||
FileProtectOutlined,
|
||||
FileTextOutlined,
|
||||
FileZipOutlined,
|
||||
FilterOutlined,
|
||||
FilterTwoTone,
|
||||
FireOutlined,
|
||||
@ -99,6 +100,7 @@ import {
|
||||
GlobalOutlined,
|
||||
GoldOutlined,
|
||||
GoogleOutlined,
|
||||
GroupOutlined,
|
||||
HddOutlined,
|
||||
HomeOutlined,
|
||||
IdcardOutlined,
|
||||
@ -253,6 +255,7 @@ export default {
|
||||
app.component('FileDoneOutlined', FileDoneOutlined)
|
||||
app.component('FileProtectOutlined', FileProtectOutlined)
|
||||
app.component('FileTextOutlined', FileTextOutlined)
|
||||
app.component('FileZipOutlined', FileZipOutlined)
|
||||
app.component('FilterOutlined', FilterOutlined)
|
||||
app.component('FilterTwoTone', FilterTwoTone)
|
||||
app.component('FireOutlined', FireOutlined)
|
||||
@ -269,6 +272,7 @@ export default {
|
||||
app.component('GlobalOutlined', GlobalOutlined)
|
||||
app.component('GoldOutlined', GoldOutlined)
|
||||
app.component('GoogleOutlined', GoogleOutlined)
|
||||
app.component('GroupOutlined', GroupOutlined)
|
||||
app.component('HddOutlined', HddOutlined)
|
||||
app.component('HomeOutlined', HomeOutlined)
|
||||
app.component('IdcardOutlined', IdcardOutlined)
|
||||
|
||||
@ -37,7 +37,8 @@ import {
|
||||
genericUtilPlugin,
|
||||
localesPlugin,
|
||||
dialogUtilPlugin,
|
||||
cpuArchitectureUtilPlugin
|
||||
cpuArchitectureUtilPlugin,
|
||||
imagesUtilPlugin
|
||||
} from './utils/plugins'
|
||||
import { VueAxios } from './utils/request'
|
||||
import directives from './utils/directives'
|
||||
@ -55,6 +56,7 @@ vueApp.use(localesPlugin)
|
||||
vueApp.use(genericUtilPlugin)
|
||||
vueApp.use(dialogUtilPlugin)
|
||||
vueApp.use(cpuArchitectureUtilPlugin)
|
||||
vueApp.use(imagesUtilPlugin)
|
||||
vueApp.use(extensions)
|
||||
vueApp.use(directives)
|
||||
|
||||
|
||||
@ -404,10 +404,11 @@ const user = {
|
||||
}).catch(ignored => {})
|
||||
}
|
||||
|
||||
api('listUsers', { id: Cookies.get('userid') }).then(response => {
|
||||
api('listUsers', { id: Cookies.get('userid'), showicon: true }).then(response => {
|
||||
const result = response.listusersresponse.user[0]
|
||||
commit('SET_INFO', result)
|
||||
commit('SET_NAME', result.firstname + ' ' + result.lastname)
|
||||
commit('SET_AVATAR', result.icon?.base64image || '')
|
||||
store.dispatch('SetCsLatestVersion', result.rolename)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
|
||||
@ -338,7 +338,7 @@ export const showIconPlugin = {
|
||||
if (resource) {
|
||||
resourceType = resource
|
||||
}
|
||||
if (['zone', 'zones', 'template', 'iso', 'account', 'accountuser', 'vm', 'domain', 'project', 'vpc', 'guestnetwork'].includes(resourceType)) {
|
||||
if (['zone', 'zones', 'template', 'iso', 'account', 'accountuser', 'vm', 'domain', 'project', 'vpc', 'guestnetwork', 'guestoscategory'].includes(resourceType)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -412,6 +412,7 @@ export const resourceTypePlugin = {
|
||||
case 'VpnCustomerGateway':
|
||||
case 'AutoScaleVmGroup':
|
||||
case 'QuotaTariff':
|
||||
case 'GuestOsCategory':
|
||||
return resourceType.toLowerCase()
|
||||
}
|
||||
return ''
|
||||
@ -543,10 +544,24 @@ export const cpuArchitectureUtilPlugin = {
|
||||
install (app) {
|
||||
app.config.globalProperties.$fetchCpuArchitectureTypes = function () {
|
||||
const architectures = [
|
||||
{ id: 'x86_64', name: 'AMD 64 bits (x86_64)' },
|
||||
{ id: 'x86_64', name: 'Intel/AMD 64 bits (x86_64)' },
|
||||
{ id: 'aarch64', name: 'ARM 64 bits (aarch64)' }
|
||||
]
|
||||
return architectures.map(item => ({ ...item, description: item.name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const imagesUtilPlugin = {
|
||||
install (app) {
|
||||
app.config.globalProperties.$fetchTemplateTypes = function () {
|
||||
const baseTypes = ['USER', 'VNF']
|
||||
const adminTypes = ['SYSTEM', 'BUILTIN', 'ROUTING']
|
||||
const types = [...baseTypes]
|
||||
if (store.getters.userInfo?.roletype === 'Admin') {
|
||||
types.push(...adminTypes)
|
||||
}
|
||||
return types.map(type => ({ id: type, name: type, description: type }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1261,6 +1261,9 @@ export default {
|
||||
}
|
||||
var paramName = param.name
|
||||
var extractedParamName = paramName.replace('ids', '').replace('id', '').toLowerCase()
|
||||
if (extractedParamName.endsWith('ory')) {
|
||||
extractedParamName = extractedParamName.slice(0, -3) + 'orie'
|
||||
}
|
||||
var params = { listall: true }
|
||||
for (const filter in filters) {
|
||||
params[filter] = filters[filter]
|
||||
|
||||
@ -175,7 +175,7 @@
|
||||
class="auth-btn github-auth"
|
||||
style="height: 38px; width: 185px; padding: 0; margin-bottom: 5px;" >
|
||||
<img src="/assets/github.svg" style="width: 32px; padding: 5px" />
|
||||
<a-text>Sign in with Github</a-text>
|
||||
<a-typography-text>Sign in with Github</a-typography-text>
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="social-auth" v-if="googleprovider">
|
||||
@ -187,7 +187,7 @@
|
||||
class="auth-btn google-auth"
|
||||
style="height: 38px; width: 185px; padding: 0" >
|
||||
<img src="/assets/google.svg" style="width: 32px; padding: 5px" />
|
||||
<a-text>Sign in with Google</a-text>
|
||||
<a-typography-text>Sign in with Google</a-typography-text>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -34,72 +34,70 @@
|
||||
<div style="margin-top: 15px">
|
||||
<span>{{ $t('message.select.a.zone') }}</span><br/>
|
||||
<a-form-item :label="$t('label.zoneid')" name="zoneid" ref="zoneid">
|
||||
<div v-if="zones.length <= 8">
|
||||
<a-row type="flex" :gutter="[16,18]" justify="start">
|
||||
<div v-for="(zoneItem, idx) in zones" :key="idx">
|
||||
<a-radio-group
|
||||
:key="idx"
|
||||
:size="large"
|
||||
v-model:value="form.zoneid"
|
||||
@change="onSelectZoneId(zoneItem.id)">
|
||||
<a-col :span="6">
|
||||
<a-radio-button
|
||||
:value="zoneItem.id"
|
||||
style="border-width: 2px"
|
||||
class="zone-radio-button">
|
||||
<span>
|
||||
<resource-icon
|
||||
v-if="zoneItem && zoneItem.icon && zoneItem.icon.base64image"
|
||||
:image="zoneItem.icon.base64image"
|
||||
size="2x" />
|
||||
<global-outlined size="2x" v-else />
|
||||
{{ zoneItem.name }}
|
||||
</span>
|
||||
</a-radio-button>
|
||||
</a-col>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</a-row>
|
||||
</div>
|
||||
<a-select
|
||||
v-else
|
||||
v-model:value="form.zoneid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="onSelectZoneId"
|
||||
:loading="loading.zones"
|
||||
v-focus="true"
|
||||
>
|
||||
<a-select-option v-for="zone1 in zones" :key="zone1.id" :label="zone1.name">
|
||||
<span>
|
||||
<resource-icon v-if="zone1.icon && zone1.icon.base64image" :image="zone1.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zone1.name }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<zone-block-radio-group-select
|
||||
:items="zones"
|
||||
:selectedValue="form.zoneid"
|
||||
@change="onSelectZoneId" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="$t('label.template')"
|
||||
v-if="!zoneSelected || isZoneSelectedMultiArch"
|
||||
:title="$t('label.arch')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template #description>
|
||||
<div v-if="zoneSelected" style="margin-top: 15px">
|
||||
{{ $t('message.instance.architecture') }}
|
||||
<block-radio-group-select
|
||||
style="margin-top: 5px;"
|
||||
:items="architectureTypes.opts"
|
||||
:selectedValue="selectedArchitecture"
|
||||
@change="changeArchitecture">
|
||||
<template #radio-option="{ item }">
|
||||
<span>{{ item.name || item.description }}</span>
|
||||
</template>
|
||||
<template #select-option="{ item }">
|
||||
<span>{{ item.name || item.description }}</span>
|
||||
</template>
|
||||
</block-radio-group-select>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="$t('label.template.select')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template #description>
|
||||
<div v-if="zoneSelected" style="margin-top: 15px">
|
||||
<os-based-image-selection
|
||||
v-if="isModernImageSelection"
|
||||
:imageTypeSelectionAllowed="false"
|
||||
:imagePreSelected="!!this.queryTemplateId"
|
||||
:guestOsCategoriesSelectionDisallowed="!this.queryGuestOsCategoryId && !!this.queryTemplateId"
|
||||
:guestOsCategories="options.guestOsCategories"
|
||||
:guestOsCategoriesLoading="loading.guestOsCategories"
|
||||
:selectedGuestOsCategoryId="form.guestoscategoryid"
|
||||
:imageItems="options.templates"
|
||||
:imagesLoading="loading.templates"
|
||||
:diskSizeSelectionDeployAsIsMessageVisible="template && template.deployasis"
|
||||
:rootDiskOverrideDisabled="rootDiskSizeFixed > 0 || (template && template.deployasis) || showOverrideDiskOfferingOption"
|
||||
:rootDiskOverrideChecked="form.rootdisksizeitem"
|
||||
:filterOption="filterOption"
|
||||
:preFillContent="dataPreFill"
|
||||
@change-guest-os-category="onSelectGuestOsCategory"
|
||||
@handle-image-search-filter="($event) => fetchAllTemplates($event)"
|
||||
@update-image="updateFieldValue"
|
||||
@update-disk-size="updateFieldValue" />
|
||||
<a-card
|
||||
:tabList="tabList"
|
||||
:activeTabKey="tabKey"
|
||||
@tabChange="key => onTabChange(key, 'tabKey')">
|
||||
<div v-if="tabKey === 'templateid'">
|
||||
v-else
|
||||
:tabList="imageTypeList"
|
||||
:activeTabKey="imageType">
|
||||
<div>
|
||||
{{ $t('message.template.desc') }}
|
||||
<template-iso-selection
|
||||
input-decorator="templateid"
|
||||
:items="options.templates"
|
||||
:selected="tabKey"
|
||||
:selected="imageType"
|
||||
:loading="loading.templates"
|
||||
:preFillContent="dataPreFill"
|
||||
:key="templateKey"
|
||||
@ -109,10 +107,10 @@
|
||||
{{ $t('label.override.rootdisk.size') }}
|
||||
<a-switch
|
||||
v-model:checked="form.rootdisksizeitem"
|
||||
:disabled="rootDiskSizeFixed > 0 || template.deployasis || showOverrideDiskOfferingOption"
|
||||
:disabled="rootDiskSizeFixed > 0 || (template && template.deployasis) || showOverrideDiskOfferingOption"
|
||||
@change="val => { showRootDiskSizeChanger = val }"
|
||||
style="margin-left: 10px;"/>
|
||||
<div v-if="template.deployasis"> {{ $t('message.deployasis') }} </div>
|
||||
<div v-if="template && template.deployasis"> {{ $t('message.deployasis') }} </div>
|
||||
</div>
|
||||
<disk-size-selection
|
||||
v-if="showRootDiskSizeChanger"
|
||||
@ -220,7 +218,7 @@
|
||||
<span v-if="serviceOffering && !serviceOffering.diskofferingstrictness">
|
||||
<a-step
|
||||
:status="zoneSelected ? 'process' : 'wait'"
|
||||
v-if="!template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
v-if="template && !template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
<template #description>
|
||||
<div v-if="zoneSelected">
|
||||
<multi-disk-selection
|
||||
@ -244,7 +242,7 @@
|
||||
:value="overrideDiskOffering ? overrideDiskOffering.id : ''"
|
||||
:loading="loading.diskOfferings"
|
||||
:preFillContent="dataPreFill"
|
||||
:isIsoSelected="tabKey==='isoid'"
|
||||
:isIsoSelected="imageType==='isoid'"
|
||||
:isRootDiskOffering="true"
|
||||
@on-selected-root-disk-size="onSelectRootDiskSize"
|
||||
@select-disk-offering-item="($event) => updateOverrideDiskOffering($event)"
|
||||
@ -273,7 +271,7 @@
|
||||
<a-step
|
||||
:title="$t('label.data.disk')"
|
||||
:status="zoneSelected ? 'process' : 'wait'"
|
||||
v-if="!template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
v-if="template && !template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
<template #description>
|
||||
<div v-if="zoneSelected">
|
||||
<multi-disk-selection
|
||||
@ -286,7 +284,7 @@
|
||||
</a-step>
|
||||
<a-step
|
||||
v-else
|
||||
:title="tabKey === 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
|
||||
:title="imageType === 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template #description>
|
||||
<div v-if="zoneSelected">
|
||||
@ -305,7 +303,7 @@
|
||||
:value="diskOffering ? diskOffering.id : ''"
|
||||
:loading="loading.diskOfferings"
|
||||
:preFillContent="dataPreFill"
|
||||
:isIsoSelected="tabKey==='isoid'"
|
||||
:isIsoSelected="imageType==='isoid'"
|
||||
@on-selected-disk-size="onSelectDiskSize"
|
||||
@select-disk-offering-item="($event) => updateDiskOffering($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('diskOfferings', $event)"
|
||||
@ -768,13 +766,13 @@
|
||||
</template>
|
||||
<a-card>
|
||||
<div v-if="this.template && this.template.userdataid">
|
||||
<a-text type="primary">
|
||||
<a-typography-text>
|
||||
Userdata "{{ $t(this.template.userdataname) }}" is linked with template "{{ $t(this.template.name) }}" with override policy "{{ $t(this.template.userdatapolicy) }}"
|
||||
</a-text><br/><br/>
|
||||
</a-typography-text><br/><br/>
|
||||
<div v-if="templateUserDataParams.length > 0 && !doUserdataOverride">
|
||||
<a-text type="primary" v-if="this.template && this.template.userdataid && templateUserDataParams.length > 0">
|
||||
<a-typography-text v-if="this.template && this.template.userdataid && templateUserDataParams.length > 0">
|
||||
Enter the values for the variables in userdata
|
||||
</a-text>
|
||||
</a-typography-text>
|
||||
<a-input-group>
|
||||
<a-table
|
||||
size="small"
|
||||
@ -1034,12 +1032,15 @@ import eventBus from '@/config/eventBus'
|
||||
|
||||
import InfoCard from '@/components/view/InfoCard'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import ZoneBlockRadioGroupSelect from '@views/compute/wizard/ZoneBlockRadioGroupSelect.vue'
|
||||
import BlockRadioGroupSelect from '@/components/widgets/BlockRadioGroupSelect'
|
||||
import ComputeOfferingSelection from '@views/compute/wizard/ComputeOfferingSelection'
|
||||
import ComputeSelection from '@views/compute/wizard/ComputeSelection'
|
||||
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
|
||||
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
|
||||
import MultiDiskSelection from '@views/compute/wizard/MultiDiskSelection'
|
||||
import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
|
||||
import OsBasedImageSelection from '@views/compute/wizard/OsBasedImageSelection'
|
||||
import AffinityGroupSelection from '@views/compute/wizard/AffinityGroupSelection'
|
||||
import NetworkSelection from '@views/compute/wizard/NetworkSelection'
|
||||
import NetworkConfiguration from '@views/compute/wizard/NetworkConfiguration'
|
||||
@ -1057,6 +1058,10 @@ const STATUS_FAILED = 'error'
|
||||
export default {
|
||||
name: 'Wizard',
|
||||
components: {
|
||||
InfoCard,
|
||||
ResourceIcon,
|
||||
ZoneBlockRadioGroupSelect,
|
||||
BlockRadioGroupSelect,
|
||||
SshKeyPairSelection,
|
||||
UserDataSelection,
|
||||
NetworkConfiguration,
|
||||
@ -1064,14 +1069,13 @@ export default {
|
||||
LoadBalancerSelection,
|
||||
AffinityGroupSelection,
|
||||
TemplateIsoSelection,
|
||||
OsBasedImageSelection,
|
||||
DiskSizeSelection,
|
||||
MultiDiskSelection,
|
||||
DiskOfferingSelection,
|
||||
InfoCard,
|
||||
ComputeOfferingSelection,
|
||||
ComputeSelection,
|
||||
SecurityGroupSelection,
|
||||
ResourceIcon,
|
||||
TooltipLabel,
|
||||
InstanceNicsNetworkSelectListView
|
||||
},
|
||||
@ -1103,7 +1107,10 @@ export default {
|
||||
},
|
||||
zoneId: '',
|
||||
zoneSelected: false,
|
||||
isZoneSelectedMultiArch: false,
|
||||
dynamicscalingenabled: true,
|
||||
imageType: 'templateid',
|
||||
imageSearchFilters: null,
|
||||
templateKey: 0,
|
||||
showRegisteredUserdata: true,
|
||||
doUserdataOverride: false,
|
||||
@ -1126,6 +1133,7 @@ export default {
|
||||
disksize: null
|
||||
},
|
||||
options: {
|
||||
guestOsCategories: [],
|
||||
templates: {},
|
||||
serviceOfferings: [],
|
||||
diskOfferings: [],
|
||||
@ -1139,6 +1147,7 @@ export default {
|
||||
rowCount: {},
|
||||
loading: {
|
||||
deploy: false,
|
||||
guestOsCategories: false,
|
||||
templates: false,
|
||||
serviceOfferings: false,
|
||||
diskOfferings: false,
|
||||
@ -1251,16 +1260,9 @@ export default {
|
||||
templateUserDataParams: [],
|
||||
templateUserDataValues: {},
|
||||
overrideDiskOffering: {},
|
||||
templateFilter: [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
],
|
||||
initDataConfig: {},
|
||||
defaultNetworkId: '',
|
||||
dataNetworkCreated: [],
|
||||
tabKey: 'templateid',
|
||||
userdataTabKey: 'userdataregistered',
|
||||
dataPreFill: {},
|
||||
showDetails: false,
|
||||
@ -1280,7 +1282,9 @@ export default {
|
||||
zones: [],
|
||||
selectedZone: '',
|
||||
formModel: {},
|
||||
nicToNetworkSelection: []
|
||||
nicToNetworkSelection: [],
|
||||
selectedArchitecture: null,
|
||||
architectureTypes: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -1380,7 +1384,7 @@ export default {
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
networkid: this.defaultNetworkId,
|
||||
id: this.lbRuleId,
|
||||
id: this.queryLbRuleId,
|
||||
projectid: store.getters.project ? store.getters.project.id : null,
|
||||
domainid: store.getters.project && store.getters.project.id ? null : store.getters.userInfo.domainid,
|
||||
account: store.getters.project && store.getters.project.id ? null : store.getters.userInfo.account,
|
||||
@ -1389,6 +1393,18 @@ export default {
|
||||
keyword: undefined,
|
||||
showIcon: true
|
||||
}
|
||||
},
|
||||
guestOsCategories: {
|
||||
list: 'listOsCategories',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
isfeatured: true,
|
||||
isiso: _.get(this.form, 'imagetype') === 'isoid',
|
||||
arch: this.selectedArchitecture,
|
||||
isvnf: false,
|
||||
showicon: true
|
||||
},
|
||||
field: 'guestoscategoryid'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1406,21 +1422,29 @@ export default {
|
||||
templateConfigurationExists () {
|
||||
return this.vm.templateid && this.templateConfigurations && this.templateConfigurations.length > 0
|
||||
},
|
||||
templateId () {
|
||||
queryZoneId () {
|
||||
return this.$route.query.zoneid || null
|
||||
},
|
||||
queryArchId () {
|
||||
return this.$route.query.arch || null
|
||||
},
|
||||
queryTemplateId () {
|
||||
return this.$route.query.templateid || null
|
||||
},
|
||||
networkId () {
|
||||
queryNetworkId () {
|
||||
return this.$route.query.networkid || null
|
||||
},
|
||||
lbRuleId () {
|
||||
queryGuestOsCategoryId () {
|
||||
return this.$route.query.oscategoryid || null
|
||||
},
|
||||
queryLbRuleId () {
|
||||
return this.$route.query.lbruleid || null
|
||||
},
|
||||
tabList () {
|
||||
const tabList = [{
|
||||
imageTypeList () {
|
||||
return [{
|
||||
key: 'templateid',
|
||||
tab: this.$t('label.templates')
|
||||
}]
|
||||
return tabList
|
||||
},
|
||||
userdataTabList () {
|
||||
let tabList = []
|
||||
@ -1451,6 +1475,18 @@ export default {
|
||||
},
|
||||
isCustomizedIOPS () {
|
||||
return this.rootDiskSelected?.iscustomizediops || this.serviceOffering?.iscustomizediops || false
|
||||
},
|
||||
isModernImageSelection () {
|
||||
return this.$config.imageSelectionInterface === undefined || this.$config.imageSelectionInterface === 'modern'
|
||||
},
|
||||
imageSelection () {
|
||||
return this.isModernImageSelection ? 'modern' : 'legacy'
|
||||
},
|
||||
showUserCategoryForModernImageSelection () {
|
||||
return this.$config.showUserCategoryForModernImageSelection === undefined || this.$config.showUserCategoryForModernImageSelection
|
||||
},
|
||||
showAllCategoryForModernImageSelection () {
|
||||
return this.$config.showAllCategoryForModernImageSelection
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -1466,7 +1502,7 @@ export default {
|
||||
Object.keys(vmgroupConfig).forEach(field => {
|
||||
this.vm[field] = this.vmgroupConfig[field]
|
||||
})
|
||||
this.template = ''
|
||||
this.template = null
|
||||
for (const key in this.options.templates) {
|
||||
var template = _.find(_.get(this.options.templates[key], 'template', []), (option) => option.id === vmgroupConfig.templateid)
|
||||
if (template) {
|
||||
@ -1545,7 +1581,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.template.deployasis && this.template.childtemplates && this.template.childtemplates.length > 0) {
|
||||
if (this.template && !this.template.deployasis && this.template.childtemplates && this.template.childtemplates.length > 0) {
|
||||
this.vm.diskofferingid = ''
|
||||
this.vm.diskofferingname = ''
|
||||
this.vm.diskofferingsize = ''
|
||||
@ -1766,6 +1802,27 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
getImageFilters (params, forReset) {
|
||||
if (this.isModernImageSelection) {
|
||||
if (this.form.guestoscategoryid === '0') {
|
||||
return ['self']
|
||||
}
|
||||
if (this.isModernImageSelection && params && !forReset) {
|
||||
if (params.featured) {
|
||||
return ['featured']
|
||||
} else if (params.public) {
|
||||
return ['community']
|
||||
}
|
||||
}
|
||||
return this.isNormalAndDomainUser ? ['executable'] : ['all']
|
||||
}
|
||||
return [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
]
|
||||
},
|
||||
getPropertyQualifiers (qualifiers, type) {
|
||||
var result = ''
|
||||
switch (type) {
|
||||
@ -1805,18 +1862,25 @@ export default {
|
||||
let zones = []
|
||||
let apiName = ''
|
||||
const params = {}
|
||||
if (this.templateId) {
|
||||
if (this.queryZoneId) {
|
||||
zones.push(this.queryZoneId)
|
||||
if (this.queryTemplateId) {
|
||||
this.dataPreFill.templateid = this.queryTemplateId
|
||||
}
|
||||
return resolve(zones)
|
||||
} else if (this.queryTemplateId) {
|
||||
apiName = 'listTemplates'
|
||||
params.listall = true
|
||||
params.templatefilter = this.isNormalAndDomainUser ? 'executable' : 'all'
|
||||
params.id = this.templateId
|
||||
} else if (this.networkId) {
|
||||
params.id = this.queryTemplateId
|
||||
this.dataPreFill.templateid = this.queryTemplateId
|
||||
} else if (this.queryNetworkId) {
|
||||
params.listall = true
|
||||
params.id = this.networkId
|
||||
params.id = this.queryNetworkId
|
||||
apiName = 'listNetworks'
|
||||
} else if (this.lbRuleId) {
|
||||
} else if (this.queryLbRuleId) {
|
||||
params.listall = true
|
||||
params.id = this.lbRuleId
|
||||
params.id = this.queryLbRuleId
|
||||
apiName = 'listLoadBalancerRules'
|
||||
}
|
||||
if (!apiName) return resolve(zones)
|
||||
@ -1840,6 +1904,10 @@ export default {
|
||||
})
|
||||
},
|
||||
async fetchData () {
|
||||
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
|
||||
if (this.queryArchId) {
|
||||
this.architectureTypes.opts = this.architectureTypes.opts.filter(o => o.id === this.queryArchId)
|
||||
}
|
||||
const zones = await this.fetchZoneByQuery()
|
||||
if (zones && zones.length === 1) {
|
||||
this.selectedZone = zones[0]
|
||||
@ -1986,25 +2054,18 @@ export default {
|
||||
},
|
||||
updateFieldValue (name, value) {
|
||||
if (name === 'templateid') {
|
||||
this.tabKey = 'templateid'
|
||||
this.form.templateid = value
|
||||
this.resetFromTemplateConfiguration()
|
||||
let template = ''
|
||||
for (const key in this.options.templates) {
|
||||
var t = _.find(_.get(this.options.templates[key], 'template', []), (option) => option.id === value)
|
||||
if (t) {
|
||||
this.template = t
|
||||
this.templateConfigurations = []
|
||||
this.selectedTemplateConfiguration = {}
|
||||
this.templateNics = []
|
||||
this.templateLicenses = []
|
||||
this.templateProperties = {}
|
||||
this.updateTemplateParameters()
|
||||
template = t
|
||||
for (const entry of Object.values(this.options.templates)) {
|
||||
template = entry?.template.find(option => option.id === value) || null
|
||||
if (template) {
|
||||
this.template = template
|
||||
break
|
||||
}
|
||||
}
|
||||
if (template) {
|
||||
this.resetTemplateAssociatedResources()
|
||||
var size = template.size / (1024 * 1024 * 1024) || 0 // bytes to GB
|
||||
this.dataPreFill.minrootdisksize = Math.ceil(size)
|
||||
this.updateTemplateLinkedUserData(this.template.userdataid)
|
||||
@ -2392,6 +2453,58 @@ export default {
|
||||
getText (option) {
|
||||
return _.get(option, 'displaytext', _.get(option, 'name'))
|
||||
},
|
||||
fetchGuestOsCategories (skipFetchImages) {
|
||||
const key = 'guestOsCategories'
|
||||
const params = this.params[key]
|
||||
if (this.queryGuestOsCategoryId) {
|
||||
params.options.id = this.queryGuestOsCategoryId
|
||||
} else if (this.queryTemplateId) {
|
||||
this.fetchAllTemplates()
|
||||
return Promise.resolve()
|
||||
}
|
||||
return this.fetchOptions(params, key, ['zones'])
|
||||
.then((res) => {
|
||||
if (!this.options.guestOsCategories) {
|
||||
this.options.guestOsCategories = []
|
||||
}
|
||||
if (!this.queryGuestOsCategoryId) {
|
||||
if (this.showUserCategoryForModernImageSelection) {
|
||||
const userCategory = {
|
||||
id: '0',
|
||||
name: this.$t('label.user')
|
||||
}
|
||||
if (this.$store.getters.avatar) {
|
||||
userCategory.icon = {
|
||||
base64image: this.$store.getters.avatar
|
||||
}
|
||||
}
|
||||
this.options.guestOsCategories.push(userCategory)
|
||||
}
|
||||
if (this.showAllCategoryForModernImageSelection) {
|
||||
this.options.guestOsCategories.push({
|
||||
id: '-1',
|
||||
name: this.$t('label.all')
|
||||
})
|
||||
}
|
||||
}
|
||||
this.form.guestoscategoryid = this.options.guestOsCategories[0].id
|
||||
if (skipFetchImages) {
|
||||
return
|
||||
}
|
||||
this.fetchAllTemplates()
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('Error fetching guestOsCategories:', e)
|
||||
})
|
||||
},
|
||||
changeArchitecture (arch) {
|
||||
this.selectedArchitecture = arch
|
||||
if (this.isModernImageSelection) {
|
||||
this.fetchGuestOsCategories()
|
||||
return
|
||||
}
|
||||
this.fetchAllTemplates()
|
||||
},
|
||||
async handleSubmit (e) {
|
||||
console.log('wizard submit')
|
||||
e.preventDefault()
|
||||
@ -2552,7 +2665,7 @@ export default {
|
||||
|
||||
if (this.showRootDiskSizeChanger && values.rootdisksize && values.rootdisksize > 0) {
|
||||
createVmGroupData.rootdisksize = values.rootdisksize
|
||||
} else if (this.rootDiskSizeFixed > 0 && !this.template.deployasis) {
|
||||
} else if (this.rootDiskSizeFixed > 0 && this.template && !this.template.deployasis) {
|
||||
createVmGroupData.rootdisksize = this.rootDiskSizeFixed
|
||||
}
|
||||
|
||||
@ -2799,10 +2912,9 @@ export default {
|
||||
})
|
||||
},
|
||||
fetchOptions (param, name, exclude) {
|
||||
if (exclude && exclude.length > 0) {
|
||||
if (exclude.includes(name)) {
|
||||
return
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (exclude && exclude.length > 0 && exclude.includes(name)) {
|
||||
return resolve(null)
|
||||
}
|
||||
this.loading[name] = true
|
||||
param.loading = true
|
||||
@ -2817,10 +2929,10 @@ export default {
|
||||
if (Object.keys(responseItem).length === 0) {
|
||||
this.rowCount[name] = 0
|
||||
this.options[name] = []
|
||||
return
|
||||
return resolve(null)
|
||||
}
|
||||
if (!responseKey.includes('response')) {
|
||||
return
|
||||
return resolve(null)
|
||||
}
|
||||
_.map(responseItem, (response, key) => {
|
||||
if (key === 'count') {
|
||||
@ -2848,23 +2960,38 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
resolve(response)
|
||||
}).catch(function (error) {
|
||||
console.log(error.stack)
|
||||
param.loading = false
|
||||
reject(error)
|
||||
}).finally(() => {
|
||||
this.loading[name] = false
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchTemplates (templateFilter, params) {
|
||||
const args = Object.assign({}, params)
|
||||
if (args.keyword || args.category !== templateFilter) {
|
||||
if (this.isModernImageSelection && this.form.guestoscategoryid && !['-1', '0'].includes(this.form.guestoscategoryid)) {
|
||||
args.oscategoryid = this.form.guestoscategoryid
|
||||
}
|
||||
if (args.keyword || (args.category && args.category !== templateFilter)) {
|
||||
args.page = 1
|
||||
args.pageSize = args.pageSize || 10
|
||||
}
|
||||
args.zoneid = _.get(this.zone, 'id')
|
||||
if (this.isZoneSelectedMultiArch) {
|
||||
args.arch = this.selectedArchitecture
|
||||
}
|
||||
args.templatefilter = templateFilter
|
||||
args.details = 'all'
|
||||
args.showicon = 'true'
|
||||
args.id = this.queryTemplateId
|
||||
args.isvnf = false
|
||||
|
||||
delete args.category
|
||||
delete args.public
|
||||
delete args.featured
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
api('listTemplates', args).then((response) => {
|
||||
@ -2879,14 +3006,16 @@ export default {
|
||||
const promises = []
|
||||
const templates = {}
|
||||
this.loading.templates = true
|
||||
this.templateFilter.forEach((filter) => {
|
||||
this.imageSearchFilters = params
|
||||
const templateFilters = this.getImageFilters(params)
|
||||
templateFilters.forEach((filter) => {
|
||||
templates[filter] = { count: 0, template: [] }
|
||||
promises.push(this.fetchTemplates(filter, params))
|
||||
})
|
||||
this.options.templates = templates
|
||||
Promise.all(promises).then((response) => {
|
||||
response.forEach((resItem, idx) => {
|
||||
templates[this.templateFilter[idx]] = _.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : resItem.listtemplatesresponse
|
||||
templates[templateFilters[idx]] = _.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : resItem.listtemplatesresponse
|
||||
this.options.templates = { ...templates }
|
||||
})
|
||||
}).catch((reason) => {
|
||||
@ -2898,35 +3027,71 @@ export default {
|
||||
filterOption (input, option) {
|
||||
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0
|
||||
},
|
||||
resetTemplatesList () {
|
||||
const templates = {}
|
||||
const templateFilters = this.getImageFilters(null, true)
|
||||
templateFilters.forEach((filter) => {
|
||||
templates[filter] = { count: 0, template: [] }
|
||||
})
|
||||
this.options.templates = templates
|
||||
},
|
||||
resetTemplateAssociatedResources () {
|
||||
this.templateConfigurations = []
|
||||
this.selectedTemplateConfiguration = {}
|
||||
this.templateNics = []
|
||||
this.templateLicenses = []
|
||||
this.templateProperties = {}
|
||||
},
|
||||
async fetchZoneOptions () {
|
||||
let guestOsFetch = null
|
||||
for (const [name, param] of Object.entries(this.params)) {
|
||||
if (this.queryNetworkId && name === 'networks') {
|
||||
param.options = { id: this.queryNetworkId }
|
||||
}
|
||||
if (name === 'loadbalancers') {
|
||||
if (!this.queryLbRuleId && !this.defaultNetworkId) {
|
||||
continue
|
||||
}
|
||||
if (this.queryLbRuleId) {
|
||||
param.options = { id: this.queryLbRuleId }
|
||||
}
|
||||
}
|
||||
const shouldLoad = !('isLoad' in param) || param.isLoad
|
||||
if (!shouldLoad) continue
|
||||
if (this.isModernImageSelection && name === 'guestOsCategories') {
|
||||
guestOsFetch = this.fetchGuestOsCategories(true)
|
||||
} else {
|
||||
this.fetchOptions(param, name, ['zones'])
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isModernImageSelection && guestOsFetch) {
|
||||
await guestOsFetch
|
||||
}
|
||||
this.fetchAllTemplates()
|
||||
this.updateTemplateKey()
|
||||
this.formModel = toRaw(this.form)
|
||||
},
|
||||
onSelectZoneId (value) {
|
||||
if (this.dataPreFill.zoneid !== value) {
|
||||
this.dataPreFill = {}
|
||||
}
|
||||
this.zoneId = value
|
||||
this.zone = _.find(this.options.zones, (option) => option.id === value)
|
||||
this.zoneSelected = true
|
||||
this.isZoneSelectedMultiArch = this.zone.ismultiarch
|
||||
if (this.isZoneSelectedMultiArch) {
|
||||
this.selectedArchitecture = this.architectureTypes.opts[0].id
|
||||
}
|
||||
this.selectedZone = this.zoneId
|
||||
this.form.zoneid = this.zoneId
|
||||
this.form.templateid = undefined
|
||||
this.tabKey = 'templateid'
|
||||
_.each(this.params, (param, name) => {
|
||||
if (this.networkId && name === 'networks') {
|
||||
param.options = {
|
||||
id: this.networkId
|
||||
}
|
||||
}
|
||||
if (this.lbRuleId && name === 'loadbalancers') {
|
||||
param.options = {
|
||||
id: this.lbRuleId
|
||||
}
|
||||
}
|
||||
if (!('isLoad' in param) || param.isLoad) {
|
||||
this.fetchOptions(param, name, ['zones'])
|
||||
}
|
||||
})
|
||||
if (this.tabKey === 'templateid') {
|
||||
this.fetchAllTemplates()
|
||||
}
|
||||
this.updateTemplateKey()
|
||||
this.formModel = toRaw(this.form)
|
||||
this.resetTemplatesList()
|
||||
this.fetchZoneOptions()
|
||||
},
|
||||
onSelectGuestOsCategory (value) {
|
||||
this.form.guestoscategoryid = value
|
||||
this.fetchAllTemplates(this.imageSearchFilters)
|
||||
},
|
||||
handleSearchFilter (name, options) {
|
||||
this.params[name].options = { ...this.params[name].options, ...options }
|
||||
@ -3184,7 +3349,7 @@ export default {
|
||||
.vm-info-card {
|
||||
.ant-card-body {
|
||||
min-height: 250px;
|
||||
max-height: calc(100vh - 150px);
|
||||
max-height: calc(100vh - 250px);
|
||||
overflow-y: auto;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -34,72 +34,70 @@
|
||||
<div style="margin-top: 15px">
|
||||
<span>{{ $t('message.select.a.zone') }}</span><br/>
|
||||
<a-form-item :label="$t('label.zoneid')" name="zoneid" ref="zoneid">
|
||||
<div v-if="zones.length <= 8">
|
||||
<a-row type="flex" :gutter="[16, 18]" justify="start">
|
||||
<div v-for="(zoneItem, idx) in zones" :key="idx">
|
||||
<a-radio-group
|
||||
:key="idx"
|
||||
:size="large"
|
||||
v-model:value="form.zoneid"
|
||||
@change="onSelectZoneId(zoneItem.id)">
|
||||
<a-col :span="6">
|
||||
<a-radio-button
|
||||
:value="zoneItem.id"
|
||||
style="border-width: 2px"
|
||||
class="zone-radio-button">
|
||||
<span>
|
||||
<resource-icon
|
||||
v-if="zoneItem && zoneItem.icon && zoneItem.icon.base64image"
|
||||
:image="zoneItem.icon.base64image"
|
||||
size="2x" />
|
||||
<global-outlined size="2x" v-else />
|
||||
{{ zoneItem.name }}
|
||||
</span>
|
||||
</a-radio-button>
|
||||
</a-col>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</a-row>
|
||||
</div>
|
||||
<a-select
|
||||
v-else
|
||||
v-model:value="form.zoneid"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
@change="onSelectZoneId"
|
||||
:loading="loading.zones"
|
||||
v-focus="true"
|
||||
>
|
||||
<a-select-option v-for="zone1 in zones" :key="zone1.id" :label="zone1.name">
|
||||
<span>
|
||||
<resource-icon v-if="zone1.icon && zone1.icon.base64image" :image="zone1.icon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ zone1.name }}
|
||||
</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<zone-block-radio-group-select
|
||||
:items="zones"
|
||||
:selectedValue="form.zoneid"
|
||||
@change="onSelectZoneId" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="$t('label.template')"
|
||||
v-if="!zoneSelected || isZoneSelectedMultiArch"
|
||||
:title="$t('label.arch')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template #description>
|
||||
<div v-if="zoneSelected" style="margin-top: 15px">
|
||||
{{ $t('message.instance.architecture') }}
|
||||
<block-radio-group-select
|
||||
style="margin-top: 5px;"
|
||||
:items="architectureTypes.opts"
|
||||
:selectedValue="selectedArchitecture"
|
||||
@change="changeArchitecture">
|
||||
<template #radio-option="{ item }">
|
||||
<span>{{ item.name || item.description }}</span>
|
||||
</template>
|
||||
<template #select-option="{ item }">
|
||||
<span>{{ item.name || item.description }}</span>
|
||||
</template>
|
||||
</block-radio-group-select>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
<a-step
|
||||
:title="$t('label.template.select')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template #description>
|
||||
<div v-if="zoneSelected" style="margin-top: 15px">
|
||||
<os-based-image-selection
|
||||
v-if="isModernImageSelection"
|
||||
:imageTypeSelectionAllowed="false"
|
||||
:imagePreSelected="!!this.queryTemplateId"
|
||||
:guestOsCategoriesSelectionDisallowed="guestOsCategoriesSelectionDisallowed"
|
||||
:guestOsCategories="options.guestOsCategories"
|
||||
:guestOsCategoriesLoading="loading.guestOsCategories"
|
||||
:selectedGuestOsCategoryId="form.guestoscategoryid"
|
||||
:imageItems="options.templates"
|
||||
:imagesLoading="loading.templates"
|
||||
:diskSizeSelectionDeployAsIsMessageVisible="template && template.deployasis"
|
||||
:rootDiskOverrideDisabled="rootDiskSizeFixed > 0 || (template && template.deployasis) || showOverrideDiskOfferingOption"
|
||||
:rootDiskOverrideChecked="form.rootdisksizeitem"
|
||||
:filterOption="filterOption"
|
||||
:preFillContent="dataPreFill"
|
||||
@change-guest-os-category="onSelectGuestOsCategory"
|
||||
@handle-image-search-filter="($event) => fetchAllTemplates($event)"
|
||||
@update-image="updateFieldValue"
|
||||
@update-disk-size="updateFieldValue" />
|
||||
<a-card
|
||||
:tabList="tabList"
|
||||
:activeTabKey="tabKey"
|
||||
@tabChange="key => onTabChange(key, 'tabKey')">
|
||||
<div v-if="tabKey === 'templateid'">
|
||||
v-else
|
||||
:tabList="imageTypeList"
|
||||
:activeTabKey="imageType">
|
||||
<div>
|
||||
{{ $t('message.template.desc') }}
|
||||
<template-iso-selection
|
||||
input-decorator="templateid"
|
||||
:items="options.templates"
|
||||
:selected="tabKey"
|
||||
:selected="imageType"
|
||||
:loading="loading.templates"
|
||||
:preFillContent="dataPreFill"
|
||||
:key="templateKey"
|
||||
@ -109,10 +107,10 @@
|
||||
{{ $t('label.override.rootdisk.size') }}
|
||||
<a-switch
|
||||
v-model:checked="form.rootdisksizeitem"
|
||||
:disabled="rootDiskSizeFixed > 0 || template.deployasis || showOverrideDiskOfferingOption"
|
||||
:disabled="rootDiskSizeFixed > 0 || (template && template.deployasis) || showOverrideDiskOfferingOption"
|
||||
@change="val => { showRootDiskSizeChanger = val }"
|
||||
style="margin-left: 10px;"/>
|
||||
<div v-if="template.deployasis"> {{ $t('message.deployasis') }} </div>
|
||||
<div v-if="template && template.deployasis"> {{ $t('message.deployasis') }} </div>
|
||||
</div>
|
||||
<disk-size-selection
|
||||
v-if="showRootDiskSizeChanger"
|
||||
@ -148,9 +146,7 @@
|
||||
v-model:value="form.templateConfiguration"
|
||||
defaultActiveFirstOption
|
||||
:placeholder="$t('message.ovf.configurations')"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:filterOption="filterOption"
|
||||
@change="onSelectTemplateConfigurationId"
|
||||
>
|
||||
<a-select-option v-for="opt in templateConfigurations" :key="opt.id" :label="opt.name || opt.description">
|
||||
@ -207,19 +203,10 @@
|
||||
<a-input v-model:value="form.memory"/>
|
||||
</a-form-item>
|
||||
</span>
|
||||
<span v-if="tabKey!=='isoid'">
|
||||
{{ $t('label.override.root.diskoffering') }}
|
||||
<a-switch
|
||||
v-model:checked="showOverrideDiskOfferingOption"
|
||||
:checked="serviceOffering && !serviceOffering.diskofferingstrictness && showOverrideDiskOfferingOption"
|
||||
:disabled="(serviceOffering && serviceOffering.diskofferingstrictness)"
|
||||
@change="val => { updateOverrideRootDiskShowParam(val) }"
|
||||
style="margin-left: 10px;"/>
|
||||
</span>
|
||||
<span v-if="tabKey!=='isoid' && serviceOffering && !serviceOffering.diskofferingstrictness">
|
||||
<span v-if="imageType!=='isoid' && serviceOffering && !serviceOffering.diskofferingstrictness">
|
||||
<a-step
|
||||
:status="zoneSelected ? 'process' : 'wait'"
|
||||
v-if="!template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
v-if="template && !template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
<template #description>
|
||||
<div v-if="zoneSelected">
|
||||
<multi-disk-selection
|
||||
@ -243,7 +230,7 @@
|
||||
:value="overrideDiskOffering ? overrideDiskOffering.id : ''"
|
||||
:loading="loading.diskOfferings"
|
||||
:preFillContent="dataPreFill"
|
||||
:isIsoSelected="tabKey==='isoid'"
|
||||
:isIsoSelected="imageType==='isoid'"
|
||||
:isRootDiskOffering="true"
|
||||
@on-selected-root-disk-size="onSelectRootDiskSize"
|
||||
@select-disk-offering-item="($event) => updateOverrideDiskOffering($event)"
|
||||
@ -272,7 +259,7 @@
|
||||
<a-step
|
||||
:title="$t('label.data.disk')"
|
||||
:status="zoneSelected ? 'process' : 'wait'"
|
||||
v-if="!template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
v-if="template && !template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
|
||||
<template #description>
|
||||
<div v-if="zoneSelected">
|
||||
<multi-disk-selection
|
||||
@ -285,7 +272,7 @@
|
||||
</a-step>
|
||||
<a-step
|
||||
v-else-if="vm.templateid && template.templatetype !== 'VNF'"
|
||||
:title="tabKey === 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
|
||||
:title="imageType === 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
|
||||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template #description>
|
||||
<div v-if="zoneSelected">
|
||||
@ -304,7 +291,7 @@
|
||||
:value="diskOffering ? diskOffering.id : ''"
|
||||
:loading="loading.diskOfferings"
|
||||
:preFillContent="dataPreFill"
|
||||
:isIsoSelected="tabKey==='isoid'"
|
||||
:isIsoSelected="imageType==='isoid'"
|
||||
@on-selected-disk-size="onSelectDiskSize"
|
||||
@select-disk-offering-item="($event) => updateDiskOffering($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('diskOfferings', $event)"
|
||||
@ -439,9 +426,7 @@
|
||||
optionFilterProp="value"
|
||||
v-model:value="form['properties.' + escapePropertyKey(property.key)]"
|
||||
:placeholder="property.description"
|
||||
:filterOption="(input, option) => {
|
||||
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:filterOption="filterOption"
|
||||
>
|
||||
<a-select-option v-for="opt in getPropertyQualifiers(property.qualifiers, 'select')" :key="opt">
|
||||
{{ opt }}
|
||||
@ -520,7 +505,7 @@
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div
|
||||
v-if="vm.templateid && ['KVM', 'VMware', 'XenServer'].includes(hypervisor) && !template.deployasis">
|
||||
v-if="vm.templateid && ['KVM', 'VMware', 'XenServer'].includes(hypervisor) && this.template && !template.deployasis">
|
||||
<a-form-item :label="$t('label.boottype')" name="boottype" ref="boottype">
|
||||
<a-select
|
||||
v-model:value="form.boottype"
|
||||
@ -547,7 +532,7 @@
|
||||
</div>
|
||||
<a-form-item
|
||||
:label="$t('label.bootintosetup')"
|
||||
v-if="zoneSelected && ((tabKey === 'isoid' && hypervisor === 'VMware') || (tabKey === 'templateid' && template && template.hypervisor === 'VMware'))"
|
||||
v-if="zoneSelected && ((imageType === 'isoid' && hypervisor === 'VMware') || (imageType === 'templateid' && template && template.hypervisor === 'VMware'))"
|
||||
name="bootintosetup"
|
||||
ref="bootintosetup">
|
||||
<a-switch v-model:checked="form.bootintosetup" />
|
||||
@ -567,13 +552,13 @@
|
||||
<a-form-item :label="$t('label.userdata')">
|
||||
<a-card>
|
||||
<div v-if="this.template && this.template.userdataid">
|
||||
<a-text type="primary">
|
||||
<a-typography-text>
|
||||
Userdata "{{ $t(this.template.userdataname) }}" is linked with template "{{ $t(this.template.name) }}" with override policy "{{ $t(this.template.userdatapolicy) }}"
|
||||
</a-text><br/><br/>
|
||||
</a-typography-text><br/><br/>
|
||||
<div v-if="templateUserDataParams.length > 0 && !doUserdataOverride">
|
||||
<a-text type="primary" v-if="this.template && this.template.userdataid && templateUserDataParams.length > 0">
|
||||
<a-typography-text v-if="this.template && this.template.userdataid && templateUserDataParams.length > 0">
|
||||
Enter the values for the variables in userdata
|
||||
</a-text>
|
||||
</a-typography-text>
|
||||
<a-input-group>
|
||||
<a-table
|
||||
size="small"
|
||||
@ -592,13 +577,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="this.iso && this.iso.userdataid">
|
||||
<a-text type="primary">
|
||||
<a-typography-text>
|
||||
Userdata "{{ $t(this.iso.userdataname) }}" is linked with ISO "{{ $t(this.iso.name) }}" with override policy "{{ $t(this.iso.userdatapolicy) }}"
|
||||
</a-text><br/><br/>
|
||||
</a-typography-text><br/><br/>
|
||||
<div v-if="templateUserDataParams.length > 0 && !doUserdataOverride">
|
||||
<a-text type="primary" v-if="this.iso && this.iso.userdataid && templateUserDataParams.length > 0">
|
||||
<a-typography-text v-if="this.iso && this.iso.userdataid && templateUserDataParams.length > 0">
|
||||
Enter the values for the variables in userdata
|
||||
</a-text>
|
||||
</a-typography-text>
|
||||
<a-input-group>
|
||||
<a-table
|
||||
size="small"
|
||||
@ -864,12 +849,15 @@ import eventBus from '@/config/eventBus'
|
||||
|
||||
import InfoCard from '@/components/view/InfoCard'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import ZoneBlockRadioGroupSelect from '@views/compute/wizard/ZoneBlockRadioGroupSelect.vue'
|
||||
import BlockRadioGroupSelect from '@/components/widgets/BlockRadioGroupSelect'
|
||||
import ComputeOfferingSelection from '@views/compute/wizard/ComputeOfferingSelection'
|
||||
import ComputeSelection from '@views/compute/wizard/ComputeSelection'
|
||||
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
|
||||
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
|
||||
import MultiDiskSelection from '@views/compute/wizard/MultiDiskSelection'
|
||||
import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
|
||||
import OsBasedImageSelection from '@views/compute/wizard/OsBasedImageSelection'
|
||||
import AffinityGroupSelection from '@views/compute/wizard/AffinityGroupSelection'
|
||||
import NetworkSelection from '@views/compute/wizard/NetworkSelection'
|
||||
import NetworkConfiguration from '@views/compute/wizard/NetworkConfiguration'
|
||||
@ -883,21 +871,24 @@ import InstanceNicsNetworkSelectListView from '@/components/view/InstanceNicsNet
|
||||
export default {
|
||||
name: 'Wizard',
|
||||
components: {
|
||||
InfoCard,
|
||||
ResourceIcon,
|
||||
ZoneBlockRadioGroupSelect,
|
||||
BlockRadioGroupSelect,
|
||||
SshKeyPairSelection,
|
||||
UserDataSelection,
|
||||
NetworkConfiguration,
|
||||
NetworkSelection,
|
||||
AffinityGroupSelection,
|
||||
TemplateIsoSelection,
|
||||
OsBasedImageSelection,
|
||||
DiskSizeSelection,
|
||||
MultiDiskSelection,
|
||||
DiskOfferingSelection,
|
||||
InfoCard,
|
||||
ComputeOfferingSelection,
|
||||
ComputeSelection,
|
||||
SecurityGroupSelection,
|
||||
VnfNicsSelection,
|
||||
ResourceIcon,
|
||||
TooltipLabel,
|
||||
InstanceNicsNetworkSelectListView
|
||||
},
|
||||
@ -917,7 +908,10 @@ export default {
|
||||
podId: null,
|
||||
clusterId: null,
|
||||
zoneSelected: false,
|
||||
isZoneSelectedMultiArch: false,
|
||||
dynamicscalingenabled: true,
|
||||
imageType: 'templateid',
|
||||
imageSearchFilters: null,
|
||||
templateKey: 0,
|
||||
showRegisteredUserdata: true,
|
||||
doUserdataOverride: false,
|
||||
@ -943,6 +937,7 @@ export default {
|
||||
disksize: null
|
||||
},
|
||||
options: {
|
||||
guestOsCategories: [],
|
||||
templates: {},
|
||||
isos: {},
|
||||
hypervisors: [],
|
||||
@ -966,6 +961,7 @@ export default {
|
||||
rowCount: {},
|
||||
loading: {
|
||||
deploy: false,
|
||||
guestOsCategories: false,
|
||||
templates: false,
|
||||
isos: false,
|
||||
hypervisors: false,
|
||||
@ -1029,23 +1025,10 @@ export default {
|
||||
templateUserDataParams: [],
|
||||
templateUserDataValues: {},
|
||||
overrideDiskOffering: {},
|
||||
templateFilter: [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
],
|
||||
isoFilter: [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
],
|
||||
initDataConfig: {},
|
||||
defaultnetworkid: '',
|
||||
networkConfig: [],
|
||||
dataNetworkCreated: [],
|
||||
tabKey: 'templateid',
|
||||
userdataTabKey: 'userdataregistered',
|
||||
dataPreFill: {},
|
||||
showDetails: false,
|
||||
@ -1065,7 +1048,9 @@ export default {
|
||||
zones: [],
|
||||
selectedZone: '',
|
||||
formModel: {},
|
||||
nicToNetworkSelection: []
|
||||
nicToNetworkSelection: [],
|
||||
selectedArchitecture: null,
|
||||
architectureTypes: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -1201,6 +1186,18 @@ export default {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
name: 'enable.dynamic.scale.vm'
|
||||
}
|
||||
},
|
||||
guestOsCategories: {
|
||||
list: 'listOsCategories',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
isfeatured: true,
|
||||
isiso: _.get(this.form, 'imagetype') === 'isoid',
|
||||
arch: this.selectedArchitecture,
|
||||
isvnf: true,
|
||||
showicon: true
|
||||
},
|
||||
field: 'guestoscategoryid'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1274,34 +1271,38 @@ export default {
|
||||
templateConfigurationExists () {
|
||||
return this.vm.templateid && this.templateConfigurations && this.templateConfigurations.length > 0
|
||||
},
|
||||
templateId () {
|
||||
queryZoneId () {
|
||||
return this.$route.query.zoneid || null
|
||||
},
|
||||
queryArchId () {
|
||||
return this.$route.query.arch || null
|
||||
},
|
||||
queryTemplateId () {
|
||||
return this.$route.query.templateid || null
|
||||
},
|
||||
isoId () {
|
||||
return this.$route.query.isoid || null
|
||||
},
|
||||
networkId () {
|
||||
queryNetworkId () {
|
||||
return this.$route.query.networkid || null
|
||||
},
|
||||
tabList () {
|
||||
const tabList = [{
|
||||
queryGuestOsCategoryId () {
|
||||
return this.$route.query.oscategoryid || null
|
||||
},
|
||||
imageTypeList () {
|
||||
return [{
|
||||
key: 'templateid',
|
||||
tab: this.$t('label.templates')
|
||||
}]
|
||||
return tabList
|
||||
},
|
||||
userdataTabList () {
|
||||
let tabList = []
|
||||
tabList = [{
|
||||
return [
|
||||
{
|
||||
key: 'userdataregistered',
|
||||
tab: this.$t('label.userdata.registered')
|
||||
},
|
||||
{
|
||||
key: 'userdatatext',
|
||||
tab: this.$t('label.userdata.text')
|
||||
}]
|
||||
|
||||
return tabList
|
||||
}
|
||||
]
|
||||
},
|
||||
showVnfNicsSection () {
|
||||
return this.networks && this.networks.length > 0 && this.vm.templateid && this.templateVnfNics && this.templateVnfNics.length > 0
|
||||
@ -1339,6 +1340,21 @@ export default {
|
||||
},
|
||||
isCustomizedIOPS () {
|
||||
return this.rootDiskSelected?.iscustomizediops || this.serviceOffering?.iscustomizediops || false
|
||||
},
|
||||
isModernImageSelection () {
|
||||
return this.$config.imageSelectionInterface === undefined || this.$config.imageSelectionInterface === 'modern'
|
||||
},
|
||||
imageSelection () {
|
||||
return this.isModernImageSelection ? 'modern' : 'legacy'
|
||||
},
|
||||
showUserCategoryForModernImageSelection () {
|
||||
return this.$config.showUserCategoryForModernImageSelection === undefined || this.$config.showUserCategoryForModernImageSelection
|
||||
},
|
||||
showAllCategoryForModernImageSelection () {
|
||||
return this.$config.showAllCategoryForModernImageSelection
|
||||
},
|
||||
guestOsCategoriesSelectionDisallowed () {
|
||||
return (!this.queryGuestOsCategoryId || this.options.guestOsCategories.length === 0) && (!!this.queryTemplateId || !!this.queryIsoId)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -1354,7 +1370,7 @@ export default {
|
||||
Object.keys(vnfAppConfig).forEach(field => {
|
||||
this.vm[field] = this.vnfAppConfig[field]
|
||||
})
|
||||
this.template = ''
|
||||
this.template = null
|
||||
for (const key in this.options.templates) {
|
||||
var template = _.find(_.get(this.options.templates[key], 'template', []), (option) => option.id === vnfAppConfig.templateid)
|
||||
if (template) {
|
||||
@ -1363,7 +1379,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
this.iso = ''
|
||||
this.iso = null
|
||||
for (const key in this.options.isos) {
|
||||
var iso = _.find(_.get(this.options.isos[key], 'iso', []), (option) => option.id === vnfAppConfig.isoid)
|
||||
if (iso) {
|
||||
@ -1487,7 +1503,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.template.deployasis && this.template.childtemplates && this.template.childtemplates.length > 0) {
|
||||
if (this.template && !this.template.deployasis && this.template.childtemplates && this.template.childtemplates.length > 0) {
|
||||
this.vm.diskofferingid = ''
|
||||
this.vm.diskofferingname = ''
|
||||
this.vm.diskofferingsize = ''
|
||||
@ -1527,7 +1543,6 @@ export default {
|
||||
provide () {
|
||||
return {
|
||||
vmFetchTemplates: this.fetchAllTemplates,
|
||||
vmFetchIsos: this.fetchAllIsos,
|
||||
vmFetchNetworks: this.fetchNetwork
|
||||
}
|
||||
},
|
||||
@ -1626,6 +1641,27 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
getImageFilters (params, forReset) {
|
||||
if (this.isModernImageSelection) {
|
||||
if (this.form.guestoscategoryid === '0') {
|
||||
return ['self']
|
||||
}
|
||||
if (this.isModernImageSelection && params && !forReset) {
|
||||
if (params.featured) {
|
||||
return ['featured']
|
||||
} else if (params.public) {
|
||||
return ['community']
|
||||
}
|
||||
}
|
||||
return this.isNormalAndDomainUser ? ['executable'] : ['all']
|
||||
}
|
||||
return [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
]
|
||||
},
|
||||
getPropertyQualifiers (qualifiers, type) {
|
||||
var result = ''
|
||||
switch (type) {
|
||||
@ -1665,19 +1701,21 @@ export default {
|
||||
let zones = []
|
||||
let apiName = ''
|
||||
const params = {}
|
||||
if (this.templateId) {
|
||||
if (this.queryZoneId) {
|
||||
zones.push(this.queryZoneId)
|
||||
if (this.queryTemplateId) {
|
||||
this.dataPreFill.templateid = this.queryTemplateId
|
||||
}
|
||||
return resolve(zones)
|
||||
} else if (this.queryTemplateId) {
|
||||
apiName = 'listVnfTemplates'
|
||||
params.listall = true
|
||||
params.templatefilter = this.isNormalAndDomainUser ? 'executable' : 'all'
|
||||
params.id = this.templateId
|
||||
} else if (this.isoId) {
|
||||
params.id = this.queryTemplateId
|
||||
this.dataPreFill.templateid = this.queryTemplateId
|
||||
} else if (this.queryNetworkId) {
|
||||
params.listall = true
|
||||
params.isofilter = this.isNormalAndDomainUser ? 'executable' : 'all'
|
||||
params.id = this.isoId
|
||||
apiName = 'listIsos'
|
||||
} else if (this.networkId) {
|
||||
params.listall = true
|
||||
params.id = this.networkId
|
||||
params.id = this.queryNetworkId
|
||||
apiName = 'listNetworks'
|
||||
}
|
||||
if (!apiName) return resolve(zones)
|
||||
@ -1701,6 +1739,10 @@ export default {
|
||||
})
|
||||
},
|
||||
async fetchData () {
|
||||
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
|
||||
if (this.queryArchId) {
|
||||
this.architectureTypes.opts = this.architectureTypes.opts.filter(o => o.id === this.queryArchId)
|
||||
}
|
||||
const zones = await this.fetchZoneByQuery()
|
||||
if (zones && zones.length === 1) {
|
||||
this.selectedZone = zones[0]
|
||||
@ -1816,27 +1858,20 @@ export default {
|
||||
},
|
||||
updateFieldValue (name, value) {
|
||||
if (name === 'templateid') {
|
||||
this.tabKey = 'templateid'
|
||||
this.form.templateid = value
|
||||
this.form.isoid = null
|
||||
this.resetFromTemplateConfiguration()
|
||||
let template = ''
|
||||
for (const key in this.options.templates) {
|
||||
var t = _.find(_.get(this.options.templates[key], 'template', []), (option) => option.id === value)
|
||||
if (t) {
|
||||
this.template = t
|
||||
this.templateConfigurations = []
|
||||
this.selectedTemplateConfiguration = {}
|
||||
this.templateNics = []
|
||||
this.templateLicenses = []
|
||||
this.templateProperties = {}
|
||||
this.templateVnfNics = []
|
||||
this.updateTemplateParameters()
|
||||
template = t
|
||||
for (const entry of Object.values(this.options.templates)) {
|
||||
template = entry?.template.find(option => option.id === value) || null
|
||||
if (template) {
|
||||
this.template = template
|
||||
break
|
||||
}
|
||||
}
|
||||
if (template) {
|
||||
this.resetTemplateAssociatedResources()
|
||||
this.updateTemplateParameters()
|
||||
var size = template.size / (1024 * 1024 * 1024) || 0 // bytes to GB
|
||||
this.dataPreFill.minrootdisksize = Math.ceil(size)
|
||||
this.updateTemplateLinkedUserData(template.userdataid)
|
||||
@ -1851,19 +1886,6 @@ export default {
|
||||
this.form.iodriverpolicy = template.details?.['io.policy']
|
||||
this.form.keyboard = template.details?.keyboard
|
||||
}
|
||||
} else if (name === 'isoid') {
|
||||
this.templateConfigurations = []
|
||||
this.selectedTemplateConfiguration = {}
|
||||
this.templateNics = []
|
||||
this.templateLicenses = []
|
||||
this.templateProperties = {}
|
||||
this.templateVnfNics = []
|
||||
this.tabKey = 'isoid'
|
||||
this.resetFromTemplateConfiguration()
|
||||
this.form.isoid = value
|
||||
this.form.templateid = null
|
||||
this.updateTemplateLinkedUserData(this.iso.userdataid)
|
||||
this.userdataDefaultOverridePolicy = this.iso.userdatapolicy
|
||||
} else if (['cpuspeed', 'cpunumber', 'memory'].includes(name)) {
|
||||
this.vm[name] = value
|
||||
this.form[name] = value
|
||||
@ -1977,6 +1999,60 @@ export default {
|
||||
getText (option) {
|
||||
return _.get(option, 'displaytext', _.get(option, 'name'))
|
||||
},
|
||||
fetchGuestOsCategories (skipFetchImages) {
|
||||
const key = 'guestOsCategories'
|
||||
const params = this.params[key]
|
||||
if (this.queryGuestOsCategoryId) {
|
||||
params.options.id = this.queryGuestOsCategoryId
|
||||
} else if (this.queryTemplateId) {
|
||||
this.fetchAllTemplates()
|
||||
return Promise.resolve()
|
||||
}
|
||||
return this.fetchOptions(params, key, ['zones'])
|
||||
.then((res) => {
|
||||
if (!this.options.guestOsCategories) {
|
||||
this.options.guestOsCategories = []
|
||||
}
|
||||
if (!this.queryGuestOsCategoryId) {
|
||||
if (this.showUserCategoryForModernImageSelection) {
|
||||
const userCategory = {
|
||||
id: '0',
|
||||
name: this.$t('label.user')
|
||||
}
|
||||
if (this.$store.getters.avatar) {
|
||||
userCategory.icon = {
|
||||
base64image: this.$store.getters.avatar
|
||||
}
|
||||
}
|
||||
this.options.guestOsCategories.push(userCategory)
|
||||
}
|
||||
if (this.showAllCategoryForModernImageSelection) {
|
||||
this.options.guestOsCategories.push({
|
||||
id: '-1',
|
||||
name: this.$t('label.all')
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.options.guestOsCategories.length > 0) {
|
||||
this.form.guestoscategoryid = this.options.guestOsCategories[0].id
|
||||
}
|
||||
if (skipFetchImages) {
|
||||
return
|
||||
}
|
||||
this.fetchAllTemplates()
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('Error fetching guestOsCategories:', e)
|
||||
})
|
||||
},
|
||||
changeArchitecture (arch) {
|
||||
this.selectedArchitecture = arch
|
||||
if (this.isModernImageSelection) {
|
||||
this.fetchGuestOsCategories()
|
||||
return
|
||||
}
|
||||
this.fetchAllTemplates()
|
||||
},
|
||||
handleSubmitAndStay (e) {
|
||||
this.form.stayonpage = true
|
||||
this.handleSubmit(e.domEvent)
|
||||
@ -2096,7 +2172,7 @@ export default {
|
||||
createVnfAppData.userdata = this.$toBase64AndURIEncoded(values.userdata)
|
||||
}
|
||||
// step 2: select template/iso
|
||||
if (this.tabKey === 'templateid') {
|
||||
if (this.imageType === 'templateid') {
|
||||
createVnfAppData.templateid = values.templateid
|
||||
values.hypervisor = null
|
||||
} else {
|
||||
@ -2105,7 +2181,7 @@ export default {
|
||||
|
||||
if (this.showRootDiskSizeChanger && values.rootdisksize && values.rootdisksize > 0) {
|
||||
createVnfAppData.rootdisksize = values.rootdisksize
|
||||
} else if (this.rootDiskSizeFixed > 0 && !this.template.deployasis) {
|
||||
} else if (this.rootDiskSizeFixed > 0 && this.template && !this.template.deployasis) {
|
||||
createVnfAppData.rootdisksize = this.rootDiskSizeFixed
|
||||
}
|
||||
|
||||
@ -2320,9 +2396,10 @@ export default {
|
||||
} else {
|
||||
credentials.push(this.$t('message.vnf.no.credentials'))
|
||||
}
|
||||
const credentialsDesc = credentials.join('<br>')
|
||||
this.$notification.success({
|
||||
message: `${this.$t('message.vnf.credentials.default')} ` + name,
|
||||
description: (<span v-html={credentials.join('<br>')}></span>),
|
||||
description: (<span v-html={credentialsDesc}></span>),
|
||||
duration: 0
|
||||
})
|
||||
eventBus.emit('vm-refresh-data')
|
||||
@ -2394,10 +2471,9 @@ export default {
|
||||
})
|
||||
},
|
||||
fetchOptions (param, name, exclude) {
|
||||
if (exclude && exclude.length > 0) {
|
||||
if (exclude.includes(name)) {
|
||||
return
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (exclude && exclude.length > 0 && exclude.includes(name)) {
|
||||
return resolve(null)
|
||||
}
|
||||
this.loading[name] = true
|
||||
param.loading = true
|
||||
@ -2412,10 +2488,10 @@ export default {
|
||||
if (Object.keys(responseItem).length === 0) {
|
||||
this.rowCount[name] = 0
|
||||
this.options[name] = []
|
||||
return
|
||||
return resolve(null)
|
||||
}
|
||||
if (!responseKey.includes('response')) {
|
||||
return
|
||||
return resolve(null)
|
||||
}
|
||||
_.map(responseItem, (response, key) => {
|
||||
if (key === 'count') {
|
||||
@ -2449,24 +2525,37 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
resolve(response)
|
||||
}).catch(function (error) {
|
||||
console.log(error.stack)
|
||||
param.loading = false
|
||||
reject(error)
|
||||
}).finally(() => {
|
||||
this.loading[name] = false
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchTemplates (templateFilter, params) {
|
||||
const args = Object.assign({}, params)
|
||||
if (args.keyword || args.category !== templateFilter) {
|
||||
if (this.isModernImageSelection && this.form.guestoscategoryid && !['-1', '0'].includes(this.form.guestoscategoryid)) {
|
||||
args.oscategoryid = this.form.guestoscategoryid
|
||||
}
|
||||
if (args.keyword || (args.category && args.category !== templateFilter)) {
|
||||
args.page = 1
|
||||
args.pageSize = args.pageSize || 10
|
||||
}
|
||||
args.zoneid = _.get(this.zone, 'id')
|
||||
if (this.isZoneSelectedMultiArch) {
|
||||
args.arch = this.selectedArchitecture
|
||||
}
|
||||
args.templatefilter = templateFilter
|
||||
args.details = 'all'
|
||||
args.showicon = 'true'
|
||||
args.id = this.templateId
|
||||
args.id = this.queryTemplateId
|
||||
|
||||
delete args.category
|
||||
delete args.public
|
||||
delete args.featured
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
api('listVnfTemplates', args).then((response) => {
|
||||
@ -2477,39 +2566,20 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchIsos (isoFilter, params) {
|
||||
const args = Object.assign({}, params)
|
||||
if (args.keyword || args.category !== isoFilter) {
|
||||
args.page = 1
|
||||
args.pageSize = args.pageSize || 10
|
||||
}
|
||||
args.zoneid = _.get(this.zone, 'id')
|
||||
args.isoFilter = isoFilter
|
||||
args.bootable = true
|
||||
args.showicon = 'true'
|
||||
args.id = this.isoId
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
api('listIsos', args).then((response) => {
|
||||
resolve(response)
|
||||
}).catch((reason) => {
|
||||
// ToDo: Handle errors
|
||||
reject(reason)
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchAllTemplates (params) {
|
||||
const promises = []
|
||||
const templates = {}
|
||||
this.loading.templates = true
|
||||
this.templateFilter.forEach((filter) => {
|
||||
this.imageSearchFilters = params
|
||||
const templateFilters = this.getImageFilters(params)
|
||||
templateFilters.forEach((filter) => {
|
||||
templates[filter] = { count: 0, template: [] }
|
||||
promises.push(this.fetchTemplates(filter, params))
|
||||
})
|
||||
this.options.templates = templates
|
||||
Promise.all(promises).then((response) => {
|
||||
response.forEach((resItem, idx) => {
|
||||
templates[this.templateFilter[idx]] = _.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : resItem.listtemplatesresponse
|
||||
templates[templateFilters[idx]] = _.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : resItem.listtemplatesresponse
|
||||
this.options.templates = { ...templates }
|
||||
})
|
||||
}).catch((reason) => {
|
||||
@ -2518,36 +2588,60 @@ export default {
|
||||
this.loading.templates = false
|
||||
})
|
||||
},
|
||||
fetchAllIsos (params) {
|
||||
const promises = []
|
||||
const isos = {}
|
||||
this.loading.isos = true
|
||||
this.isoFilter.forEach((filter) => {
|
||||
isos[filter] = { count: 0, iso: [] }
|
||||
promises.push(this.fetchIsos(filter, params))
|
||||
})
|
||||
this.options.isos = isos
|
||||
Promise.all(promises).then((response) => {
|
||||
response.forEach((resItem, idx) => {
|
||||
isos[this.isoFilter[idx]] = _.isEmpty(resItem.listisosresponse) ? { count: 0, iso: [] } : resItem.listisosresponse
|
||||
this.options.isos = { ...isos }
|
||||
})
|
||||
}).catch((reason) => {
|
||||
console.log(reason)
|
||||
}).finally(() => {
|
||||
this.loading.isos = false
|
||||
})
|
||||
},
|
||||
filterOption (input, option) {
|
||||
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0
|
||||
},
|
||||
resetTemplatesList () {
|
||||
const templates = {}
|
||||
const templateFilters = this.getImageFilters(null, true)
|
||||
templateFilters.forEach((filter) => {
|
||||
templates[filter] = { count: 0, template: [] }
|
||||
})
|
||||
this.options.templates = templates
|
||||
},
|
||||
resetTemplateAssociatedResources () {
|
||||
this.templateConfigurations = []
|
||||
this.selectedTemplateConfiguration = {}
|
||||
this.templateNics = []
|
||||
this.templateLicenses = []
|
||||
this.templateProperties = {}
|
||||
this.templateVnfNics = []
|
||||
},
|
||||
async fetchZoneOptions () {
|
||||
let guestOsFetch = null
|
||||
for (const [name, param] of Object.entries(this.params)) {
|
||||
if (this.queryNetworkId && name === 'networks') {
|
||||
param.options = { id: this.queryNetworkId }
|
||||
}
|
||||
const shouldLoad = !('isLoad' in param) || param.isLoad
|
||||
if (!shouldLoad) continue
|
||||
if (this.isModernImageSelection && name === 'guestOsCategories') {
|
||||
guestOsFetch = this.fetchGuestOsCategories(true)
|
||||
} else {
|
||||
this.fetchOptions(param, name, ['zones'])
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isModernImageSelection && guestOsFetch) {
|
||||
await guestOsFetch
|
||||
}
|
||||
this.fetchAllTemplates()
|
||||
this.updateTemplateKey()
|
||||
this.formModel = toRaw(this.form)
|
||||
},
|
||||
onSelectZoneId (value) {
|
||||
if (this.dataPreFill.zoneid !== value) {
|
||||
this.dataPreFill = {}
|
||||
}
|
||||
this.zoneId = value
|
||||
this.podId = null
|
||||
this.clusterId = null
|
||||
this.zone = _.find(this.options.zones, (option) => option.id === value)
|
||||
this.zoneSelected = true
|
||||
this.isZoneSelectedMultiArch = this.zone.ismultiarch
|
||||
if (this.isZoneSelectedMultiArch) {
|
||||
this.selectedArchitecture = this.architectureTypes.opts[0].id
|
||||
}
|
||||
this.form.startvm = true
|
||||
this.form.vnfconfiguremanagement = false
|
||||
this.form.vnfcidrlist = '0.0.0.0/0'
|
||||
@ -2558,27 +2652,8 @@ export default {
|
||||
this.form.hostid = undefined
|
||||
this.form.templateid = undefined
|
||||
this.form.isoid = undefined
|
||||
this.tabKey = 'templateid'
|
||||
if (this.isoId) {
|
||||
this.tabKey = 'isoid'
|
||||
}
|
||||
_.each(this.params, (param, name) => {
|
||||
if (this.networkId && name === 'networks') {
|
||||
param.options = {
|
||||
id: this.networkId
|
||||
}
|
||||
}
|
||||
if (!('isLoad' in param) || param.isLoad) {
|
||||
this.fetchOptions(param, name, ['zones'])
|
||||
}
|
||||
})
|
||||
if (this.tabKey === 'templateid') {
|
||||
this.fetchAllTemplates()
|
||||
} else {
|
||||
this.fetchAllIsos()
|
||||
}
|
||||
this.updateTemplateKey()
|
||||
this.formModel = toRaw(this.form)
|
||||
this.resetTemplatesList()
|
||||
this.fetchZoneOptions()
|
||||
},
|
||||
onSelectPodId (value) {
|
||||
this.podId = value
|
||||
@ -2603,15 +2678,16 @@ export default {
|
||||
this.form.hostid = undefined
|
||||
}
|
||||
},
|
||||
onSelectGuestOsCategory (value) {
|
||||
this.form.guestoscategoryid = value
|
||||
this.fetchAllTemplates(this.imageSearchFilters)
|
||||
},
|
||||
handleSearchFilter (name, options) {
|
||||
this.params[name].options = { ...this.params[name].options, ...options }
|
||||
this.fetchOptions(this.params[name], name)
|
||||
},
|
||||
onTabChange (key, type) {
|
||||
this[type] = key
|
||||
if (key === 'isoid') {
|
||||
this.fetchAllIsos()
|
||||
}
|
||||
},
|
||||
onUserdataTabChange (key, type) {
|
||||
this[type] = key
|
||||
|
||||
@ -19,28 +19,35 @@
|
||||
<a-form
|
||||
v-ctrl-enter="handleSubmit"
|
||||
@finish="handleSubmit"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-alert
|
||||
type="warning"
|
||||
show-icon
|
||||
>
|
||||
<template #message><span
|
||||
style="margin-bottom: 5px"
|
||||
v-html="$t('message.reinstall.vm')"
|
||||
/></template>
|
||||
layout="vertical">
|
||||
<a-alert type="warning" show-icon>
|
||||
<template #message>
|
||||
<span style="margin-bottom: 5px" v-html="$t('message.reinstall.vm')" />
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-form-item>
|
||||
<os-based-image-selection
|
||||
v-if="isModernImageSelection"
|
||||
:imageTypeSelectionAllowed="false"
|
||||
:guestOsCategories="options.guestOsCategories"
|
||||
:guestOsCategoriesLoading="loading.guestOsCategories"
|
||||
:selectedGuestOsCategoryId="selectedGuestOsCategoryId"
|
||||
:imageItems="options.templates"
|
||||
:imagesLoading="loading.templates"
|
||||
:filterOption="filterOption"
|
||||
:preFillContent="dataPreFill"
|
||||
@change-guest-os-category="onSelectGuestOsCategory"
|
||||
@handle-image-search-filter="($event) => fetchAllTemplates($event)"
|
||||
@update-image="updateFieldValue" />
|
||||
<a-form-item v-else>
|
||||
<template-iso-selection
|
||||
input-decorator="templateid"
|
||||
:items="templates"
|
||||
:items="options.templates"
|
||||
:selected="tabKey"
|
||||
:loading="loading.templates"
|
||||
:preFillContent="dataPrefill"
|
||||
:preFillContent="dataPreFill"
|
||||
:key="templateKey"
|
||||
@handle-search-filter="($event) => fetchAllTemplates($event)"
|
||||
@update-template-iso="updateFieldValue"
|
||||
/>
|
||||
@update-template-iso="updateFieldValue" />
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template #label>
|
||||
@ -56,12 +63,12 @@
|
||||
</a-form-item>
|
||||
<a-form-item v-if="overrideDiskOffering">
|
||||
<disk-offering-selection
|
||||
:items="diskOfferings"
|
||||
:row-count="diskOfferingCount"
|
||||
:items="options.diskOfferings"
|
||||
:row-count="count.diskOfferings"
|
||||
:zoneId="resource.zoneId"
|
||||
:value="diskOffering ? diskOffering.id : ''"
|
||||
:loading="loading.diskOfferings"
|
||||
:preFillContent="dataPrefill"
|
||||
:preFillContent="dataPreFill"
|
||||
:isIsoSelected="false"
|
||||
:isRootDiskOffering="true"
|
||||
@on-selected-disk-size="onSelectDiskSize"
|
||||
@ -127,6 +134,7 @@
|
||||
import { api } from '@/api'
|
||||
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
|
||||
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
|
||||
import OsBasedImageSelection from '@views/compute/wizard/OsBasedImageSelection'
|
||||
import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
import _ from 'lodash'
|
||||
@ -137,6 +145,7 @@ export default {
|
||||
DiskOfferingSelection,
|
||||
DiskSizeSelection,
|
||||
TemplateIsoSelection,
|
||||
OsBasedImageSelection,
|
||||
TooltipLabel
|
||||
},
|
||||
props: {
|
||||
@ -151,10 +160,19 @@ export default {
|
||||
overrideDiskOffering: false,
|
||||
overrideDiskSize: false,
|
||||
expungeDisk: false,
|
||||
selectedDiskOffering: {},
|
||||
selectedGuestOsCategoryId: null,
|
||||
options: {
|
||||
templates: {},
|
||||
diskOfferings: [],
|
||||
guestOsCategories: []
|
||||
},
|
||||
loading: {
|
||||
templates: false,
|
||||
diskOfferings: false
|
||||
diskOfferings: false,
|
||||
guestOsCategories: false
|
||||
},
|
||||
count: {
|
||||
diskOfferings: 0
|
||||
},
|
||||
rootDiskSizeKey: 'details[0].rootdisksize',
|
||||
minIopsKey: 'details[0].minIops',
|
||||
@ -162,16 +180,11 @@ export default {
|
||||
rootdisksize: 0,
|
||||
minIops: 0,
|
||||
maxIops: 0,
|
||||
templateFilter: [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
],
|
||||
diskOffering: {},
|
||||
diskOfferingCount: 0,
|
||||
imageSearchFilters: null,
|
||||
templateid: null,
|
||||
templateKey: 0,
|
||||
dataPrefill: {
|
||||
dataPreFill: {
|
||||
templateid: this.resource.templateid,
|
||||
diskofferingid: this.resource.diskofferingid
|
||||
}
|
||||
@ -183,10 +196,52 @@ export default {
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
computed: {
|
||||
isNormalAndDomainUser () {
|
||||
return ['DomainAdmin', 'User'].includes(this.$store.getters.userInfo.roletype)
|
||||
},
|
||||
isModernImageSelection () {
|
||||
return this.$config.imageSelectionInterface === undefined || this.$config.imageSelectionInterface === 'modern'
|
||||
},
|
||||
imageSelection () {
|
||||
return this.isModernImageSelection ? 'modern' : 'legacy'
|
||||
},
|
||||
showUserCategoryForModernImageSelection () {
|
||||
return this.$config.showUserCategoryForModernImageSelection === undefined || this.$config.showUserCategoryForModernImageSelection
|
||||
},
|
||||
showAllCategoryForModernImageSelection () {
|
||||
return this.$config.showAllCategoryForModernImageSelection
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.fetchDiskOfferings({})
|
||||
if (this.isModernImageSelection) {
|
||||
this.fetchGuestOsCategories()
|
||||
} else {
|
||||
this.fetchAllTemplates()
|
||||
}
|
||||
},
|
||||
getImageFilters (params, forReset) {
|
||||
if (this.isModernImageSelection) {
|
||||
if (this.selectedGuestOsCategoryId === '0') {
|
||||
return ['self']
|
||||
}
|
||||
if (this.isModernImageSelection && params && !forReset) {
|
||||
if (params.featured) {
|
||||
return ['featured']
|
||||
} else if (params.public) {
|
||||
return ['community']
|
||||
}
|
||||
}
|
||||
return this.isNormalAndDomainUser ? ['executable'] : ['all']
|
||||
}
|
||||
return [
|
||||
'featured',
|
||||
'community',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
]
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
@ -244,19 +299,56 @@ export default {
|
||||
this.closeAction()
|
||||
})
|
||||
},
|
||||
fetchGuestOsCategories () {
|
||||
this.loading.guestOsCategories = true
|
||||
const params = {
|
||||
zoneid: this.resource.zoneid,
|
||||
arch: this.resource.arch,
|
||||
isiso: false,
|
||||
featured: true,
|
||||
showicon: true
|
||||
}
|
||||
api('listOsCategories', params).then((response) => {
|
||||
this.options.guestOsCategories = response?.listoscategoriesresponse?.oscategory || []
|
||||
if (this.showUserCategoryForModernImageSelection) {
|
||||
const userCategory = {
|
||||
id: '0',
|
||||
name: this.$t('label.user')
|
||||
}
|
||||
if (this.$store.getters.avatar) {
|
||||
userCategory.icon = {
|
||||
base64image: this.$store.getters.avatar
|
||||
}
|
||||
}
|
||||
this.options.guestOsCategories.push(userCategory)
|
||||
}
|
||||
if (this.showAllCategoryForModernImageSelection) {
|
||||
this.options.guestOsCategories.push({
|
||||
id: '-1',
|
||||
name: this.$t('label.all')
|
||||
})
|
||||
}
|
||||
this.selectedGuestOsCategoryId = this.options.guestOsCategories[0].id
|
||||
}).finally(() => {
|
||||
this.loading.guestOsCategories = false
|
||||
this.fetchAllTemplates()
|
||||
})
|
||||
},
|
||||
fetchAllTemplates (params) {
|
||||
const promises = []
|
||||
const templates = {}
|
||||
this.loading.templates = true
|
||||
this.templateFilter.forEach((filter) => {
|
||||
this.imageSearchFilters = params
|
||||
const templateFilters = this.getImageFilters(params)
|
||||
templateFilters.forEach((filter) => {
|
||||
templates[filter] = { count: 0, template: [] }
|
||||
promises.push(this.fetchTemplates(filter, params))
|
||||
})
|
||||
this.templates = templates
|
||||
this.options.templates = templates
|
||||
Promise.all(promises).then((response) => {
|
||||
response.forEach((resItem, idx) => {
|
||||
templates[this.templateFilter[idx]] = _.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : resItem.listtemplatesresponse
|
||||
this.templates = { ...templates }
|
||||
templates[templateFilters[idx]] = _.isEmpty(resItem.listtemplatesresponse) ? { count: 0, template: [] } : resItem.listtemplatesresponse
|
||||
this.options.templates = { ...templates }
|
||||
})
|
||||
}).catch((reason) => {
|
||||
console.log(reason)
|
||||
@ -266,12 +358,18 @@ export default {
|
||||
},
|
||||
fetchTemplates (templateFilter, params) {
|
||||
const args = Object.assign({}, params)
|
||||
if (args.keyword || args.category !== templateFilter) {
|
||||
if (this.isModernImageSelection && this.selectedGuestOsCategoryId && !['-1', '0'].includes(this.selectedGuestOsCategoryId)) {
|
||||
args.oscategoryid = this.selectedGuestOsCategoryId
|
||||
}
|
||||
if (args.keyword || (args.category && args.category !== templateFilter)) {
|
||||
args.page = 1
|
||||
args.pageSize = args.pageSize || 10
|
||||
}
|
||||
args.zoneid = _.get(this.zone, 'id')
|
||||
args.zoneid = this.resource.zoneid
|
||||
args.templatefilter = templateFilter
|
||||
if (this.resource.arch) {
|
||||
args.arch = this.resource.arch
|
||||
}
|
||||
args.details = 'all'
|
||||
args.showicon = 'true'
|
||||
|
||||
@ -285,17 +383,24 @@ export default {
|
||||
},
|
||||
fetchDiskOfferings (params) {
|
||||
api('listDiskOfferings', { zoneid: this.resource.zoneid, listall: true, ...params }).then((response) => {
|
||||
this.diskOfferings = response?.listdiskofferingsresponse?.diskoffering || []
|
||||
this.diskOfferingCount = response?.listdiskofferingsresponse?.count || 0
|
||||
this.options.diskOfferings = response?.listdiskofferingsresponse?.diskoffering || []
|
||||
this.count.diskOfferings = response?.listdiskofferingsresponse?.count || 0
|
||||
})
|
||||
},
|
||||
onSelectGuestOsCategory (value) {
|
||||
this.selectedGuestOsCategoryId = value
|
||||
this.fetchAllTemplates(this.imageSearchFilters)
|
||||
},
|
||||
onSelectDiskSize (rowSelected) {
|
||||
this.diskOffering = rowSelected
|
||||
this.dataPrefill.diskofferingid = rowSelected.id
|
||||
this.dataPreFill.diskofferingid = rowSelected.id
|
||||
},
|
||||
updateFieldValue (input, value) {
|
||||
this[input] = value
|
||||
this.dataPrefill[input] = value
|
||||
this.dataPreFill[input] = value
|
||||
},
|
||||
filterOption (input, option) {
|
||||
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,13 +26,13 @@
|
||||
v-ctrl-enter="handleSubmit"
|
||||
@finish="handleSubmit">
|
||||
<div v-if="template && template.userdataid">
|
||||
<a-text type="primary">
|
||||
<a-typography-text>
|
||||
The template "{{ $t(this.template.name) }}" is linked to Userdata "{{ $t(this.template.userdataname) }}" with override policy "{{ $t(this.template.userdatapolicy) }}"
|
||||
</a-text><br/><br/>
|
||||
</a-typography-text><br/><br/>
|
||||
<div v-if="templateUserDataParams.length > 0 && !doUserdataOverride">
|
||||
<a-text type="primary" v-if="this.template && this.template.userdataid && templateUserDataParams.length > 0">
|
||||
<a-typography-text v-if="this.template && this.template.userdataid && templateUserDataParams.length > 0">
|
||||
Enter the values for the variables in userdata
|
||||
</a-text>
|
||||
</a-typography-text>
|
||||
<a-input-group>
|
||||
<a-table
|
||||
size="small"
|
||||
|
||||
@ -179,6 +179,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['update-network-config', 'select-default-network-item', 'handler-error'],
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
|
||||
208
ui/src/views/compute/wizard/OsBasedImageRadioGroup.vue
Normal file
208
ui/src/views/compute/wizard/OsBasedImageRadioGroup.vue
Normal file
@ -0,0 +1,208 @@
|
||||
// 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-list
|
||||
class="form-item-scroll"
|
||||
itemLayout="vertical"
|
||||
size="small"
|
||||
:dataSource="imagesList"
|
||||
:pagination="false">
|
||||
<template #renderItem="{ item, index }">
|
||||
<a-list-item :key="item.id" @click="onClickRow(item)">
|
||||
<a-radio-group
|
||||
class="radio-group"
|
||||
:key="index"
|
||||
v-model:value="value"
|
||||
@change="($event) => updateSelectionTemplateIso($event.target.value)">
|
||||
<a-radio
|
||||
class="radio-group__radio"
|
||||
:value="item.id">
|
||||
<resource-icon
|
||||
v-if="item.icon && item.icon.base64image"
|
||||
class="radio-group__os-logo"
|
||||
:image="item.icon.base64image"
|
||||
size="2x" />
|
||||
<os-logo
|
||||
v-else
|
||||
class="radio-group__os-logo"
|
||||
size="2x"
|
||||
:osId="item.ostypeid"
|
||||
:os-name="item.osName" />
|
||||
|
||||
{{ item.displaytext }}
|
||||
<span v-if="item?.projectid">
|
||||
| <project-outlined /> {{ item.project }}
|
||||
</span>
|
||||
<a-tooltip :title="$t('label.passwordenabled')" v-if="item.passwordenabled">
|
||||
<lock-outlined style="margin-left: 8px;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('label.userdata')" v-if="item.userdataid">
|
||||
<solution-outlined style="margin-left: 8px;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('label.isdynamicallyscalable')" v-if="item.isdynamicallyscalable">
|
||||
<arrows-alt-outlined style="margin-left: 8px;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="$t('label.isextractable')" v-if="item.isextractable">
|
||||
<file-zip-outlined style="margin-left: 8px;"/>
|
||||
</a-tooltip>
|
||||
<a-tag v-if="item.isfeatured" style="margin-left: 8px;">
|
||||
{{ $t('label.isfeatured') }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.ispublic" style="margin-left: 8px;">
|
||||
{{ $t('label.ispublic') }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.directdownload" style="margin-left: 8px;">
|
||||
{{ $t('label.directdownload') }}
|
||||
</a-tag>
|
||||
<a-tag v-if="item.requireshvm" style="margin-left: 8px;">
|
||||
{{ $t('label.requireshvm') }}
|
||||
</a-tag>
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
|
||||
<div style="display: block; text-align: right;">
|
||||
<a-pagination
|
||||
size="small"
|
||||
:current="options.page"
|
||||
:pageSize="options.pageSize"
|
||||
:total="itemCount"
|
||||
:showTotal="total => `${$t('label.total')} ${total} ${$t('label.items')}`"
|
||||
:pageSizeOptions="['10', '20', '40', '80', '100', '200']"
|
||||
@change="onChangePage"
|
||||
@showSizeChange="onChangePageSize"
|
||||
showSizeChanger>
|
||||
<template #buildOptionText="props">
|
||||
<span>{{ props.value }} / {{ $t('label.page') }}</span>
|
||||
</template>
|
||||
</a-pagination>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
|
||||
export default {
|
||||
name: 'OsBasedImageRadioGroup',
|
||||
components: {
|
||||
OsLogo,
|
||||
ResourceIcon
|
||||
},
|
||||
props: {
|
||||
imagesList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
inputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
selected: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
itemCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
preFillContent: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
value: '',
|
||||
image: '',
|
||||
options: {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.onSelectTemplateIso()
|
||||
},
|
||||
watch: {
|
||||
selected (newVal, oldVal) {
|
||||
if (newVal === oldVal) return
|
||||
this.onSelectTemplateIso()
|
||||
}
|
||||
},
|
||||
emits: ['emit-update-image', 'handle-search-filter'],
|
||||
methods: {
|
||||
onSelectTemplateIso () {
|
||||
if (this.inputDecorator === 'templateid') {
|
||||
this.value = this.preFillContent?.templateid ? this.preFillContent.templateid : this.selected
|
||||
} else {
|
||||
this.value = this.preFillContent?.isoid ? this.preFillContent.isoid : this.selected
|
||||
}
|
||||
|
||||
this.$emit('emit-update-image', this.inputDecorator, this.value)
|
||||
},
|
||||
updateSelectionTemplateIso (id) {
|
||||
this.$emit('emit-update-image', this.inputDecorator, id)
|
||||
},
|
||||
onChangePage (page, pageSize) {
|
||||
this.options.page = page
|
||||
this.options.pageSize = pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
onChangePageSize (page, pageSize) {
|
||||
this.options.page = page
|
||||
this.options.pageSize = pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
onClickRow (os) {
|
||||
this.value = os.id
|
||||
this.$emit('emit-update-image', this.inputDecorator, this.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.radio-group {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
:deep(.ant-radio) {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
&__os-logo {
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-spin-container) {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
:deep(.ant-list-split) .ant-list-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
373
ui/src/views/compute/wizard/OsBasedImageSelection.vue
Normal file
373
ui/src/views/compute/wizard/OsBasedImageSelection.vue
Normal file
@ -0,0 +1,373 @@
|
||||
// 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>
|
||||
<a-form-item v-if="imageTypeSelectionAllowed" :label="$t('label.type')" name="imagetype" ref="imagetype">
|
||||
<a-radio-group
|
||||
v-model:value="localSelectedImageType"
|
||||
button-style="solid"
|
||||
:disabled="imagePreSelected"
|
||||
@change="emitChangeImageType()">
|
||||
<a-radio-button value="templateid">{{ $t('label.template') }}</a-radio-button>
|
||||
<a-radio-button value="isoid">{{ $t('label.iso') }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
<div style="margin-top: 5px; margin-bottom: 5px;">
|
||||
{{ $t('message.' + localSelectedImageType.replace('id', '') + '.desc') }}
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="$t('label.os')"
|
||||
name="guestoscategoryid"
|
||||
ref="guestoscategoryid"
|
||||
v-if="!guestOsCategoriesSelectionDisallowed">
|
||||
<block-radio-group-select
|
||||
:maxBlocks="16"
|
||||
:items="guestOsCategories"
|
||||
:selectedValue="localSelectedGuestOsCategoryId"
|
||||
:horizontalGutter="6"
|
||||
:verticalGutter="6"
|
||||
blockSize="square"
|
||||
@change="handleGuestOsCategoryChange">
|
||||
<template #radio-option="{ item }">
|
||||
<div class="radio-option">
|
||||
<div class="radio-opion__icon">
|
||||
<resource-icon v-if="item.icon && item.icon.base64image" :image="item.icon.base64image" size="os" style="margin-bottom: 2px; margin-left: 1px" />
|
||||
<font-awesome-icon v-else-if="['-1', '0'].includes(item.id)" :icon="['fas', item.id === '0' ? 'user' : 'images']" size="2x" :style="categoryFontAwesomeIconStyle" />
|
||||
<os-logo v-else size="2x" :os-name="item.name" />
|
||||
</div>
|
||||
<a-tooltip placement="top" :title="item.name">
|
||||
<div class="ellipsis">{{ item.name }}</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template #select-option="{ item }">
|
||||
<span>
|
||||
<resource-icon v-if="item.icon && item.icon.base64image" :image="item.icon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
<font-awesome-icon
|
||||
v-else-if="item.id === '0'"
|
||||
:icon="['fas', 'user']"
|
||||
size="2x"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#666' }]"
|
||||
/>
|
||||
<os-logo v-else :os-name="item.name" style="margin-right: 5px" />
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</template>
|
||||
</block-radio-group-select>
|
||||
</a-form-item>
|
||||
<a-card>
|
||||
<os-based-image-selection-search-view
|
||||
v-if="!imagePreSelected"
|
||||
class="search-input"
|
||||
:filtersDisabled="searchFiltersDisabled"
|
||||
@search="handleImageSearch">
|
||||
</os-based-image-selection-search-view>
|
||||
<a-spin :spinning="imagesLoading">
|
||||
<os-based-image-radio-group
|
||||
:imagesList="imagesList"
|
||||
:itemCount="imagesCount"
|
||||
:input-decorator="localSelectedImageType"
|
||||
:selected="selectedImageId"
|
||||
:preFillContent="preFillContent"
|
||||
@emit-update-image="updateImage"
|
||||
@handle-search-filter="($event) => eventPagination($event)"
|
||||
/>
|
||||
</a-spin>
|
||||
<div v-if="diskSizeSelectionAllowed">
|
||||
<div>
|
||||
{{ $t('label.override.rootdisk.size') }}
|
||||
<a-switch
|
||||
v-model:checked="localRootDiskOverrideChecked"
|
||||
:disabled="rootDiskOverrideDisabled"
|
||||
@change="handleRootDiskOverrideCheckedChange"
|
||||
style="margin-left: 10px;"/>
|
||||
<div v-if="diskSizeSelectionDeployAsIsMessageVisible"> {{ $t('message.deployasis') }} </div>
|
||||
</div>
|
||||
<disk-size-selection
|
||||
v-if="showRootDiskSizeChanger"
|
||||
:input-decorator="diskSizeSelectionInputDecorator"
|
||||
:preFillContent="preFillContent"
|
||||
:isCustomized="true"
|
||||
:minDiskSize="preFillContent.minrootdisksize"
|
||||
@update-disk-size="emitUpdateDiskSize"
|
||||
style="margin-top: 10px;"/>
|
||||
</div>
|
||||
<a-form-item :label="$t('label.hypervisor')" v-if="localSelectedImageType === 'isoid'">
|
||||
<a-select
|
||||
v-model:value="localSelectedIsoHypervisor"
|
||||
:preFillContent="preFillContent"
|
||||
:options="isoHypervisorItems"
|
||||
@change="handleIsoHypervisorChange()"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="filterOption" />
|
||||
</a-form-item>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BlockRadioGroupSelect from '@/components/widgets/BlockRadioGroupSelect.vue'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import OsBasedImageSelectionSearchView from '@views/compute/wizard/OsBasedImageSelectionSearchView'
|
||||
import OsBasedImageRadioGroup from '@views/compute/wizard/OsBasedImageRadioGroup'
|
||||
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
|
||||
|
||||
export default {
|
||||
name: 'OsBasedImageSelection',
|
||||
components: {
|
||||
BlockRadioGroupSelect,
|
||||
ResourceIcon,
|
||||
OsLogo,
|
||||
OsBasedImageSelectionSearchView,
|
||||
OsBasedImageRadioGroup,
|
||||
DiskSizeSelection
|
||||
},
|
||||
props: {
|
||||
imageTypeSelectionAllowed: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
selectedImageType: {
|
||||
type: String,
|
||||
default: 'templateid'
|
||||
},
|
||||
imagePreSelected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
guestOsCategoriesSelectionDisallowed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
guestOsCategories: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
guestOsCategoriesLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedGuestOsCategoryId: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
imageItems: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
imagesLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
diskSizeSelectionAllowed: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
diskSizeSelectionDeployAsIsMessageVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
diskSizeSelectionInputDecorator: {
|
||||
type: String,
|
||||
default: 'rootdisksize'
|
||||
},
|
||||
rootDiskOverrideDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rootDiskOverrideChecked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isoHypervisorItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedIsoHypervisor: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
filterOption: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
preFillContent: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filterType: 'executable',
|
||||
selectedImageId: '',
|
||||
imageSearchFilters: {},
|
||||
showRootDiskSizeChanger: false,
|
||||
// Local data properties to mirror props
|
||||
localSelectedImageType: this.selectedImageType,
|
||||
localSelectedGuestOsCategoryId: this.selectedGuestOsCategoryId,
|
||||
localRootDiskOverrideChecked: this.rootDiskOverrideChecked,
|
||||
localSelectedIsoHypervisor: this.selectedIsoHypervisor
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.filterType = this.defaultImageFilter
|
||||
},
|
||||
watch: {
|
||||
selectedImageType (newValue) {
|
||||
this.localSelectedImageType = newValue
|
||||
},
|
||||
guestOsCategories (newValue) {
|
||||
this.imageSearchFilters = {}
|
||||
},
|
||||
selectedGuestOsCategoryId (newValue) {
|
||||
this.localSelectedGuestOsCategoryId = newValue
|
||||
this.updateImageFilterType()
|
||||
},
|
||||
rootDiskOverrideChecked (newValue) {
|
||||
this.localRootDiskOverrideChecked = newValue
|
||||
},
|
||||
selectedIsoHypervisor (newValue) {
|
||||
this.localSelectedIsoHypervisor = newValue
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
defaultImageFilter () {
|
||||
return ['DomainAdmin', 'User'].includes(this.$store.getters.userInfo.roletype) ? 'executable' : 'all'
|
||||
},
|
||||
imagesList () {
|
||||
if (!this.localSelectedImageType || !this.imageItems || !this.imageItems[this.filterType]) {
|
||||
return []
|
||||
}
|
||||
const imageTypeKey = this.localSelectedImageType.slice(0, -2)
|
||||
return this.imageItems[this.filterType][imageTypeKey] || []
|
||||
},
|
||||
imagesCount () {
|
||||
return this.imageItems[this.filterType] ? this.imageItems[this.filterType].count || 0 : 0
|
||||
},
|
||||
selectedCategory () {
|
||||
if (this.localSelectedGuestOsCategoryId && this.guestOsCategories) {
|
||||
return this.guestOsCategories.find(option => option.id === this.localSelectedGuestOsCategoryId)
|
||||
}
|
||||
return null
|
||||
},
|
||||
categoryFontAwesomeIconStyle () {
|
||||
return [this.$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#666' }]
|
||||
},
|
||||
searchFiltersDisabled () {
|
||||
return this.selectedCategory?.disableimagefilters
|
||||
}
|
||||
},
|
||||
emits: ['change-image-type', 'change-guest-os-category', 'update-image', 'handle-image-search-filter', 'change-root-disk-override-checked', 'update-disk-size', 'change-iso-hypervisor'],
|
||||
methods: {
|
||||
emitChangeImageType () {
|
||||
this.$emit('change-image-type', this.localSelectedImageType)
|
||||
},
|
||||
handleGuestOsCategoryChange (value) {
|
||||
this.localSelectedGuestOsCategoryId = value
|
||||
this.$emit('change-guest-os-category', this.localSelectedGuestOsCategoryId)
|
||||
},
|
||||
updateImage (decorator, id) {
|
||||
this.selectedImageId = id
|
||||
this.$emit('update-image', decorator, id)
|
||||
},
|
||||
handleImageSearch (searchFilters) {
|
||||
this.imageSearchFilters = {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
Object.assign(this.imageSearchFilters, searchFilters)
|
||||
this.updateImageFilterType()
|
||||
this.emitSearchFilter()
|
||||
},
|
||||
updateImageFilterType () {
|
||||
this.filterType = this.defaultImageFilter
|
||||
if (this.localSelectedGuestOsCategoryId === '0') {
|
||||
this.filterType = 'self'
|
||||
} else {
|
||||
if (this.imageSearchFilters?.featured) {
|
||||
this.filterType = 'featured'
|
||||
} else if (this.imageSearchFilters?.public) {
|
||||
this.filterType = 'community'
|
||||
}
|
||||
}
|
||||
},
|
||||
eventPagination (pagination) {
|
||||
Object.assign(this.imageSearchFilters, pagination)
|
||||
this.emitSearchFilter()
|
||||
},
|
||||
emitSearchFilter () {
|
||||
this.$emit('handle-image-search-filter', this.imageSearchFilters)
|
||||
},
|
||||
changeFilterType (value) {
|
||||
this.filterType = value
|
||||
},
|
||||
handleRootDiskOverrideCheckedChange (value) {
|
||||
this.showRootDiskSizeChanger = value
|
||||
this.$emit('change-root-disk-override-checked', value)
|
||||
},
|
||||
emitUpdateDiskSize (decorator, value) {
|
||||
this.$emit('update-disk-size', decorator, value)
|
||||
},
|
||||
handleIsoHypervisorChange () {
|
||||
this.$emit('change-iso-hypervisor', this.localIsoHypervisor)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search-input {
|
||||
z-index: 8;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-tabs-nav-scroll) {
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
.radio-option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
max-width: 80px;
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.radio-opion__icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
119
ui/src/views/compute/wizard/OsBasedImageSelectionSearchView.vue
Normal file
119
ui/src/views/compute/wizard/OsBasedImageSelectionSearchView.vue
Normal file
@ -0,0 +1,119 @@
|
||||
// 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>
|
||||
<span class="filter-group">
|
||||
<a-row type="flex">
|
||||
<a-col flex="200px" v-if="!filtersDisabled">
|
||||
<a-select
|
||||
mode="multiple"
|
||||
class="filter-select"
|
||||
:placeholder="$t('label.filterby')"
|
||||
v-model:value="filterValues"
|
||||
@change="handleFilterChange"
|
||||
showSearch
|
||||
allowClear
|
||||
:showArrow="true"
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}">
|
||||
<template #suffixIcon><filter-outlined class="ant-select-suffix" /></template>
|
||||
<a-select-option
|
||||
v-for="filter in filters"
|
||||
:key="filter"
|
||||
:label="$t('label.' + filter)">
|
||||
{{ $t('label.' + filter) }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col flex="auto">
|
||||
<a-input-search
|
||||
class="filter-search"
|
||||
:placeholder="$t('label.search')"
|
||||
v-model:value="searchedText"
|
||||
allowClear
|
||||
@search="handleTextSearch" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'OsBasedImageSelectionSearchView',
|
||||
props: {
|
||||
filtersDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filters: [
|
||||
'public',
|
||||
'featured'
|
||||
],
|
||||
filterValues: undefined,
|
||||
searchedText: null,
|
||||
paramsFilter: {}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
},
|
||||
methods: {
|
||||
handleTextSearch (value) {
|
||||
this.searchedText = value
|
||||
if (value) {
|
||||
this.paramsFilter.keyword = value
|
||||
} else {
|
||||
delete this.paramsFilter.keyword
|
||||
}
|
||||
const params = this.paramsFilter
|
||||
if (this.filtersDisabled) {
|
||||
delete this.paramsFilter.public
|
||||
delete this.paramsFilter.featured
|
||||
}
|
||||
this.$emit('search', params)
|
||||
},
|
||||
handleFilterChange () {
|
||||
this.paramsFilter = {}
|
||||
if (Array.isArray(this.filterValues)) {
|
||||
this.filterValues.forEach(e => {
|
||||
this.paramsFilter[e] = true
|
||||
})
|
||||
}
|
||||
if (this.searchedText) {
|
||||
this.paramsFilter.keyword = this.searchedText
|
||||
}
|
||||
this.$emit('search', this.paramsFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.filter-group .ant-select,
|
||||
.filter-group .ant-input-search {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
51
ui/src/views/compute/wizard/ZoneBlockRadioGroupSelect.vue
Normal file
51
ui/src/views/compute/wizard/ZoneBlockRadioGroupSelect.vue
Normal file
@ -0,0 +1,51 @@
|
||||
// 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>
|
||||
<block-radio-group-select>
|
||||
<template #radio-option="{ item }">
|
||||
<span>
|
||||
<resource-icon
|
||||
v-if="item && item.icon && item.icon.base64image"
|
||||
:image="item.icon.base64image"
|
||||
size="2x" />
|
||||
<global-outlined size="2x" v-else />
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</template>
|
||||
<template #select-option="{ item }">
|
||||
<span>
|
||||
<resource-icon v-if="item.icon && zone1.icon.base64image" :image="item.icon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
{{ item.name }}
|
||||
</span>
|
||||
</template>
|
||||
</block-radio-group-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BlockRadioGroupSelect from '@/components/widgets/BlockRadioGroupSelect.vue'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
|
||||
export default {
|
||||
name: 'ZoneBlockRadioGroupSelect',
|
||||
components: {
|
||||
BlockRadioGroupSelect,
|
||||
ResourceIcon
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -38,8 +38,8 @@
|
||||
:rowExpandable="(record) => record.downloaddetails.length > 0">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'zonename'">
|
||||
<span v-if="fetchZoneIcon(record.zoneid)">
|
||||
<resource-icon :image="zoneIcon" size="1x" style="margin-right: 5px"/>
|
||||
<span v-if="record.zoneicon && record.zoneicon.base64image">
|
||||
<resource-icon :image="record.zoneicon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
<span> {{ record.zonename }} </span>
|
||||
@ -52,6 +52,14 @@
|
||||
<span v-if="record.created">{{ $toLocaleDate(record.created) }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'actions'">
|
||||
<span style="margin-right: 5px" v-if="'deployVirtualMachine' in $store.getters.apis">
|
||||
<tooltip-button
|
||||
:disabled="!record.isready"
|
||||
:title="$t('label.vm.add')"
|
||||
icon="rocket-outlined"
|
||||
@onClick="onAddInstance(record)"/>
|
||||
</span>
|
||||
<span v-if="isActionsOnIsoPermitted">
|
||||
<span style="margin-right: 5px">
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.action.copy.iso')"
|
||||
@ -77,6 +85,7 @@
|
||||
icon="delete-outlined" />
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
<template #expandedRowRender="{ record }">
|
||||
@ -162,7 +171,7 @@
|
||||
}"
|
||||
:loading="zoneLoading"
|
||||
v-focus="true">
|
||||
<a-select-option v-for="zone in zones" :key="zone.id" :label="zone.name">
|
||||
<a-select-option v-for="zone in copyZones" :key="zone.id" :label="zone.name">
|
||||
<div>
|
||||
<span v-if="zone.icon && zone.icon.base64image">
|
||||
<resource-icon :image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
|
||||
@ -237,6 +246,7 @@ export default {
|
||||
showCopyActionForm: false,
|
||||
currentRecord: {},
|
||||
zones: [],
|
||||
copyZones: [],
|
||||
zoneLoading: false,
|
||||
copyLoading: false,
|
||||
deleteLoading: false,
|
||||
@ -278,6 +288,13 @@ export default {
|
||||
key: 'isready',
|
||||
title: this.$t('label.isready'),
|
||||
dataIndex: 'isready'
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
title: '',
|
||||
dataIndex: 'actions',
|
||||
fixed: 'right',
|
||||
width: 130
|
||||
}
|
||||
]
|
||||
this.storagePoolInnerColumns = [
|
||||
@ -308,15 +325,6 @@ export default {
|
||||
dataIndex: 'downloadState'
|
||||
}
|
||||
]
|
||||
if (this.isActionPermitted()) {
|
||||
this.columns.push({
|
||||
key: 'actions',
|
||||
title: '',
|
||||
dataIndex: 'actions',
|
||||
fixed: 'right',
|
||||
width: 100
|
||||
})
|
||||
}
|
||||
|
||||
const userInfo = this.$store.getters.userInfo
|
||||
if (!['Admin'].includes(userInfo.roletype) &&
|
||||
@ -332,6 +340,16 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isActionsOnIsoPermitted () {
|
||||
return (['Admin'].includes(this.$store.getters.userInfo.roletype) || // If admin or owner or belongs to current project
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.projectid && this.$store.getters.project && this.$store.getters.project.id && this.resource.projectid === this.$store.getters.project.id)) &&
|
||||
(this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Iso is ready or downloaded
|
||||
this.resource.account !== 'system'
|
||||
}
|
||||
},
|
||||
emits: ['update-zones'],
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
@ -358,16 +376,10 @@ export default {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.fetchLoading = false
|
||||
this.updateImageZones()
|
||||
})
|
||||
this.fetchZoneData()
|
||||
},
|
||||
fetchZoneIcon (zoneid) {
|
||||
const zoneItem = this.zones.filter(zone => zone.id === zoneid)
|
||||
if (zoneItem?.[0]?.icon?.base64image) {
|
||||
this.zoneIcon = zoneItem[0].icon.base64image
|
||||
return true
|
||||
}
|
||||
return false
|
||||
this.fetchOsCategoryId()
|
||||
},
|
||||
handleChangePage (page, pageSize) {
|
||||
this.page = page
|
||||
@ -379,13 +391,6 @@ export default {
|
||||
this.pageSize = pageSize
|
||||
this.fetchData()
|
||||
},
|
||||
isActionPermitted () {
|
||||
return (['Admin'].includes(this.$store.getters.userInfo.roletype) || // If admin or owner or belongs to current project
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.projectid && this.$store.getters.project && this.$store.getters.project.id && this.resource.projectid === this.$store.getters.project.id)) &&
|
||||
(this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Iso is ready or downloaded
|
||||
this.resource.account !== 'system'
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
this.$emit('selection-change', this.selectedRowKeys)
|
||||
@ -501,9 +506,41 @@ export default {
|
||||
this.zoneLoading = true
|
||||
api('listZones', { showicon: true }).then(json => {
|
||||
const zones = json.listzonesresponse.zone || []
|
||||
this.zones = [...zones.filter((zone) => this.currentRecord.zoneid !== zone.id)]
|
||||
this.zones = zones
|
||||
this.copyZones = [...zones.filter((zone) => this.currentRecord.zoneid !== zone.id)]
|
||||
}).finally(() => {
|
||||
this.zoneLoading = false
|
||||
this.updateImageZones()
|
||||
})
|
||||
},
|
||||
updateImageZones () {
|
||||
if (!Array.isArray(this.dataSource) || !Array.isArray(this.zones) ||
|
||||
this.dataSource.length === 0 || this.zones.length === 0) {
|
||||
return
|
||||
}
|
||||
const imageZones = []
|
||||
this.dataSource.forEach(item => {
|
||||
const zone = this.zones.find(zone => item.zoneid === zone.id)
|
||||
if (zone && zone.icon) {
|
||||
item.zoneicon = zone.icon
|
||||
}
|
||||
imageZones.push(zone)
|
||||
})
|
||||
if (imageZones.length !== 0) {
|
||||
this.$emit('update-zones', imageZones)
|
||||
}
|
||||
},
|
||||
fetchOsCategoryId () {
|
||||
const needed = this.$route.meta.name === 'iso' &&
|
||||
'listOsTypes' in this.$store.getters.apis &&
|
||||
this.resource && this.resource.ostypeid &&
|
||||
(this.$config.imageSelectionInterface === undefined ||
|
||||
this.$config.imageSelectionInterface === 'modern')
|
||||
if (!needed) {
|
||||
return
|
||||
}
|
||||
api('listOsTypes', { id: this.resource.ostypeid }).then(json => {
|
||||
this.osCategoryId = json?.listostypesresponse?.ostype?.[0]?.oscategoryid || null
|
||||
})
|
||||
},
|
||||
showCopyIso (record) {
|
||||
@ -558,6 +595,19 @@ export default {
|
||||
},
|
||||
closeModal () {
|
||||
this.showConfirmationAction = false
|
||||
},
|
||||
onAddInstance (record) {
|
||||
const query = { isoid: this.resource.id, zoneid: record.zoneid }
|
||||
if (this.resource.arch) {
|
||||
query.arch = this.resource.arch
|
||||
}
|
||||
if (this.osCategoryId) {
|
||||
query.oscategoryid = this.osCategoryId
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/action/deployVirtualMachine',
|
||||
query: query
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -582,7 +582,7 @@ export default {
|
||||
this.fetchCustomHypervisorName()
|
||||
this.fetchZone()
|
||||
this.fetchOsTypes()
|
||||
this.fetchTemplateTypes()
|
||||
this.templateTypes.opts = this.$fetchTemplateTypes()
|
||||
this.architectureTypes.opts = this.$fetchCpuArchitectureTypes()
|
||||
this.fetchUserData()
|
||||
this.fetchUserdataPolicy()
|
||||
@ -726,33 +726,6 @@ export default {
|
||||
this.osTypes.loading = false
|
||||
})
|
||||
},
|
||||
fetchTemplateTypes () {
|
||||
this.templateTypes.opts = []
|
||||
const templatetypes = []
|
||||
templatetypes.push({
|
||||
id: 'USER',
|
||||
description: 'USER'
|
||||
})
|
||||
templatetypes.push({
|
||||
id: 'VNF',
|
||||
description: 'VNF'
|
||||
})
|
||||
if (this.isAdminRole) {
|
||||
templatetypes.push({
|
||||
id: 'SYSTEM',
|
||||
description: 'SYSTEM'
|
||||
})
|
||||
templatetypes.push({
|
||||
id: 'BUILTIN',
|
||||
description: 'BUILTIN'
|
||||
})
|
||||
templatetypes.push({
|
||||
id: 'ROUTING',
|
||||
description: 'ROUTING'
|
||||
})
|
||||
}
|
||||
this.templateTypes.opts = templatetypes
|
||||
},
|
||||
fetchUserData () {
|
||||
const params = {}
|
||||
params.listAll = true
|
||||
|
||||
@ -38,8 +38,8 @@
|
||||
:rowExpandable="(record) => record.downloaddetails.length > 0">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'zonename'">
|
||||
<span v-if="fetchZoneIcon(record.zoneid)">
|
||||
<resource-icon :image="zoneIcon" size="2x" style="margin-right: 5px"/>
|
||||
<span v-if="record.zoneicon && record.zoneicon.base64image">
|
||||
<resource-icon :image="record.zoneicon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<global-outlined v-else style="margin-right: 5px" />
|
||||
<span> {{ record.zonename }} </span>
|
||||
@ -52,21 +52,32 @@
|
||||
<span v-if="record.created">{{ $toLocaleDate(record.created) }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'actions'">
|
||||
<span style="margin-right: 5px" v-if="'deployVirtualMachine' in $store.getters.apis">
|
||||
<tooltip-button
|
||||
:disabled="!record.isready"
|
||||
:title="$t('label.vm.add')"
|
||||
icon="rocket-outlined"
|
||||
@onClick="onAddInstance(record)"/>
|
||||
</span>
|
||||
<span v-if="isActionsOnTemplatePermitted">
|
||||
<span style="margin-right: 5px">
|
||||
<tooltip-button
|
||||
style="margin-right: 5px"
|
||||
:disabled="!('copyTemplate' in $store.getters.apis && record.isready)"
|
||||
:title="$t('label.action.copy.template')"
|
||||
icon="copy-outlined"
|
||||
:loading="copyLoading"
|
||||
@onClick="showCopyTemplate(record)" />
|
||||
</span>
|
||||
<span style="margin-right: 5px">
|
||||
<tooltip-button
|
||||
style="margin-right: 5px"
|
||||
:disabled="!('deleteTemplate' in $store.getters.apis)"
|
||||
:title="$t('label.action.delete.template')"
|
||||
type="primary"
|
||||
:danger="true"
|
||||
icon="delete-outlined"
|
||||
@onClick="onShowDeleteModal(record)"/>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
<template #expandedRowRender="{ record }">
|
||||
@ -168,7 +179,7 @@
|
||||
}"
|
||||
:loading="zoneLoading"
|
||||
v-focus="true">
|
||||
<a-select-option v-for="zone in zones" :key="zone.id" :label="zone.name">
|
||||
<a-select-option v-for="zone in copyZones" :key="zone.id" :label="zone.name">
|
||||
<div>
|
||||
<span v-if="zone.icon && zone.icon.base64image">
|
||||
<resource-icon :image="zone.icon.base64image" size="2x" style="margin-right: 5px"/>
|
||||
@ -282,6 +293,7 @@ export default {
|
||||
showCopyActionForm: false,
|
||||
currentRecord: {},
|
||||
zones: [],
|
||||
copyZones: [],
|
||||
zoneLoading: false,
|
||||
copyLoading: false,
|
||||
deleteLoading: false,
|
||||
@ -298,7 +310,8 @@ export default {
|
||||
confirmMessage: this.$t('label.confirm.delete.templates')
|
||||
},
|
||||
modalWidth: '30vw',
|
||||
showTable: false
|
||||
showTable: false,
|
||||
osCategoryId: null
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
@ -324,6 +337,12 @@ export default {
|
||||
key: 'isready',
|
||||
title: this.$t('label.isready'),
|
||||
dataIndex: 'isready'
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
title: '',
|
||||
dataIndex: 'actions',
|
||||
width: 130
|
||||
}
|
||||
]
|
||||
this.imageStoreInnerColumns = [
|
||||
@ -354,14 +373,6 @@ export default {
|
||||
dataIndex: 'downloadState'
|
||||
}
|
||||
]
|
||||
if (this.isActionPermitted()) {
|
||||
this.columns.push({
|
||||
key: 'actions',
|
||||
title: '',
|
||||
dataIndex: 'actions',
|
||||
width: 100
|
||||
})
|
||||
}
|
||||
|
||||
const userInfo = this.$store.getters.userInfo
|
||||
if (!['Admin'].includes(userInfo.roletype) &&
|
||||
@ -378,6 +389,16 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isActionsOnTemplatePermitted () {
|
||||
return (['Admin'].includes(this.$store.getters.userInfo.roletype) || // If admin or owner or belongs to current project
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.projectid && this.$store.getters.project && this.$store.getters.project.id && this.resource.projectid === this.$store.getters.project.id)) &&
|
||||
(this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Template is ready or downloaded
|
||||
this.resource.templatetype !== 'SYSTEM'
|
||||
}
|
||||
},
|
||||
emits: ['update-zones'],
|
||||
methods: {
|
||||
initForm () {
|
||||
this.formRef = ref()
|
||||
@ -404,16 +425,10 @@ export default {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.fetchLoading = false
|
||||
this.updateImageZones()
|
||||
})
|
||||
this.fetchZoneData()
|
||||
},
|
||||
fetchZoneIcon (zoneid) {
|
||||
const zoneItem = this.zones.filter(zone => zone.id === zoneid)
|
||||
if (zoneItem?.[0]?.icon?.base64image) {
|
||||
this.zoneIcon = zoneItem[0].icon.base64image
|
||||
return true
|
||||
}
|
||||
return false
|
||||
this.fetchOsCategoryId()
|
||||
},
|
||||
handleChangePage (page, pageSize) {
|
||||
this.page = page
|
||||
@ -425,13 +440,6 @@ export default {
|
||||
this.pageSize = pageSize
|
||||
this.fetchData()
|
||||
},
|
||||
isActionPermitted () {
|
||||
return (['Admin'].includes(this.$store.getters.userInfo.roletype) || // If admin or owner or belongs to current project
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
|
||||
(this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.projectid && this.$store.getters.project && this.$store.getters.project.id && this.resource.projectid === this.$store.getters.project.id)) &&
|
||||
(this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Template is ready or downloaded
|
||||
this.resource.templatetype !== 'SYSTEM'
|
||||
},
|
||||
setSelection (selection) {
|
||||
this.selectedRowKeys = selection
|
||||
if (selection?.length > 0) {
|
||||
@ -566,9 +574,41 @@ export default {
|
||||
this.zoneLoading = true
|
||||
api('listZones', { showicon: true }).then(json => {
|
||||
const zones = json.listzonesresponse.zone || []
|
||||
this.zones = [...zones.filter((zone) => this.currentRecord.zoneid !== zone.id)]
|
||||
this.zones = zones
|
||||
this.copyZones = [...zones.filter((zone) => this.currentRecord.zoneid !== zone.id)]
|
||||
}).finally(() => {
|
||||
this.zoneLoading = false
|
||||
this.updateImageZones()
|
||||
})
|
||||
},
|
||||
updateImageZones () {
|
||||
if (!Array.isArray(this.dataSource) || !Array.isArray(this.zones) ||
|
||||
this.dataSource.length === 0 || this.zones.length === 0) {
|
||||
return
|
||||
}
|
||||
const imageZones = []
|
||||
this.dataSource.forEach(item => {
|
||||
const zone = this.zones.find(zone => item.zoneid === zone.id)
|
||||
if (zone && zone.icon) {
|
||||
item.zoneicon = zone.icon
|
||||
}
|
||||
imageZones.push(zone)
|
||||
})
|
||||
if (imageZones.length !== 0) {
|
||||
this.$emit('update-zones', imageZones)
|
||||
}
|
||||
},
|
||||
fetchOsCategoryId () {
|
||||
const needed = this.$route.meta.name === 'template' &&
|
||||
'listOsTypes' in this.$store.getters.apis &&
|
||||
this.resource && this.resource.ostypeid &&
|
||||
(this.$config.imageSelectionInterface === undefined ||
|
||||
this.$config.imageSelectionInterface === 'modern')
|
||||
if (!needed) {
|
||||
return
|
||||
}
|
||||
api('listOsTypes', { id: this.resource.ostypeid }).then(json => {
|
||||
this.osCategoryId = json?.listostypesresponse?.ostype?.[0]?.oscategoryid || null
|
||||
})
|
||||
},
|
||||
showCopyTemplate (record) {
|
||||
@ -634,6 +674,19 @@ export default {
|
||||
}).catch(error => {
|
||||
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||
})
|
||||
},
|
||||
onAddInstance (record) {
|
||||
const query = { templateid: this.resource.id, zoneid: record.zoneid }
|
||||
if (this.resource.arch) {
|
||||
query.arch = this.resource.arch
|
||||
}
|
||||
if (this.osCategoryId) {
|
||||
query.oscategoryid = this.osCategoryId
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/action/deployVirtualMachine',
|
||||
query: query
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
package com.cloud.utils.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -46,6 +47,8 @@ public interface EntityManager {
|
||||
*/
|
||||
public <T> T findByUuid(Class<T> entityType, String uuid);
|
||||
|
||||
<T> List<T> listByUuids(Class<T> entityType, Collection<String> uuids);
|
||||
|
||||
/**
|
||||
* Finds a unique entity by uuid string, including those removed entries
|
||||
* @param <T> entity class
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user